/* avr-gcc -mmcu=atmega8 -O -g -Wall -o ballbeamctrl_a ballbeamctrl.c avr-objcopy -Osrec ballbeamctrl_a ballbeamctrl_a.sr uisp -dprog=stk200 --erase --upload if=ballbeamctrl_a.sr avr-objdump -S ballbeamctrl_a > simcom -115200 /dev/ttyS0 */ #include "trtkernel.c" /********************* BALL AND BEAM APPLICATION ****************************/ // Controller parameters (fixed-point with 11 fractional bits) #define ad 1418 #define bd -3547 #define ai -3 #define K_outer -512 #define K_inner 2 // Global variables used for inter-task communication // (protected by semaphores) int u1 = 0; int u2 = 0; int angleRef1 = 0; int angleRef2 = 0; int ref = 0; // Data for PWM tasks struct PWMdata { uint32_t t; uint8_t channel; uint8_t sem; int *uPtr; }; // Data for inner controller tasks struct ICdata { uint32_t t; uint32_t d; uint8_t ang_channel; uint8_t low; uint8_t high; uint16_t value; int angle; uint8_t sem1; uint8_t sem2; int u; int *angleRefPtr; int *uPtr; }; // Data for outer controller tasks struct OCdata { uint32_t t; uint32_t d; uint8_t pos_channel; uint8_t low; uint8_t high; uint8_t value; int pos; uint8_t sem; int *refPtr; int *angleRefPtr; int P; int I; int D; int yold; }; /** * Interrupt handler for receiving characters * on the serial connection */ SIGNAL(SIG_UART_RECV){ char ch = inp(UDR); if (ch == 32) { ref = -ref; } if (ch == 's') { ref = 6554; /* 4 V */ } } /** * Interrupt handler for data transmission, * called when send buffer empty to * repeatedly send info on running task. */ SIGNAL(SIG_UART_DATA){ outp('0'+kernel.running, UDR); } /** * Generic code function for the PWM tasks */ void PWMtask(void* args) { struct PWMdata* d = (struct PWMdata*) args; d->t = trtGetRelease(); while (1) { PORTB |= d->channel; // AnalogOut +10 volt trtWait(d->sem); // Semaphore protecting u (global) d->t += ((uint16_t)(*(d->uPtr) + 512)) >> 3; trtSignal(d->sem); trtSleepUntil(d->t, 0); PORTB &= ~(d->channel); // AnalogOut -10 volt trtWait(d->sem); // Semaphore protecting u (global) d->t += 128 - (((uint16_t) (*(d->uPtr) + 512)) >> 3); trtSignal(d->sem); trtSleepUntil(d->t, 0); } } /** * Generic code function for the inner controller tasks */ void InnerCtrl(void* args) { struct ICdata* d = (struct ICdata*) args; d->t = trtGetRelease(); d->d = trtGetDeadline(); while(1) { // AnalogIn angle ADMUX = d->ang_channel; // Read angle sensor ADCSRA |= 0x40; // start conversion while (ADCSRA & 0x40); // wait for conversion to finish d->low = ADCL; // read value d->high = ADCH; d->value = (d->high<<8) | (d->low); d->angle = (d->value - 512) << 5; trtWait(d->sem1); // Semaphore protecting angleRef (global) d->u = (-K_inner*(*(d->angleRefPtr) - d->angle)) >> 5; trtSignal(d->sem1); trtWait(d->sem2); // Semaphore protecting u (global) *(d->uPtr) = d->u; // limit control if (d->u > 511) { *(d->uPtr) = 511; } else if (d->u < -512) { *(d->uPtr) = -512; } trtSignal(d->sem2); // Sleep d->t += SECONDS2TICKS(0.02); d->d += SECONDS2TICKS(0.02); trtSleepUntil(d->t, d->d); } } /** * Generic code function for the outer controller tasks */ void OuterCtrl(void* args) { struct OCdata* d = (struct OCdata*) args; d->t = trtGetRelease(); d->d = trtGetDeadline(); while(1) { // AnalogIn position ADMUX = d->pos_channel; // Read position sensor ADCSRA |= 0x40; // start conversion while (ADCSRA & 0x40); // wait for conversion to finish d->low = ADCL; // read value d->high = ADCH; d->value = (d->high<<8) | (d->low); d->pos = (d->value - 512) << 5; // Calculate Output d->D = ((long)ad*(long)(d->D) - (long)bd*(long)(d->pos - d->yold)) >> 11; d->P = ((long)K_outer*(long)(*(d->refPtr)-d->pos)) >> 11; trtWait(d->sem); // Semaphore protecting angleRef (global) (*d->angleRefPtr) = d->P + d->I + d->D; trtSignal(d->sem); d->I = d->I + (int)(((long)ai*(long)(*(d->refPtr) - d->pos)) >> 11); d->yold = d->pos; // Sleep d->t += SECONDS2TICKS(0.04); d->d += SECONDS2TICKS(0.04); trtSleepUntil(d->t, d->d); } } /* Data structures for the individual tasks */ struct PWMdata pd[2]; struct ICdata id[2]; struct OCdata od[2]; /** * Main Program. */ int main(void) { uint8_t i; DDRB = 0xff; // Bit 0,1,3-5 output DDRC = 0x30; // Bit 4-5 output ADCSRA = 0xc7; // ADC enable (Rolf's error/fault/failure) /* Configure serial communication */ outp(0x00, UCSRA); // USART: outp(0xa8, UCSRB); // USART: RXC enable, UDRE enable, Receiver enable, Transmitter enable // outp(0xa8, UCSRB); // UDRE disable, no monitoring outp(0x86, UCSRC); // USART: 8bit, no parity outp(0x00, UBRRH); // USART: 38400 @ 14.7456MHz outp(7, UBRRL); // USART: 38400 @ 14.7456MHz, table p. 157 // Initialize task data pd[0].channel = 0x02; // analogout channel 1 pd[0].sem = 1; // semaphore protecting u1 pd[0].uPtr = &u1; // pointer to global variable pd[1].channel = 0x04; // analogout channel 2 pd[1].sem = 2; // semaphore protecting u2 pd[1].uPtr = &u2; // pointer to global variable id[0].ang_channel = 0xc2; // ADMUX value for analogin channel id[0].sem1 = 3; // semaphore protecting angleRef1 id[0].sem2 = 1; // semaphore protecting u1 id[0].uPtr = &u1; // pointer to global variable id[0].angleRefPtr = &angleRef1; // pointer to global variable id[1].ang_channel = 0xc0; // ADMUX value for analogin channel id[1].sem1 = 4; // semaphore protecting angleRef2 id[1].sem2 = 2; // semaphore protecting u2 id[1].uPtr = &u2; // pointer to global variable id[1].angleRefPtr = &angleRef2; // pointer to global variable od[0].pos_channel = 0xc3; // ADMUX value for analogin channel od[0].sem = 3; // semaphore protecting angleRef1 od[0].refPtr = &ref; // pointer to global variable od[0].angleRefPtr = &angleRef1; // pointer to global variable od[0].I = 0; od[0].D = 0; od[0].yold = 0; od[1].pos_channel = 0xc1; // ADMUX value for analogin channel od[1].sem = 4; // semaphore protecting angleRef2 od[1].refPtr = &ref; // pointer to global variable od[1].angleRefPtr = &angleRef2; // pointer to global variable od[1].I = 0; od[1].D = 0; od[1].yold = 0; trtInitKernel(80); // Create Semaphores (must be done before creating the tasks that use them) for (i=1; i<=4; i++) { trtCreateSemaphore(i, 1); } trtCreateTask(PWMtask, 100, SECONDS2TICKS(0.0), SECONDS2TICKS(0), &(pd[0])); trtCreateTask(PWMtask, 100, SECONDS2TICKS(0.0), SECONDS2TICKS(0), &(pd[1])); trtCreateTask(InnerCtrl, 100, SECONDS2TICKS(0.015), SECONDS2TICKS(0.02), &(id[0])); trtCreateTask(InnerCtrl, 100, SECONDS2TICKS(0.015), SECONDS2TICKS(0.02), &(id[1])); trtCreateTask(OuterCtrl, 150, SECONDS2TICKS(0.005), SECONDS2TICKS(0.04), &(od[0])); trtCreateTask(OuterCtrl, 150, SECONDS2TICKS(0.005), SECONDS2TICKS(0.04), &(od[1])); // Idle task while (1); }