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/current_control.c b/linear_pendulum_2009/avr/current_control.c new file mode 100644 index 0000000000000000000000000000000000000000..5ea0ae3cebc2cdc0f5d1df6161b73565f3a278dc --- /dev/null +++ b/linear_pendulum_2009/avr/current_control.c @@ -0,0 +1,422 @@ +/********************************************* + + Current regulation - Aleks Ponjavic 18/09/07 + for LTH - reglerteknik + + Note that the voltage is actually a duty + cycle for the PWM. The PWM is running at + 12V with a duty cycle set by a 10bit value + +*********************************************/ + +//#include <math.h> //Only include if sin will be used, takes up memory! +#include <avr/io.h> +#include <avr/signal.h> +#include <avr/interrupt.h> + +//Main control parameters +#define MODE 3 //1 = velocity, 2 = current, 3 = cascade, 0 = control off +#define U_MAX 400 //Voltage saturation point +#define U_FIXEDPOINT 3000 //Wanted current, used when MODE = 2, note this is in fixed point arithmetic 2^6 +#define U_REAL 100 //When MODE = 1, voltage used for left-right motion + +#define NBR_CHAN 1 + +//Current regulation parameters +#define KC_P 2 //Proportional constant +#define KC_I 0 //Integral constant (currently 0 as PI_C is run as many times as PI_V integral control + // -does not make sense when in cascade) for MODE = 2, KC_I = 5 is ok +#define ANTI_WINDUP 10 //Anti windup constant + +//Current regulation variables +volatile long v_C = 0; //Voltage +volatile long e_C = 0; //Error +volatile long I_C = 0; //Integral part +volatile long a_C = 0; //Current (amperes) +volatile long u_C = 0; //Used for anti windup +volatile long U = 0; //= +/- U_FIXEDPOINT, used for current regulation, MODE = 2 + +//Velocity regulation parameters +#define V_REQ 70 //Required velocity, note the magnitude of this depends on STEP +#define KV_P 8 //Proportional constant +#define KV_I 2 //Integral constant + +//Velocity regulation variables +volatile long v_V; //Voltage +volatile long e_V = 5; //Error +volatile long I_V = 0; //Integral part +volatile long u_V = 0; +volatile long VEL = 0; //= +/- V_REQ + +//Position/velocity parameters +#define STEP 100 //Amount of steps between position measurements, used for determining velocity + +#define ENCODERY (PIND&(uint8_t)(1<<2)) //Positional encoder pins +#define ENCODERX (PINB&(uint8_t)(1<<1)) //Positional encoder pins + +//Position/velocity variables +volatile int positions[STEP]; //Array holding history of position at each sample +volatile int currentPos; //Current sample +volatile long vel = 0; //Current velocity +volatile int oldX; //Used for positional encoders +volatile int oldY; //Used for positional encoders +volatile int sum; //Used for positional encoders +volatile long pos = 0; //Current position + +//Sampling variables +volatile int sendData[8]={0,7,6,5,4,3,2,1}; //Array holding data that is sent through serial port +volatile char dataAvailable = 0; //Flag if an unhandled ADC has been completed +volatile int nbrSampSinceLastSend = 0; //Checks if a sample has been missed and if so discards it +volatile char logStarted = 0; //Flag + +volatile int dir = 0; //Direction of motion (+ = right, - = left) + +/* Routine used to set the red LED */ +void setLED(uint8_t on) +{ + if (on) PORTB &= ~0x80; //Turn on + else PORTB |= 0x80; //Turn off +} + +/* Routine used to initialize the positional encoders */ +void initPos() +{ + oldX = ENCODERX; + oldY = ENCODERY; +} + +/* Routine used to track the cart position */ +void setPos() +{ + int newX = ENCODERX; + int newY = ENCODERY; + if((newX != oldX) || (newY != oldY)) //Check if any value changed + { + sum = (oldX<<2)+oldY+newX+(newY>>2); //Find state + if(sum == 2 || sum == 4 || sum == 11 || sum == 13) //Predetermined values determine direction + pos++; + else + pos--; + oldX = newX; + oldY = newY; + } +} + +/* Routine used to set the PWM duty cycle */ +void setMotorVoltage(int axis, int u) +{ + int pwm; + + u >>= 1; //Taking specified 10bit value but PWM is 9bit, therefore compensate + + if (axis==0) //Axis determines which motor is used but currently only one motor available + { + if (u < 0) { //Determine direction + pwm = -u; + PORTC |= 0x80; + } + else + { + pwm = u; + PORTC &= ~0x80; + } + if (pwm > 0x3ff) { pwm = 0x3ff; } + OCR1B = pwm & 0x3ff; //Set PWM duty cycle + } +} + + +/* 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) {}; +} + +/* Routine for updating the motor voltage input with current control */ +void PI_C(long input, long ref) +{ + //Convert adc input to current. Calculations listed in trac-wiki + a_C = input*5-2601; //Get current + a_C*=3; //Convert it to + e_C = ref+a_C; + + v_C = KC_P*(e_C)+KC_I*I_C+60*dir; //Set voltage + + v_C = (v_C >> 6); //Remove fixed point arithmetic + + if(v_C <=U_MAX && v_C>-U_MAX) //Check for saturation + { + u_C = v_C; + setLED(1); //Notify that PI is running + } + else + { + if(v_C<0) u_C=-U_MAX; + else u_C=U_MAX; + setLED(0); + } + + if((e_C>>5)==0) //Depending on sign 0 gets rounded as 0 or -1, compensate + I_C+= 1 + ANTI_WINDUP*(u_C-v_C); + else + I_C += (e_C >> 5) + ANTI_WINDUP*(u_C-v_C); //Update integral part + + setMotorVoltage(0,v_C); //Set voltage +} + +/* Routine for updating the input for the current control with speed regulation */ +void PI_V(long input) +{ + e_V = (VEL<<4)-(input<<4); //Calculate error + v_V = KV_P*e_V + KV_I*I_V; //Set voltage + + v_V = (v_V >> 4)+60*dir; //Remove fixed point arithmetic and add friction compensation + + if(v_V <=U_MAX && v_V>-U_MAX) //Check for saturation + { + u_V = v_V; + setLED(1); //Notify that PI is running + } + else + { + if(v_V<0) u_V=-U_MAX; + else u_V=U_MAX; + setLED(0); //Notify saturation + } + + if((e_V>>6)==0) //Again, sign rounding compensation + I_V++; + else + I_V += e_V>>6; //Update integral part + + if(MODE==1)setMotorVoltage(0,u_V); //Set voltage if in velocity control, otherwise cascaded current will set voltage +} + +/* Interrupt service routing for handling timer 1 */ +SIGNAL(SIG_OVERFLOW1) +{ + static long ctr1; + static long ctr2; + ctr1++; + + //Sinewave motion in steps + if(ctr1>=3) + { + outp(BV(ADEN)|BV(ADSC)|BV(ADIE)|BV(ADPS2)|BV(ADPS1)|BV(ADPS0),ADCSR); //Request ADC + + ctr2++; + if(ctr2>=8000) //Delay between direction switch + { + //Right-left motion + if(dir==-1) + { + if(MODE) //Check that control is on + { + U = -U_FIXEDPOINT; //Set required current (in fixed point arithmetic) + VEL = -V_REQ; //Set required velocity + I_C = 0; //Reset integral part + I_V = 0; //Reset integral part + } + else + { + setMotorVoltage(0,-U_REAL); //If no control simply set voltage to required value + } + dir = 1; //Change direction + } + else + { + if(MODE) //Repeat of previous if statement but in reverse direction + { + U = U_FIXEDPOINT; + VEL = V_REQ; + I_C = 0; + I_V = 0; + } + else + { + setMotorVoltage(0,U_REAL); + } + dir = -1; + } + ctr2=0; //Reset counter + } + ctr1=0; //Reset counter + } +} + +/* Not currently in use but necessary to catch interrupt on timer0 overflow */ +SIGNAL(SIG_OVERFLOW0) +{ + static int ctr0; + ctr0++; + if (ctr0>=10) + { + //putchar('O'); + ctr0 = 0; + } +} + +/* Interrupt service routine for handling ADC interrupts */ +SIGNAL(SIG_ADC) +{ + static int ctrA; + unsigned char lbyte,hbyte; + ctrA++; + + lbyte = inp(ADCL); //Get ADC result low byte + hbyte = inp(ADCH); //Get ADC result high byte + if (ctrA>=1) + { + if (logStarted == 1) + { + cli(); + dataAvailable = 1; //Flag data being available + nbrSampSinceLastSend++; //Increase samples since last send + sendData[0] = (int)((hbyte<<8)+lbyte); //Store result in data array + sei(); + } + else + { + dataAvailable = 1; + sendData[0] = (int)((hbyte<<8)+lbyte); + } + ctrA = 0; + } +} + + +/* Interrupt service routine for handling incoming bytes on the serial port */ +SIGNAL(SIG_UART_RECV) +{ + char ch = inp(UDR); //Read input + cli(); + logStarted = 1; //Flag log started + sei(); +} + +/* Interrupt service routine for timer 0; Not in use */ +/*SIGNAL(SIG_OVERFLOW0) +{ + static int ctr0; + ctr0++; + if (ctr0>=10) + { + //putchar('O'); + ctr0 = 0; + } +}*/ + +int main() +{ + int i,j; //Used for for loops + + //Port directions + outp(0x80,PORTB); // LED off + outp(0x80,DDRB); // output on LED + outp(0x0c,PORTC); // pull up on overtemp signals + outp(0xf0,DDRC); // output on dir and brake + outp(0x80,PORTD); // pull up on reset switch + outp(0x30,DDRD); // output on pwm1&2 + + //Counters + outp(BV(TOIE0)|BV(TOIE1),TIMSK);// Enable TCNT0 and TCNT1 overflow interrupts + outp(0x00,TCNT0); // Reset TCNT0, CLK/1024 + outp(0x00,TCNT1H); // Reset TCNT1 high, CLK/512 + outp(0x00,TCNT1L); // Reset TCNT1 low, CLK/512 + outp(BV(CS02)|BV(CS00), TCCR0); // 1024 + + //Serial communication + outp(0x00, UCSRA); // USART: + outp(0x98, UCSRB); // USART: RxIntEnable|RxEnable|TxEnable + outp(0x86, UCSRC); // USART: 8bit, no parity + outp(0x00, UBRRH); // USART: 115200 @ 14.7456MHz + outp(7, UBRRL); // USART: 115200 @ 14.7456MHz + + // Timer 1 (PWM) 14.7456MHz/1/512 -> 28.8kHz + // PWM needs to run at 1/512 in order to avoid vibration excitation + // OC1A & OC1B 9 bit fast PWM, active high + outp(BV(COM1A1)|BV(COM1B1)|BV(WGM11),TCCR1A); + outp(BV(WGM12)|BV(CS10),TCCR1B); // No prescaler, or /1... + + outp(BV(REFS0)|BV(MUX0),ADMUX); //AREF (AREF is 5V) pin external capacitor, MUX0 for current, MUX3 for pendulum angle + + // Enable ADC interrupts, CLK/128 + outp(BV(ADEN)|BV(ADSC)|BV(ADIE)|BV(ADPS2)|BV(ADPS1)|BV(ADPS0),ADCSR); + + unsigned int data[8]={1,2,3,4,5,6,7,8}; //Reset data array + + /* Wait a little bit, probably not needed...*/ + int tmp; + for(i=0;i<2000;i++) + for(j=0;j<400;j++) + tmp = j*j*j; + + //Initialize encoder + initPos(); + + //Intialize velocity + for(j=0;j<STEP;j++) + positions[j]=0; + currentPos = STEP; + + sei(); //Enable interrupts + for(;;) + { + //Update positional encoder + setPos(); + + if(dataAvailable && !logStarted) + { + dataAvailable = 0; //Reset flag + + //Update velocity + vel = pos - positions[(currentPos+1)<STEP ? currentPos+1 : 0]; //Determine velocity + positions[currentPos]=pos; //Update position array + currentPos++; //Update current sample + if(currentPos>=STEP) + currentPos-=STEP; //If overflow, loop around + + if(MODE==1||MODE==3)PI_V(vel); //Velocity or cascade control + if(MODE==3)PI_C(sendData[0], u_V<<6); //Cascade control, voltage is put in fixed point arithmetic + if(MODE==2)PI_C(sendData[0], U); //Velocity control + } + + while(logStarted==1 && dataAvailable==1) + { + cli(); // Enter critical section: disable interrupts + + for(i=0;i<NBR_CHAN;i++) + { + data[i] = (unsigned int)sendData[i]; //Store data locally + } + //Update velocity + vel = pos - positions[(currentPos+1)<STEP ? currentPos+1 : 0]; //Determine velocity + positions[currentPos]=pos; //Update position array + currentPos++; //Update current sample + if(currentPos>=STEP) + currentPos-=STEP; //If overflow, loop around + + data[2] = (unsigned int)vel; //Parameter that can be sent through SerialLogger + + if (nbrSampSinceLastSend>1) + data[0] = 0; //Check if sample was missed + + dataAvailable = 0; //Reset flag + nbrSampSinceLastSend = 0; + sei(); // Exit critical section: enable interrupts + + if(MODE==1||MODE==3)PI_V(vel); //Velocity or cascade control + if(MODE==3)PI_C(sendData[0], u_V<<6); //Cascade control, voltage is put in fixed point arithmetic + if(MODE==2)PI_C(sendData[0], U); //Velocity control + + // Transmit data + for(i=0;i<NBR_CHAN;i++) + { + putchar((unsigned char)((data[i+2]&0xff00)>>8)); //Low byte + putchar((unsigned char)((data[i+2]&0x00ff))); //High byte + } + } + } +}