diff --git a/linear_pendulum_2009/avr/Makefile b/linear_pendulum_2009/avr/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..91fe37a78cda4bb5a89de1e7998ec604d9a2c05d --- /dev/null +++ b/linear_pendulum_2009/avr/Makefile @@ -0,0 +1,81 @@ +PROJECT=current_control +CHIP=atmega16 + +ARCH=avr +AVRSOURCE= +AVRAUTO=compiled/$(ARCH)/ +AVRARCH=compiled/$(ARCH)/ +LIB_TARGET=$(AVRARCH)lib$(TARGET).a + +CC=$(ARCH)-gcc +CXX=$(ARCH)-gcc +CCFLAGS=-mmcu=$(CHIP) -O5 -I. -I$(AVRAUTO) +AS=$(ARCH)-as +ASFLAGS=-mmcu=$(CHIP) +AR=$(ARCH)-ar +LD=$(ARCH)-ld +OBJDUMP=$(ARCH)-objdump +LDFLAGS= -T $(CHIP).link + +all: $(AVRARCH)$(PROJECT) + +$(LIB_TARGET)(%.o): $(AVRSOURCE)%.c Makefile + $(CC) $(CCFLAGS) -c -o $(AVRARCH)$(%) -I$(AVRSOURCE) -I$(AVRAUTO) $(<) + $(AR) r $(@) $(AVRARCH)$(%) + rm $(AVRARCH)$(%) + +$(LIB_TARGET)(%.o): $(AVRSOURCE)%.cc Makefile + $(CXX) $(CCFLAGS) -c -o $(AVRARCH)$(%) -I$(AVRSOURCE) -I$(AVRAUTO) $(<) + $(AR) r $(@) $(AVRARCH)$(%) + rm $(AVRARCH)$(%) + +$(LIB_TARGET)(%.o): $(AVRSOURCE)%.s Makefile + $(AS) $(ASFLAGS) -o $(AVRARCH)$(%) $(AVRSOURCE)$(*).s + $(AR) r $(@) $(AVRARCH)$(%) + rm $(AVRARCH)$(%) + +$(AVRARCH)%.o: $(AVRSOURCE)%.s Makefile + $(AS) $(ASFLAGS) -o $@ $(AVRSOURCE)$(*).s + +$(AVRARCH)%.o: $(AVRSOURCE)%.c Makefile + $(CC) $(CCFLAGS) -c -o $@ -I. -I$(AVRAUTO) $< + + +$(AVRARCH)%: $(AVRARCH)%.o Makefile + $(CC) $(CCFLAGS) -o $@ $< -lm + +$(AVRARCH)%.sr: all $(AVRARCH)% + avr-objcopy -O srec $(AVRARCH)/$(*) $@ + +%.sr: $(AVRARCH)%.sr + @/bin/true + +%.load: compiled/avr/%.sr +# /work/andersb/atmel/uisp/uisp-0.2b/src/uisp \ +# -dno-poll -dstk200 --erase --upload if=$(AVRARCH)/$(*).sr + uisp \ + -dprog=stk200 \ + --erase \ + --upload if=compiled/avr/$*.sr + +%.load.stk500: compiled/avr/%.sr + uisp \ + -dprog=stk500 \ + --erase \ + --upload if=compiled/avr/$*.sr + +%.dump: all $(AVRARCH)/% + $(OBJDUMP) -D $(AVRARCH)/$(*) + +$(AVRARCH)fuse.sr: fuse.s + avr-gcc -o $(AVRARCH)fuse.o -c fuse.s + avr-objcopy -O srec $(AVRARCH)fuse.o $(AVRARCH)fuse.sr + +fuse.load: $(AVRARCH)fuse.sr + uisp \ + -dprog=stk200 \ + --erase \ + --segment=fuse \ + --upload if=compiled/avr/fuse.sr + +load.stk500: $(PROJECT).load.stk500 \ No newline at end of file diff --git a/linear_pendulum_2009/avr/compileUploadCurrentControl.sh b/linear_pendulum_2009/avr/compileUploadCurrentControl.sh new file mode 100755 index 0000000000000000000000000000000000000000..e97cfdfa62c342c42bb1305d2a7bf9a7c07d00fd --- /dev/null +++ b/linear_pendulum_2009/avr/compileUploadCurrentControl.sh @@ -0,0 +1,3 @@ +avr-gcc -mmcu=atmega16 -g -Wall -o current_control current_control.c +avr-objcopy -Osrec current_control current_control.sr +uisp -dprog=stk200 --erase --upload if=current_control.sr \ No newline at end of file diff --git a/linear_pendulum_2009/avr/compileUploadVelControl.sh b/linear_pendulum_2009/avr/compileUploadVelControl.sh new file mode 100755 index 0000000000000000000000000000000000000000..bad2b4b7d48faaecc7859da4f6633d7afcc1dbd8 --- /dev/null +++ b/linear_pendulum_2009/avr/compileUploadVelControl.sh @@ -0,0 +1,3 @@ +avr-gcc -mmcu=atmega16 -O -g -Wall -o vel_control.o vel_control.c pccom.c +avr-objcopy -Osrec vel_control.o vel_control.sr +uisp -dprog=stk200 --erase --upload if=vel_control.sr \ No newline at end of file diff --git a/linear_pendulum_2009/avr/current_control.c b/linear_pendulum_2009/avr/current_control.c new file mode 100644 index 0000000000000000000000000000000000000000..5736fce012fb15b4b8fe9d0aa49e60937ae30168 --- /dev/null +++ b/linear_pendulum_2009/avr/current_control.c @@ -0,0 +1,225 @@ +/* +*************************************************************** + + Current regulation - Pontus Giselsson, Per-Ola Larsson 18/02/09 + for LTH - reglerteknik + +*************************************************************** +*/ +#include <avr/twi.h> +#include <avr/io.h> +#include <avr/signal.h> +#include <avr/interrupt.h> +#include <inttypes.h> + + +// control variables +volatile int16_t ref=0; // reference value (from velocity controller) +volatile int16_t y; // measurement, 9 frac bits +volatile uint16_t low, high; // when reading AD-conversion +volatile int16_t e = 0; // control error, 9 frac bits +volatile int16_t v = 0; // temporary ctrl signal, 9 frac bits +volatile int16_t vSat = 0; // saturated temporary ctrl signal, 9 frac bits +volatile int16_t I = 0; // integral part of ctrl, 13 frac bits +volatile int16_t u = 0; // ctrl signal = pwm high time (8 bits) +volatile int16_t K = 111; // 7 frac bits +volatile int16_t Ke = 38; // 7 frac bits, K*h/Ti +volatile int8_t intCond = 0; // flag for conditional integration + +#define V_MAX 508 //max/min for saturation +#define V_MIN -508 + +// twi variable +volatile int16_t status; + +// logging variables +/* +#define log_len 100 +volatile int16_t ctrl_log[log_len]; +volatile int16_t error_log[log_len]; +volatile int32_t I_log[log_len]; +volatile int16_t skipSamples = 1000; +volatile int16_t countSamples = 0; +volatile int16_t jj=0; +volatile int8_t stop = 0; +*/ + +/* Routine used to transmit bytes on the serial port */ +/* +static void putchar(unsigned char ch) +{ + while ((inp(UCSRA) & 0x20) == 0) {}; + outp(ch, UDR); + while ((inp(UCSRA) & 0x20) == 0) {}; +} +*/ + +/* Send logged data over Serial connection */ +/* +static inline void sendData() { + int16_t ii = 0; + while (ii < log_len) { + + putchar((unsigned char) ((ctrl_log[ii]&0xff00)>>8)); + putchar((unsigned char) (ctrl_log[ii]&0x00ff)); + + putchar((unsigned char) ((error_log[ii]&0xff00)>>8)); + putchar((unsigned char) (error_log[ii]&0x00ff)); + + putchar((unsigned char) ((I_log[ii]&0xff000000)>>24)); + putchar((unsigned char) ((I_log[ii]&0x00ff0000)>>16)); + + putchar((unsigned char) ((I_log[ii]&0x0000ff00)>>8)); + putchar((unsigned char) (I_log[ii]&0x000000ff)); + + ii++; + } +} +*/ + + + +/* Timer 2 compare match interupt, 28.8 kHz, syncronized with pwm-period */ +SIGNAL(SIG_OUTPUT_COMPARE2) { + + // Start AD conversion + ADCSRA |= BV(ADSC); + + // Read previous AD-conversion result + low = inp(ADCL); + high = inp(ADCH); + y = ((int16_t)((high<<8) | low)) - 512; // y 9 frac bits + + // control error + e = ref-y; // e 9 frac bits + + // temporary ctrl-signal + v = (((K*e+64)>>7)+((I+8)>>4)); + + // variable that decides if I-part should be updated + intCond = 1; + + // saturation and update integral part of ctrl with antiwindup + if (v > V_MAX) { + vSat = V_MAX; + if (e > 0) + intCond = 0; + } else if (v < V_MIN) { + vSat = V_MIN; + if (e < 0) + intCond = 0; + } else { + vSat = v; + } + + if (intCond) + I = I + (((Ke*e)+(1<<2))>>3); + + // ctrl signal, 7 bits + direction + u = ((vSat+2)>>2); //7 frac bits to pwm + + // set pwm switching time + if (u < 0) { + PORTC |= 0x80; // set direction of motion + OCR1BL = ((unsigned char) (-u)); // set length of pwm-high + } else { + PORTC &= 0x7f; // set direction of motion + OCR1BL = ((unsigned char) (u)); // set length of pwm-high + } + + + // TWI-communication, recieve reference from velocity controller + if ((BV(TWINT)&inp(TWCR))) { + status = (inp(TWSR)&0xf8); + // status 0x80 means data recieved + if (status == 0x80) { + ref = (int16_t)((int8_t)inp(TWDR)); // read 8 bit reference + ref = (ref<<2); // shift up 2 steps for 10 bits reference in loop + } + else { + } + outp(BV(TWINT)|BV(TWEN)|BV(TWEA),TWCR); + } + + // For logging purposes + /* + countSamples++; + if (countSamples == skipSamples) { + ctrl_log[jj] = u; + I_log[jj] = I; + error_log[jj] = e; + jj++; + countSamples = 0; + + // Stop after a while + if ((jj == (log_len-1)) & !stop) { + outp(0x7f,OCR1BL); + stop = 1; + sendData(); + } + } + */ + +} + + +int main() +{ + // clear interrupts (might not be needed) + cli(); + + //Port directions + outp(0x08,PORTC); // pull up on overtemperature signals + outp(0xa0,DDRC); // output on direction and brake + outp(0x10,DDRD); // output on pwm-signal + outp(0x40,DDRB); // temp pwm output + + /* Timer section */ + // Timer 1, fast PWM no prescaling (non-inverting mode (start high, switch to low)) + outp(BV(COM1B1)|BV(WGM11)|BV(WGM10),TCCR1A); + outp(BV(CS10)|BV(WGM13)|BV(WGM12),TCCR1B); + + // Reset Timer1 and set TOP-value to 128 (means 7-bit pwm-signal-> 115.2 kHz) + outp(0x00,OCR1AH); + outp(0x7f,OCR1AL); + outp(0x00,TCNT1H); + outp(0x00,TCNT1L); + outp(0x00,OCR1BH); + outp(0x7f,OCR1BL); // to not start motor-rotation before control + + + + /* Timer 2, 4 times pwm-period, for control sampling, prescaler 8, 28.8 kHz */ + outp(BV(WGM21)|BV(CS21),TCCR2); + outp(0x3f,OCR2); + /* Reset timer 2 */ + outp(0,TCNT2); + + // Enable timer2 compare match interrupts + outp(BV(OCIE2),TIMSK); + + //Serial communication + outp(0x00, UCSRA); // USART: + outp(0x18, UCSRB); // USART: RxEnable|TxEnable + outp(0x86, UCSRC); // USART: 8bit, no parity + outp(0x00, UBRRH); // USART: 115200 @ 14.7456MHz + outp(7,UBRRL); // USART: 115200 @ 14.7456MHz + + /* AREF (AREF is 5V) pin external capacitor, MUX0 for current measurement */ + outp(BV(REFS0)|BV(MUX0),ADMUX); + + // Enable ADC, start first conversion, prescaler 32, not free running mode + outp(BV(ADEN)|BV(ADSC)|BV(ADPS2)|BV(ADPS0),ADCSRA); + + + // Initialize TWI + outp(0x02,TWBR); // set SCL-frequency CPU-freq/(16+2*2) + outp(0x02,TWAR); // slave address + outp(BV(TWEA)|BV(TWEN),TWCR); // enable TWI, enable ACK + + //Enable interrupts + sei(); + + // loop + while (1) {} +} diff --git a/linear_pendulum_2009/avr/pccom.c b/linear_pendulum_2009/avr/pccom.c new file mode 100644 index 0000000000000000000000000000000000000000..7d074ad8c4617dcc31cf61cbf30011b6cfa6b4e5 --- /dev/null +++ b/linear_pendulum_2009/avr/pccom.c @@ -0,0 +1,199 @@ +// pccom.c: Communication interface to PC via serialio_core. + +#include "pccom.h" +#include "serialio_core.h" +#include "vel_control.h" + +/* + * Serial I/O assignments + * + * AO 0 -- Axis 1 motor voltage + * A0 1 -- Axis 1 velocity reference ! + * + * EI 0 -- Axis 1 position ! + * EI 1 -- Axis 1 filtered velocity ! + * EI 5 -- Axis 1 position with predicted fraction + * EI 6 -- Axis 1 position unquantized + * + * AI 2 -- Axis 1 current + * AI 3 -- Pendulum angle ! + * + * AI 4 -- Axis 1 motor voltage (actual) + * + * DI 0 -- Axis 1 endpoint sensor + */ + +#define POLL_AXIS1_POSITION 0x0001 +#define POLL_AXIS1_VELOCITY 0x0002 +#define POLL_PEND_ANGLE 0x0004 +#define POLL_CURRENT_REFERENCE 0x0008 +//#define POLL_AXIS1_RESET 0x0008 +#define POLL_CONFIG 0x8000 + +static volatile uint16_t pccom_poll=0; + + +// ---------------------------------- Receiver -------------------------------- + +// only call from UART receive interrupt +static inline void addPoll(uint16_t flags) { + pccom_poll |= flags; +} + + +void pccom_receiveByte(char ch) +{ + switch (serialio_RXC(ch)) { + case serialio_clearbit: { + switch (serialio_channel) { + } + } break; + case serialio_setbit: { + switch (serialio_channel) { + } + } break; + case serialio_pollbit: { + switch (serialio_channel) { + //case 0: { addPoll(POLL_AXIS1_RESET); } break; + } + } break; + case serialio_pollchannel: { + switch (serialio_channel) { + case 0: { addPoll(POLL_AXIS1_POSITION); } break; + case 1: { addPoll(POLL_AXIS1_VELOCITY); } break; + case 2: { addPoll(POLL_PEND_ANGLE); } break; + case 3: { addPoll(POLL_CURRENT_REFERENCE); } break; + case 31: { addPoll(POLL_CONFIG); } break; + } + } break; + case serialio_setchannel: { + switch (serialio_channel) { + case 0: { + setRef(serialio_value - (1L<<12)); + } break; + } + } break; + case serialio_more: { + } break; + + case serialio_error: { + } break; + } +} + + +// ----------------------------------- Sender ------------------------------ +// return true if more to send +static uint8_t sendConfigPacket(uint8_t position) +{ + switch (position) { + case 0: CONF_ANALOG_IN(0, CONF_RESOLUTION(16)); break; // Position (now reference) + case 1: CONF_ANALOG_IN(0, CONF_MIN(CONF_NEGATIVE_VOLT(1))); break; + case 2: CONF_ANALOG_IN(0, CONF_MAX(CONF_POSITIVE_VOLT(1))); break; + + case 3: CONF_ANALOG_IN(1, CONF_RESOLUTION(18)); break; // Velocity estimate + case 4: CONF_ANALOG_IN(1, CONF_MIN(CONF_NEGATIVE_VOLT(1))); break; + case 5: CONF_ANALOG_IN(1, CONF_MAX(CONF_POSITIVE_VOLT(1))); break; + + case 6: CONF_ANALOG_IN(2, CONF_RESOLUTION(11)); break; // Pend angle measurement + case 7: CONF_ANALOG_IN(2, CONF_MIN(CONF_NEGATIVE_VOLT(1))); break; + case 8: CONF_ANALOG_IN(2, CONF_MAX(CONF_POSITIVE_VOLT(1))); break; + + case 9: CONF_ANALOG_IN(3, CONF_RESOLUTION(8)); break; // Current reference + case 10: CONF_ANALOG_IN(3, CONF_MIN(CONF_NEGATIVE_VOLT(1))); break; + case 11: CONF_ANALOG_IN(3, CONF_MAX(CONF_POSITIVE_VOLT(1))); break; + + case 12: CONF_ANALOG_OUT(0, CONF_RESOLUTION(13)); break; // Reference to vel-ctrl + case 13: CONF_ANALOG_OUT(0, CONF_MIN(CONF_NEGATIVE_VOLT(1))); break; + case 14: CONF_ANALOG_OUT(0, CONF_MAX(CONF_POSITIVE_VOLT(1))); break; + + default: CONF_END(); return 0; + } + + return 1; +} + +static uint8_t sendNextPacket() // returns 1 if a packet was available +{ + static int8_t configPosition = -1; + +#define toPoll pccom_poll // OK since sender and receiver are mutexed + + // Send _first_ requested item (only one packet!) + if (toPoll & POLL_AXIS1_POSITION) { + toPoll &= ~POLL_AXIS1_POSITION; + serialio_putchannel(0, getPosition()+(1L<<15)); + } + else if (toPoll & POLL_AXIS1_VELOCITY) { + toPoll &= ~POLL_AXIS1_VELOCITY; + serialio_putchannel(1, getVelocity()+(1L<<17)); + } + else if (toPoll & POLL_PEND_ANGLE) { + toPoll &= ~POLL_PEND_ANGLE; + serialio_putchannel(2, getAngle()+(1L<<10)); + } + else if (toPoll & POLL_CURRENT_REFERENCE) { + toPoll &= ~POLL_CURRENT_REFERENCE; + serialio_putchannel(3, getCurrentRef()+(1L<<7)); + } + else if (toPoll & POLL_CONFIG) { + if (configPosition < 0) configPosition = 0; // Start sending config? + if (!sendConfigPacket(configPosition)) { // Last packet? + configPosition = -1; + toPoll &= ~POLL_CONFIG; + } + else configPosition++; // Advance to next packet + } + else return 0; // should never happen! + + return 1; +} + +// ---- Send buffering ---- + +#define PCCOM_SEND_BUFFER_SIZE 6 // just enough for one serialio_putchannel() packet + +uint8_t pccom_sendBuffer[PCCOM_SEND_BUFFER_SIZE]; // Updated by serialio_putchar() +uint8_t pccom_sendBufferPosition = 0; // Updated by pccom_getNextByteToSend() +uint8_t pccom_sendBufferUsed = 0; // Updated by serialio_putchar(), + // and pccom_getNextByteToSend() when + // the buffer is empty. + +void serialio_putchar(unsigned char ch) { + if (pccom_sendBufferUsed < PCCOM_SEND_BUFFER_SIZE) { + pccom_sendBuffer[pccom_sendBufferUsed] = ch; + pccom_sendBufferUsed++; + } + else { + // Buffer already full -- must never happen! + // main_emergencyStop(); // show that something is wrong + } +} + +int16_t pccom_getNextByteToSend() +{ + if (pccom_sendBufferPosition >= pccom_sendBufferUsed) { + // Try to refill buffer + pccom_sendBufferPosition = 0; + pccom_sendBufferUsed = 0; + + //if (!sendNextPacket()) return -1; + sendNextPacket(); + } + + if (pccom_sendBufferPosition >= pccom_sendBufferUsed) return -1; // no data + else { + // Return next byte + uint8_t data = pccom_sendBuffer[pccom_sendBufferPosition]; + pccom_sendBufferPosition++; + return data; + } +} + + +// ------------------------------ Initialization ------------------------------ + +void pccom_init() +{ + serialio_init(); +} diff --git a/linear_pendulum_2009/avr/pccom.h b/linear_pendulum_2009/avr/pccom.h new file mode 100644 index 0000000000000000000000000000000000000000..14c4c75d7da795a47f3e884364c2ab03e3e1353e --- /dev/null +++ b/linear_pendulum_2009/avr/pccom.h @@ -0,0 +1,21 @@ +// pccom.h: Communication interface to PC via serialio_core. + +#ifndef __pccom_h +#define __pccom_h + +#include <inttypes.h> +#include "vel_control.h" + +void pccom_init(); + +// Concurrency constraints: +// Receiving and sending are mutually exclusive! +void pccom_receiveByte(char ch); +int16_t pccom_getNextByteToSend(); // returns -1 for nothing to send + + +// ------------------------------- Callbacks ---------------------------------- + +void serialio_putchar(unsigned char ch); + +#endif diff --git a/linear_pendulum_2009/avr/serialio_core.h b/linear_pendulum_2009/avr/serialio_core.h new file mode 100644 index 0000000000000000000000000000000000000000..5ca09ae04477744fc4cec9bb34c4def7e70ca5cf --- /dev/null +++ b/linear_pendulum_2009/avr/serialio_core.h @@ -0,0 +1,197 @@ +// serialio_core.h: Serialio protocol without the hardware bindings. + +#ifndef __serialio_core_h +#define __serialio_core_h +/* + * Digital in/out and poll commands are sent as one byte: + * + * +-+-+-+-+-+-+-+-+ + * |0|0 0| chan | Bit clear + * +-+-+-+-+-+-+-+-+ + * + * +-+-+-+-+-+-+-+-+ + * |0|0 1| chan | Bit set + * +-+-+-+-+-+-+-+-+ + * + * +-+-+-+-+-+-+-+-+ + * |0|1 0| chan | Bit get + * +-+-+-+-+-+-+-+-+ + * + * +-+-+-+-+-+-+-+-+ + * |0|1 1| chan | Channel get + * +-+-+-+-+-+-+-+-+ + * + * + * Channels are sent as 2 to 6 bytes, depending on resolution: + * + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * 2 |1| bit8...bit2 | |0|bit| chan | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * 3 |1|bit15...bit9 | |1| bit8...bit2 | |0|bit| chan | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + * ... + * + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * 6 |1|bit31...bit30| |1|bit29...bit23| ... |0|bit| chan | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + * + * + * Channel 31 is special, as it serves as the configuration channel. When + * reading from it multiple responses are sent with the following layout + * + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | command specific data |cmd|kind |conf chan| + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * + * kind: 000 == end of configuration + * 001 == digital in + * 010 == digital out + * 011 == analog in + * 100 == analog out + * 101 == counter in + * + *cmd == 0 (Resolution) + * + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | | # of bits |0 0|kind |conf chan| + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * + * # of bits (1..32) + * + *cmd == 1 (Minimum value) + * + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | minimum |S| unit|0 1|kind |conf chan| + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * + * S (sign): 0 == + + * 1 == - + * unit: 000 == V + * 001 == mV + * 010 == uV + * 100 == A + * + *cmd == 2 (Maximum value) + * + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * | maximum |S| unit|1 0|kind |conf chan| + * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + * + * S (sign): 0 == + + * 1 == - + * unit: 000 == V + * 001 == mV + * 010 == uV + * 100 == A + */ + +// ----------------------------- Interface ----------------------------------- + +static void serialio_init(); + +// ---- Receiving ---- +typedef enum { + serialio_error, serialio_more, serialio_clearbit, serialio_setbit, + serialio_setchannel, serialio_pollbit, serialio_pollchannel +} serialio_rxc_status; + +static serialio_rxc_status serialio_RXC(unsigned char ch); + +// ----- Sending ----- +void serialio_putchar(unsigned char ch); // Callback + +static void serialio_putbit(unsigned char channel, unsigned char value); +static void serialio_putchannel(unsigned char channel, unsigned long value); + +// ---- Configuration macros ---- +#define CONF_DIG_IN(channel) (0x20 | (channel)&0x1f) +#define CONF_DIG_OUT(channel) (0x40 | (channel)&0x1f) + +#define CONF_END() serialio_putchannel(31, 0) +#define CONF_DIGITAL_IN(chan, config) \ + serialio_putchannel(31, (0x20|(chan&0x1f)|(config&0xffffff00))) +#define CONF_DIGITAL_OUT(chan, config) \ + serialio_putchannel(31, (0x40|(chan&0x1f)|(config&0xffffff00))) +#define CONF_ANALOG_IN(chan, config) \ + serialio_putchannel(31, (0x60|(chan&0x1f)|(config&0xffffff00))) +#define CONF_ANALOG_OUT(chan, config) \ + serialio_putchannel(31, (0x80|(chan&0x1f)|(config&0xffffff00))) +#define CONF_ENCODER_IN(chan, config) \ + serialio_putchannel(31, (0xa0|(chan&0x1f)|(config&0xffffff00))) +#define CONF_RESOLUTION(bits) (((bits)<<10)|0x000) +#define CONF_MIN(value) ((value)|0x100) +#define CONF_MAX(value) ((value)|0x200) +#define CONF_NEGATIVE_VOLT(volt) (((long)(volt)<<14)|0x2000) +#define CONF_POSITIVE_VOLT(volt) ((long)(volt)<<14) +#define CONF_NEGATIVE_MILLIVOLT(millivolt) (((long)(millivolt)<<14)|0x2400) +#define CONF_POSITIVE_MILLIVOLT(millivolt) ((long)(millivolt)<<14|0x400) +#define CONF_POSITIVE_AMPERE(ampere) (((long)(ampere)<<14)|0x1000) + + +// --------------------------- Implementation --------------------------------- + +static volatile unsigned long serialio_value; +static volatile unsigned char serialio_channel, serialio_length; + +static void serialio_putbit(unsigned char channel, unsigned char value) +{ + if (value) { + serialio_putchar(0x20 | (channel & 0x1f)); + } else { + serialio_putchar(0x00 | (channel & 0x1f)); + } +} + +static void serialio_putchannel(unsigned char channel, unsigned long value) +{ + if (value >= (1L<<30)) { serialio_putchar(0x80 | ((value >> 30) & 0x03)); } + if (value >= (1L<<23)) { serialio_putchar(0x80 | ((value >> 23) & 0x7f)); } + if (value >= (1L<<16)) { serialio_putchar(0x80 | ((value >> 16) & 0x7f)); } + + if (value >= (1L<< 9)) { serialio_putchar(0x80 | ((value >> 9) & 0x7f)); } + //serialio_putchar(0x80 | ((value >> 9) & 0x7f)); // DEBUG + + serialio_putchar(0x80 | ((value >> 2) & 0x7f)); + serialio_putchar(((value << 5) & 0x60) | (channel & 0x1f)); +} + +static void serialio_init() +{ + serialio_value = 0; + serialio_channel = 255; + serialio_length = 0; +} + +static serialio_rxc_status serialio_RXC(unsigned char ch) { + unsigned char result = serialio_error; + + if (serialio_length == 0) { serialio_value = 0; } + serialio_length++; + if ((ch & 0x80) == 0x80) { + // Collect yet another byte for later processing + serialio_value = (serialio_value << 7) | (ch & 0x7f); + result = serialio_more; + } else { + serialio_value = (serialio_value << 2) | ((ch & 0x60) >> 5); + serialio_channel = ch & 0x1f; + if (serialio_length == 1) { + switch (serialio_value & 0x03) { + // Digital output buffer (ULN2803A) is inverting + case 0: { result = serialio_clearbit; } break; + case 1: { result = serialio_setbit; } break; + case 2: { result = serialio_pollbit; } break; + case 3: { result = serialio_pollchannel; } break; + } + } else { + result = serialio_setchannel; + } + serialio_length = 0; + } + return result; +} + +#endif diff --git a/linear_pendulum_2009/avr/vel_control.c b/linear_pendulum_2009/avr/vel_control.c new file mode 100644 index 0000000000000000000000000000000000000000..ea57e49244a2fb75427ba2343d565d99cadf03bf --- /dev/null +++ b/linear_pendulum_2009/avr/vel_control.c @@ -0,0 +1,336 @@ +/* +************************************************************** + + Current regulation - Pontus Giselsson, Per-Ola Larsson 18/02/09 + for LTH - reglerteknik + +*************************************************************** +*/ +#include <avr/twi.h> +#include <avr/io.h> +#include <avr/signal.h> +#include <avr/interrupt.h> +#include <inttypes.h> + +#include "pccom.h" +#include "vel_control.h" + + +// reference variables +volatile int32_t ref = 0; // 11 frac bits +volatile int32_t refCtrl = 0; // ref used in ctrl-loop (=ref sent from simulink) + + +// velocity control variables +volatile int32_t u = 0; // 11 frac bits +volatile int32_t uSend = 0; // ctrl sent to simulink +volatile int32_t v = 0; // 11 frac bits +volatile int32_t vSat = 0; +volatile int8_t brake = 0; // brake variable if pos-sample missed +volatile int32_t I = 0; // 11 frac bits +volatile int32_t e = 0; // 11 frac bits +volatile int8_t intCond = 0; + + +volatile int32_t K = 807; // 6 frac bits, prop constant +volatile int32_t Ke = 13; // 6 frac bits, integral constant +volatile int8_t fr_comp = (10<<3); +#define V_MAX (120<<4) +#define V_MIN (-120<<4) + + +// encoder variables +#define ENCODERY (PIND&0x04) //Positional encoder pins +#define ENCODERX ((PINB&0x02)<<1) //Positional encoder pins (shift one step for faster comparison with Y) + +// position variables +volatile int16_t pos = 0; // position in tics +volatile int16_t posTemp = 0; // position sent to simulink +volatile int16_t posCtrl = 0; // position used in ctrl-loop +volatile int16_t oldPos = 0; // previous pos used for velocity estimation +volatile int8_t newX; // encoder signal +volatile int8_t newY; // encoder signal +volatile int8_t oldX; // previous encoder signal +volatile int8_t oldY; // previous encoder signal + +// velocity estimation parameters +volatile int32_t velEst = 0; // vel-estimate, 5 frac bits +volatile int32_t velEstSend = 0; // vel-estimate sent to simulink +int16_t a = 116; // 7 frac bits (parameter in velocity estimate) +int16_t b = 152; // 5 frac bits (parameter in velocity estimate) + +// adc measurement variables +volatile int16_t low; // temporary variable for ad-reading +volatile int16_t high; // temporary variable for ad-reading +volatile int16_t angleOffset = 0; + +/* return position (in tics) */ +int32_t getPosition() { + cli(); + posTemp = pos; + sei(); + return ((int32_t) posTemp); +} + + +/* return velocity (in mm/s) */ +int32_t getVelocity() { + return velEstSend; +} + + +/* return last angle measurement */ +int16_t getAngle() { + low = inp(ADCL); + high = inp(ADCH); + return (((int16_t) ((high<<8) | low) - 512)-angleOffset); +} + + +/* return current-reference */ +int32_t getCurrentRef() { + return uSend; +} + + +/* Set new reference value */ +void setRef(int32_t newRef) { + ref = newRef; +} + +/* Routine used to initialize the positional encoders */ +void initPos() +{ + oldX = ENCODERX; + oldY = ENCODERY; +} + +/* Timer 2, Encoder counter, 73 kHz updates position variable */ +SIGNAL(SIG_OUTPUT_COMPARE2) { + + // Update position from encoder + newX = ENCODERX; + newY = ENCODERY; + if((newX != oldX) || (newY != oldY)) { //Check if any value changed + /* + sum = (oldX<<2)+oldY+newX+(newY>>2); + if (sum == 2 || sum == 4 || sum == 11 || sum == 13) { + pos = pos+1; + } else if (sum == 1 || sum == 7 || sum == 8 || sum == 14) { + pos = pos-1; + } else { + brake = 1; // emergency brake + } + */ + // Works like if-clauses above, but faster! + if ((oldX == newY) && (oldY != newX)) { + pos = pos+1; + } else if ((oldX != newY) && (oldY == newX)) { + pos = pos-1; + } else { + brake = 1; + } + oldX = newX; + oldY = newY; + } + +} + + +/* Timer 0, serial communication with simulink */ +SIGNAL(SIG_OUTPUT_COMPARE0) { + + TIMSK &= ~(BV(OCIE0)|BV(OCIE1A)); // Disable communication and ctrl-interrupts + + sei(); // enable interrupts from encoder-counter + + // Poll UART receiver + uint8_t status = UCSRA; + if (status & (1<<RXC)) { + char ch = UDR; + pccom_receiveByte(ch); + + if (status & ((1<<FE)|(1<<DOR)|(1<<PE))) { + //main_emergencyStop(); // stop on USART error + } + } + + // Poll UART sender + if (UCSRA & (1<<UDRE)) { + int16_t toSend = pccom_getNextByteToSend(); + if (toSend >= 0) UDR = (char)toSend; + } + + TIFR = (1<<OCF0); // skip pending interrupts from serial comm, (but not from ctrl) + + TIMSK |= (BV(OCIE0)|BV(OCIE1A)); // reenable communication and ctrl-interrupts + +} + + + +/* Timer 0, control loop , 1 kHz */ +SIGNAL(SIG_OUTPUT_COMPARE1A) { + + posCtrl = pos; // store pos to use in this loop + + sei(); // to enable interupts from encoder counter and communication + + // velocity estimation in mm/s + velEst = (((a*velEst+64)>>7)+b*(posCtrl-oldPos)); // 5 fracbits on velEst + oldPos = posCtrl; + + // store velEst and ref to be sent/used here + cli(); + velEstSend = velEst; + refCtrl = ref; + // ref = ref*(1-brake); // emergency stop + sei(); + + // control error + e = refCtrl-((velEst+16)>>5); // mm/s + + // temporary ctrl-signal + v = (((K*e+(1<<5))>>6)+((I+(1<<3))>>4)); + + // friction compensation + if (refCtrl > 0) { + v = v+fr_comp; + } else if (refCtrl < 0) { + v = v-fr_comp; + } + + // variable that decides if I-part should be updated + intCond = 1; + + // saturation of v + if (v > V_MAX) { + vSat = V_MAX; + if (e > 0) + intCond = 0; + } else if (v < V_MIN) { + vSat = V_MIN; + if (e < 0) + intCond = 0; + } else { + vSat = v; + } + + if (intCond) + I = I + (((Ke*e)+(1<<1))>>2); + + // scale ctrl-signal to send over twi + u = (vSat+8)>>4; // u=127 gives current = 6.75 A, vSat makes u saturate at 114 + + // u that is sent to simulink + cli(); + uSend = u; + sei(); + + + // TWI-communication to set current reference on the other atmel + // send start command + outp(BV(TWINT)|BV(TWEN)|BV(TWSTA),TWCR); + while (!(TWCR&BV(TWINT))) {} + + // Contact slave + outp(0x02,TWDR); // slave is 0x02 (sla+w) + outp(BV(TWINT)|BV(TWEN),TWCR); + while (!(TWCR&BV(TWINT))) {} + + // Send reference byte to slave + outp((int8_t)u,TWDR); // send 8 bits reference + outp(BV(TWINT)|BV(TWEN),TWCR); + while (!(TWCR&BV(TWINT))) {} + + // stop transmission + outp(BV(TWINT)|BV(TWEN)|BV(TWSTO),TWCR); + +} + + +int main() +{ + cli(); + + //Port directions + //outp(0x80,DDRB); // Led output + outp(0x10,DDRC); // timer calculation port + PORTD = 0x40; // pull up on reset switch + + + /* Timer section */ + // Enable timer0, timer1, timer2 compare match interrupts + outp(BV(OCIE0)|BV(OCIE1A)|BV(OCIE2),TIMSK); + + /* Timer 2, 73 kHz Prescaler 1, encoder counter for position measurement */ + outp(BV(WGM21)|BV(CS20),TCCR2); + outp(200,OCR2); + /* Reset timer 2 */ + outp(0,TCNT2); + + /* Timer 1, 1 kHz , prescaler 1, ctrl-loop */ + outp(BV(WGM12)|BV(CS10),TCCR1B); + outp(0x39,OCR1AH); + outp(0x7f,OCR1AL); + outp(0,TCNT1H); + outp(0,TCNT1L); + + + /* Timer 0, 40 kHz, Prescaler 8, serial communication */ + outp(BV(WGM01)|BV(CS01),TCCR0); + //outp(184-1,OCR0); // 10 kHz + outp(46-1,OCR0); // 40 kHz + /* Reset timer 0 */ + outp(0,TCNT0); + + + // syncronization (ctrl interrupt 34 micros before communication interrupt) + TCNT1 = TCNT0*8+500; + + + //Serial communication initialization + outp(0x00, UCSRA); // USART: + outp(0x18, UCSRB); // USART: RxEnable|TxEnable + outp(0x86, UCSRC); // USART: 8bit, no parity + outp(0x00, UBRRH); // USART: 115200 @ 14.7456MHz + outp(7,UBRRL); // USART: 115200 @ 14.7456MHz + + + /* AREF (AREF is 5V) pin external capacitor, ADC3 for pendulum angle */ + outp(BV(REFS0)|BV(MUX0)|BV(MUX1),ADMUX); + + // Enable ADC on adc3, start first conversion, prescaler 128, free running mode + outp(BV(ADEN)|BV(ADSC)|BV(ADATE)|BV(ADPS2)|BV(ADPS1)|BV(ADPS0),ADCSRA); + + + // Initialize Master TWI + outp(0x10,TWBR); // set SCL-frequency CPU-freq/(16+2*16) + outp(BV(TWEN),TWCR); // enable TWI + + // initialize position measurements + initPos(); + + // initialize pc-communication + pccom_init(); + + //Enable interrupts + sei(); + + // loop + while (1) { + // reset position, velocity estimate and integral part of ctrl if reset button pushed + if (!(PIND & 0x40)) { + cli(); + low = inp(ADCL); + high = inp(ADCH); + pos = 0; // reset position + angleOffset = ((int16_t) ((high<<8) | low) - 512); + oldPos = 0; + velEst = 0; // reset velocity estimate + I = 0; // reset integral part of controller + u = 0; // reset ctrl signal + sei(); + } + } +} diff --git a/linear_pendulum_2009/avr/vel_control.h b/linear_pendulum_2009/avr/vel_control.h new file mode 100644 index 0000000000000000000000000000000000000000..88d833be44856a97496eb28c92d4b9324f451871 --- /dev/null +++ b/linear_pendulum_2009/avr/vel_control.h @@ -0,0 +1,11 @@ +#ifndef __vel_control_h +#define __vel_control_h + + +void setRef(int32_t newRef); +int32_t getPosition(); +int32_t getVelocity(); +int16_t getAngle(); +int32_t getCurrentRef(); + +#endif diff --git a/linear_pendulum_2009/doc/2xcurrent sensor PCB.pdf b/linear_pendulum_2009/doc/2xcurrent sensor PCB.pdf new file mode 100644 index 0000000000000000000000000000000000000000..eb5ba64e0e56b6f58b228a1a4f98aca8380a3970 Binary files /dev/null and b/linear_pendulum_2009/doc/2xcurrent sensor PCB.pdf differ diff --git a/linear_pendulum_2009/doc/2xcurrent sensor SCH.pdf b/linear_pendulum_2009/doc/2xcurrent sensor SCH.pdf new file mode 100644 index 0000000000000000000000000000000000000000..93a5481637f8cb2cfc4ee527388b20f0e0c6491a Binary files /dev/null and b/linear_pendulum_2009/doc/2xcurrent sensor SCH.pdf differ diff --git a/linear_pendulum_2009/doc/ACS712.pdf b/linear_pendulum_2009/doc/ACS712.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7bbecd345ade3a2c4af23eb5d9ce5d9a39d8bd5d Binary files /dev/null and b/linear_pendulum_2009/doc/ACS712.pdf differ diff --git a/linear_pendulum_2009/doc/Atmega 16 connectors.pdf b/linear_pendulum_2009/doc/Atmega 16 connectors.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e85ae8bd2509bb3a81da8861512b7b0c8db14c43 Binary files /dev/null and b/linear_pendulum_2009/doc/Atmega 16 connectors.pdf differ diff --git a/linear_pendulum_2009/doc/Atmega16 dual powerdrive pendel 2008.pdf b/linear_pendulum_2009/doc/Atmega16 dual powerdrive pendel 2008.pdf new file mode 100644 index 0000000000000000000000000000000000000000..970c61998a2408f8b7912cef3651e5a760667217 Binary files /dev/null and b/linear_pendulum_2009/doc/Atmega16 dual powerdrive pendel 2008.pdf differ