/* *------------------------------Boost pressure PI controller-------------------------------- * *Digital project Carl Wilhelmsson, e99cwi. *Part of PhD studdies LTH, Lund, Sweden * *20050304 * *Ver 1.0 * *------------------------------------------------------------------------------------------ */ /*____________________#def section____________________*/ #include #include #include #include //Display definitions #define LCDen 0x08; #define LCDRS 0x02; #define LCDRW 0x04; #define LCDRSen 0x0A; #define LCDRWen 0x0C; //AD definitions #define ADRefBits 0x40;//Set to AVCC with capasitor at AREF //Adress space of display static char screenPos[2][16] = {{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F}, {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F}}; static char LCDChangeVal = 0; static int timeScaler;//Prescaler int used to controll the dT of ignition counter static int scalerLimit;//Should be exchanged for a #def, res is in iniziation routine static char revCNT;//latest read number of counted ignitions static char revOFFlag;//Flag used to indicate overrev of the measurment device static int revDT = 1;//Counter delta time, used to calculate engine speedx //AD convertion variables static char ADnbrToMean = 0;//Number of convurtins currently included in average avlue static unsigned int ADChanel[8] = {0,0,0,0,0,0,0,0};//Current chanel status static unsigned int ADTempSum = 0;//Current AD chanel sample sum /*____________________PID variables____________________*/ //Discretization parameters static signed int PIK = 1;//K (propotional gain) static signed int PIBi = 2;//Bi=k*h/Ti (integral step gain) static signed int PIAr = 1;//Ar=h/Tt (windup protection gain) //Limitations static signed int PIUHigh;//Upper acctuator limitation static signed int PIULow;//Lower acctuator output limitation //State of controller, PI cotrolelr specific variables, plese refer to: //IFAC Computer control: an overview Wittenmark, Åström, Årzen static signed long int PIStateI; static signed long int PIUOld; static signed long int PIVOld; static signed long int PIUc; static signed long int PIUcOld; static signed long int PIY; static signed long int PIYOld; static signed long int PINormOut; static signed long int PIOn; static signed long int PIOut; static char PIUpdateFlag = 0;//0= no uppdate, 1 PI nr one......... /*____________________LCD handeling routines____________________*/ /* Halts the circut a loong time, used for iniziation of LCD and for user interaction */ void LCDwait(void){ unsigned int k; for(k=0; k<65530; k++);//Wait a looong time return; } /* Reads the LCD state responds with one byte. bit 8 - busy flag "BF" bit 7-0 - Adress counter "AC" */ char LCDState(void){ DDRB = 0x0E;//Set port direction to input for relevant bits //Read first nibble PORTB = LCDRW;//Set R/W to high to read bits PORTB = LCDRWen;//Enable positive flank PORTB = LCDRWen;//Latching issues PORTB = LCDRWen;//Latching issues char nibbOne = PINB & 0xF0;//Read port and mask interesting bits PORTB = LCDRW;//Enable negative flank //Read second nibble PORTB = LCDRWen;//Enable positive flank PORTB = LCDRWen;//Latching issues PORTB = LCDRWen;//Latching issues PORTB = LCDRWen;//Latching issues char nibbTwo = PINB >> 4;//Shift LBS nibble to correcto position PORTB = LCDRW;//Enable negative flank PORTB = 0x00;//Zero output port DDRB = 0xFE;//Set back data direction to output return (nibbOne | nibbTwo );//Put MSB and LSB nibble together } /* Checks if LCD are ready for new data transaction */ char LCDBusy(void){ return LCDState() & 0x80;//Check if BS in Busy flag adres counter read is set } /* Sends a command to LCD display */ void LCDCommand(const char command){ while(LCDBusy()); //Wait for display ready //Output MSB nibble char nibble = command & 0xF0;//Mask the first MSB nibble of the command PORTB = nibble | LCDen;//Send first nibble, output enable PORTB = nibble;//Switch enable off //Output LSB nibble, as abowe nibble = (command & 0x0F) << 4;//Mask the LSB nibbe, shift left PORTB = nibble | LCDen;//Output the LSB nibble and enable PORTB = nibble;//Switch enable off PORTB = 0x00;//Clear the port return; } /* Displays a char on LCD, position given by cursor position */ int LCDChar(char letter){ while(LCDBusy());//Wait for display ready //Output MSB nibble, RS set before enable char nibble = letter & 0xF0;//Mask MSB nibble PORTB = nibble | LCDRS;//Set RS before enable PORTB = nibble | LCDRSen;//Set enable (pos flank) PORTB = nibble | LCDRS;//Remove enable (neg flank) //LSB nibble, same procedure nibble = (letter & 0x0F) << 4;//Mask and shift LSB nibble PORTB = nibble | LCDRS; PORTB = nibble | LCDRSen; PORTB = nibble | LCDRS; PORTB = 0x00;//Clear the port return 0; } /* Puts the cursor in first position */ void LCDHome(void){ LCDCommand(0x02); return; } /* Moves the cursor to position specified by x,y */ void LCDJumpTo(char row, char pos){ LCDCommand(0x80 | screenPos[(int)row-1][(int)pos-1]); return; } /* Cleers the LCD screen */ void LCDClearScr(void){ LCDCommand(0x01); return; } /* Programs the LCD special characters with aplication specific characters */ void LCDCharProg(void){ return; } /* Display initiation routine */ void LCDInit(void){ DDRB = 0xFE; //Sets pin 1-7 to ouput pins, pin0 might be used for external trigg of counter 0 //Reset number one LCDwait();//Skall vara 20 PORTB = 0x30 | LCDen; PORTB = 0x30; //Reset number two LCDwait();//Skall vara 20 PORTB = 0x30 | LCDen; PORTB = 0x30; //Reset number three LCDwait();//Skall vara 20 PORTB = 0x30 | LCDen; PORTB = 0x30; // I, Function set 4-bit PORTB = 0x20 | LCDen; PORTB = 0x20; // II, Function set, 0 0 1 DL N 0 * * // DL = interface data length (0 for 4 bit) // N = Single/dual line, 0/1 // => 0010 , 1000 LCDCommand(0x28); // III, Display on/off, 0000 1 D C B //D = Display activation //C = Cursor //B = Blinking of char @ cursor poss //=> 0000 , 1000 LCDCommand(0x08); // IV, Display clear, 0000 0001 LCDClearScr(); // V, Entry mode set, 0000 01 I/D S //I/D = 1:Increment, 0:Decrement //S = display shift on/off //=> 0000, 01 1 0 LCDCommand(0x06); LCDCommand(0x0C);//Switch display on (as in III, cursor, blinking, on) fdevopen(LCDChar);//Open fout stream return;//Display should be ready for usage! } /*____________________Counter related routines____________________*/ /* Iniziation routine used to meassure the engine speed */ void TCRevCNTInit(void){ //Timer counter control register, timer0 //FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00 //FOC0 = Force output compare, will forse a PWM acction, ot used //WGM00, WGM01 = Usen in waweform generation mode, not used //COM00, COM01 = Compare match output mode, nmot used //CS02-CS00 = Clock select, set to 111 for externa clock positive flank trig TCCR0 = 0x07;//Iniziate T/C 0 to operate on external clock, positive flank //Timer counter control register, timer 2 //FOC2 WGM20 COM21 COM20 WGM21 CS22 CS21 CS20 //FO20 = Force output compare, will forse a PWM acction, ot used //WGM20, WGM21 = Usen in waweform generation mode, not used //COM20, COM21 = Compare match output mode, nmot used //CS22-CS20 = Clock select, set to 111 for externa clock positive flank trig TCCR2 = 0x07;//Iniziate T/C2 to operate on internal clock 1024 in prescaler timeScaler = 0; scalerLimit = 254;//Prescaler for timer set to aquire 1.024s (using 1024 hardware prescaling revCNT = 0x00 ; revOFFlag = 0;//Zero the oveflow flag of the rev counter TCNT0 = 0;//Zero ignition counter TCNT2 = 0;//Zero realtime counter return; } /* Interrupt handler for T/C 2 overflow */ SIGNAL(SIG_OVERFLOW2){ if(timeScaler >= 1){//If enougt time has elapsed for the ignition counter to be checked revCNT = TCNT0;//Store away the number of counted cycles for display TCNT0 = 0; timeScaler = 0;//Zero the ignition counter DT }else{//Else, only increase the prescaler timeScaler++; } } /* Interrupt handler T/C 0 overflow */ SIGNAL(SIG_OVERFLOW0){ revOFFlag = 1; } /* Iniziate the timer 16bit timer/counte for PWM usage */ void TCPWMInit(void){ DDRD = 0x20;//Data direction to output TCCR1A = 0x82;//Timer control register to aplication specific settings TCCR1B = 0x11; ICR1 = 0x3FF;//Limits the top value to 10 bit operation return; } /*____________________A/D converter routines____________________*/ /* Iniziate the A/D converter for this aplication */ void ADInit(void){ ADMUX = ADRefBits;//Iniziate A/D ref source ADCSRA = 0xC0;//Enable AD converter and start convertion nr 1 while(!(ADCSRA & 0x10));//wait untill firs convertion is finished char temp = ADCL; temp = temp | (ADCH << 8);//Get the result ADCSRA = 0xDD;//Keep AD enables and start a new convertion, clear interrupt flag and enable interrupts, ADnbrToMean = 0;//Current number of convertions included in mean value return; } /* Interrupt handler attached to AD convertion complete interrupt Periodicaly samples and makes average value on 8 AD single ended channels PI controller scan be attached to each of these channels and the calcualtion of the PI output should be performed here then */ SIGNAL(SIG_ADC){ if(ADnbrToMean < 15){ int temp = ADCL;//Read first two bit:s of the 10 bits in ADResult temp = temp | (ADCH << 8);//Shift result 8 bit ADTempSum += temp >> 4;//Make a mean average of 16 samples by dividing each sample with 16 and adding it to the sum ADCSRA |= _BV(ADSC);//Start new convertion ADnbrToMean++;//Number of included samples inte the average value }else{ int chanel = ADMUX & 0x1F;//Get current chanel ADMUX = 0x40 | ((chanel+1)%8);//Change the chanel cyclic between the 8 diffrent average value chanels //Get last sample and stor away the average value in the chanel vector int temp = ADCL; temp = temp | (ADCH << 8); ADChanel[chanel] = (temp >> 4) + ADTempSum; if(chanel == 1){//PI controller input chanel, calculate PI response if(ADChanel[0] > PIOn){ //Controller abovo on limit, calculate new responce PIUcOld = PIUc; PIY = ADChanel[0]; PIVOld = PIK*(PIUc - PIY) + PIStateI;//New PI output //I-part antiwindup protection if(PIVOld > PIUHigh){ PIUOld = PIUHigh; }else if( PIVOld < PIULow){ PIUOld = PIULow; }else{ PIUOld = PIVOld; } //Controller output PIOut = (PINormOut - PIUOld); OCR1A = PIOut; PIUpdateFlag = 1; }else{ //Controller swiched off, set everyting to zero PIOut = 0; OCR1A = PIOut; PIStateI = 512; PIUOld = 0; PIVOld = 0; PIUcOld = 0; } } //Start new convertion, zero the average value counters ADCSRA |= _BV(ADSC); ADnbrToMean = 0; ADTempSum = 0; } } /*____________________PI controller code____________________*/ /* Iniziation of PI controller */ void PIInit(void){ PIStateI = 512;//State of I part, set to avoit inizial transients (with respect to PINormOut) PIUHigh = 512;//Windup protection high limit (note NOT 512, counterr will then never toggle!!) PIULow = -511;//Windup protection low limit low limit PINormOut = 512;//The controller "zero output" PIUc = 700;//Controller inizial setpoint PIOut = 0;//Controller off output PIK = 1;//K (propotional gain) PIBi = 2;//Bi=k*h/Ti (integral step gain) PIAr = 1;//Ar=h/Tt (windup protection gain) PIOn = 512;//Controller on limit return; } /* Update integral state */ void PIUpdateState(void){ PIStateI = PIStateI + (PIUcOld-PIY)/PIBi + (PIUOld - PIVOld)/PIAr; PIYOld = PIY; return; } /*____________________Interrupt Enable and general interrupt routines____________________*/ /* Iniziate interrupts, global and local */ void InterrInit(void){ TIMSK = 0x41;//enable T/C 0 and 2 overflow interrupt sei();//Enables global interrupts } /*____________________Main routine____________________*/ int main(void){ //Run inziation routines LCDInit(); TCRevCNTInit(); InterrInit(); ADInit(); TCPWMInit(); PIInit(); //Iniziation of swiches int sw = 0; DDRC = 0x03;//Set Data direction on port C to output (just to be sure) PORTC = 0x03;//Enable internal pullup resistors on pin0-1 port C for(;;){//Loop until hell freezes over //Update state of controller (if new samples available) if(PIUpdateFlag == 1){ PIUpdateState(); PIUpdateFlag = 0; } //Output current values to the display LCDJumpTo(1,1); printf("%drpm %db ",(revCNT*60)/revDT,ADChanel[0]); LCDJumpTo(2,1); printf("%ldb, %ldb, %ld%% ",PIUc,PIOn,(PIOut*100/1023)); //Handle the two button interface sw = PINC & 0x03;//Read the current switch values if(!sw){//Change the current value if(LCDChangeVal == 0) LCDChangeVal = 1; else LCDChangeVal = 0; LCDwait(); LCDwait(); LCDwait(); sw = 0; }else if(!(sw & 1)){//Increse the current value if(LCDChangeVal == 0) PIUc++; else PIOn++; LCDwait(); sw = 0; }else if(!(sw & 2)){//Decrese the current value if(LCDChangeVal == 0) PIUc--; else PIOn--; LCDwait(); sw = 0; } } return 0; }