Program Listing
Cornell University EE
476 Final Project
Fertilizer Feed Rate Controller
Warren Scott
Written using CodeVision AVR C compiler
/*
Fertilizer Feed Rate Controller
EE 476 Final Project
Warren Scott wjs16@cornell.edu
Written in CodeVision AVR C compiler
for the Atmel AT90S8515 Microcontroller
LCD is hooked up to Port A
LED's on port B flash for debugging purposes, but do not need
to be hooked up
Matrix style Keypad to Port C
PORTD.2 is shaft input 1
PORTD.3 is shaft input 2
PORTD.4 is PWM out 1
PORTD.5 is PWM out 2
ICP (pin31) is radar unit input
shaft input2/PWM2 has no control procedure yet, but it could
easily be implemented.
*/
#include <90s8515.h>
// set up LCD to be port A
#asm
.equ __lcd_port=0x1B
#endasm
#include <lcd.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
// Definitions
#define T0reload 0x01
#define T0prescale 0x05
#define T1prescale 0x01
#define HZperMPH 17.034
#define KpropStart 0.0
#define KintStart 0.0
#define KderivStart 0.0
#define Maxinterror 500
#define Mininterror -500
#define Maxderiverror 75
#define Minderiverror -75
// HZperMPH is the frequency the radar unit puts out
// factory set to 58.94, 44.21, 27.64, or 17.034 hz/mph
// equivalent to 34.80, 26.11, 16.32, or 10.06 hz/kph
// keypad stuff
#define key_released 1
#define key_wait_pressed 2
#define key_pressed 3
#define key_wait_released 4
// keypad lookup flash tables
flash unsigned char keytable[18]={0xFF, 0xEE, 0xED, 0xEB, 0xE7, 0xDE,
0xDD, 0xDB, 0xD7, 0xBE, 0xBD, 0xBB, 0xB7, 0x7E, 0x7D, 0x7B, 0x77, 0x00};
flash unsigned char keyvalue[18]={'N', '1', '4', '7', '*', '2', '5',
'8', '0', '3', '6', '9', '#', 'A', 'B', 'C', 'D', 'M'};
// global variables
// think about setting up bit variables for things like flag, update
rpm/speed,
unsigned char keypad_state;
unsigned char buttonpressed;
unsigned char flag; // flag for state machine
unsigned int overflows_shaft1, overflows_shaft2, overflows_radar;
unsigned char pwm1val, pwm2val; // are the pulse width
modulation motor controls high or low
unsigned char i; //counter
unsigned char keypad_button;
// these we don't care about being in registers, so we'll put them later
unsigned int startcount_shaft1, startcount_shaft2, startcount_radar;
unsigned int endcount_shaft1, endcount_shaft2, endcount_radar;
unsigned int pwm1_dutyhigh, pwm2_dutyhigh; // 65535 = 100% on
int int_pwm1_dutyhigh, int_pwm2_dutyhigh;
unsigned long shaft1counts, shaft2counts, radarcounts;
unsigned int rpm1, rpm2, lastrpm1, lastrpm2; // these may need to be
floats
unsigned int rpm1error_hist[10];
unsigned int desired_rpm1, desired_rpm2; // these may need to be
floats
int rpm1error;
int PWM1_change;
float mph;
unsigned char lcdline1[17], lcdline2[17], speedstring[12];
unsigned char kpstr[6], kistr[6], kdstr[6];
float Kprop, Kint, Kderiv;
// forward function definitions
void initialize(void);
void checkzeros(void);
void reset_pwms(void);
void control_1(void);
unsigned char get_key(void);
unsigned char check_keypad(void);
//------------------------------------------------------------------------------------------------
//******************************** I N T E R R U P T S *****************************************
//------------------------------------------------------------------------------------------------
interrupt [EXT_INT0] void external_interrupt0(void)
{
// for interrupt of shaft 1
// connected to pin D 2
PORTB.1=!PORTB.1;
endcount_shaft1=TCNT1;
shaft1counts=(endcount_shaft1 + overflows_shaft1*65536) - startcount_shaft1;
overflows_shaft1=0;
startcount_shaft1=endcount_shaft1;
rpm1=(4000000*60)/shaft1counts;
} // end external interrupt 0
//------------------------------------------------------------------------------------------------
interrupt [EXT_INT1] void external_interrupt1(void)
{
// for interrupt of shaft 2
// connected to pin D 3
PORTB.2=!PORTB.2;
endcount_shaft2=TCNT1;
shaft2counts=(endcount_shaft2 + overflows_shaft2*65536) - startcount_shaft2;
overflows_shaft2=0;
startcount_shaft2=endcount_shaft2;
lastrpm2=rpm2;
rpm2=(4000000*60)/shaft2counts;
} // end external interrupt 0
//------------------------------------------------------------------------------------------------
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_overflow(void)
{
TCNT0=T0reload;
flag=0xff;
} // end timer 0 overflow interrupt
//------------------------------------------------------------------------------------------------
// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_overflow(void)
{
// timer 1 has overflowed, so increment the overflow counter for
all three timers
overflows_shaft1++;
overflows_shaft2++;
overflows_radar++;
// set pwm outputs high
PORTD.4=1;
PORTD.5=1;
} // end timer1 overflow interrupt -- incrementing overflow counters
//------------------------------------------------------------------------------------------------
// Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_ext_capture(void)
{
// this will capture the timer1 value,
// then calculate the total number of clock cycles used
PORTB.0=!PORTB.0;
endcount_radar=ICR1; // read capture register
// for interrupt of radar
radarcounts=(endcount_radar + overflows_radar*65536) - startcount_radar;
overflows_radar=0;
startcount_radar=endcount_radar;
mph=(4000000.0/((float)(radarcounts)))/(HZperMPH);
} // end timer1 external capture interrupt
//------------------------------------------------------------------------------------------------
// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compA(void)
{
PORTD.4=0;
} // end timer1 compare A interrupt -- PWM 1 stuff
//------------------------------------------------------------------------------------------------
// Timer 1 output compare B interrupt service routine
interrupt [TIM1_COMPB] void timer1_compB(void)
{
PORTD.5=0;
} // end timer1 compare B interrupt -- PWM 2 stuff
//------------------------------------------------------------------------------------------------
//************************************* M A I N ************************************************
//------------------------------------------------------------------------------------------------
void main(void)
{
//main procedure
initialize();
while(1)
{
// do loop
if (flag>0)
{
flag=0;
PORTB.7=!PORTB.7;
// make sure we are not sitting at zero rpm / speed
checkzeros();
// if (PIND.7==0) desired_rpm1++;
// if (PIND.6==0) desired_rpm1--;
desired_rpm1=(unsigned int)(mph*100);
// write stuff to lcd display
//mph=(4000000.0/((float)(radarcounts)))/(HZperMPH);
//rpm1=(4000000*60)/shaft1counts;
//rpm2=(4000000*60)/shaft2counts;
sprintf(lcdline1, "%1dR%1dD ",rpm1,
desired_rpm1);
sprintf(lcdline2,"%1uPWM, %1d ERR ",pwm1_dutyhigh,rpm1error);
//ftoa(mph, 4, speedstring);
//sprintf(lcdline2, "pwm1= %d ",pwm1_dutyhigh);
//sprintf(lcdline2, "%s MPH ",speedstring);
lcd_gotoxy(0,0);
lcd_puts(lcdline1);
lcd_gotoxy(0,1);
lcd_puts(lcdline2);
sprintf(lcdline1,"%1d Change ",PWM1_change);
ftoa(Kprop,1,kpstr);
ftoa(Kint,2,kistr);
ftoa(Kderiv,2,kdstr);
sprintf(lcdline2,"%sP%sI%sD",kpstr,kistr,kdstr);
lcd_gotoxy(0,2);
lcd_puts(lcdline1);
lcd_gotoxy(0,3);
lcd_puts(lcdline2);
keypad_button=check_keypad();
if (keypad_button=='N') PORTB.2=1;
if (keypad_button!='N') PORTB.2=0;
if (keypad_button=='1') Kprop=Kprop+0.1;
if (keypad_button=='4') Kprop=Kprop-0.1;
if (keypad_button=='2') Kint=Kint+0.01;
if (keypad_button=='5') Kint=Kint-0.01;
if (keypad_button=='3') Kderiv=Kderiv+0.01;
if (keypad_button=='6') Kderiv=Kderiv-0.01;
control_1();
}// end while flag>0
}
} // end void main(void)
//------------------------------------------------------------------------------------------------
//******************************** P R O C E D U R E S *****************************************
//------------------------------------------------------------------------------------------------
void checkzeros(void)
{
// this will check for the shaft and radar producing no input
// ie the shaft is not rotating, or the tractor is not moving at all
if (overflows_shaft1>183) // more than 3 seconds without a
shaft rotation
{
rpm1=0;
}
if (overflows_shaft2>183) // more than 3 seconds without a
shaft rotation
{
rpm2=0;
}
if (overflows_radar>183) // more than 3 seconds without a
radar input
{
mph=0;
}
return;
}
//------------------------------------------------------------------------------------------------
void reset_pwms(void)
// this procedure will take the duty cycle values and reset the pwm
high and low counts.
{
OCR1A=pwm1_dutyhigh;
OCR1B=pwm2_dutyhigh;
return;
}
//------------------------------------------------------------------------------------------------
void control_1(void)
// this will take change the PWM values according to the control parameters
{
int rpmerror_sum;
int deriv_error;
rpm1error= desired_rpm1-rpm1;
rpmerror_sum=0;
for(i=0; i<9; i++)
{
rpm1error_hist[i]=rpm1error_hist[i+1];
rpmerror_sum=rpmerror_sum+rpm1error_hist[i];
}
rpm1error_hist[9]=rpm1error;
rpmerror_sum=rpmerror_sum+rpm1error;
deriv_error=lastrpm1-rpm1;
// limit the values of integral error part
if (rpmerror_sum>Maxinterror) rpmerror_sum=Maxinterror;
if (rpmerror_sum<Mininterror) rpmerror_sum=Mininterror;
// limit the values of the derivitive part
if (deriv_error>Maxderiverror) deriv_error=Maxderiverror;
if (deriv_error<Minderiverror) deriv_error=Minderiverror;
// PWM_change=(int)(Kprop*error + Kint*errorsum +
Kderiv*(lastrpm1-rpm1));
PWM1_change=(int)(Kprop*rpm1error +Kint*rpmerror_sum +
Kderiv*(lastrpm1-rpm1));
// put in code that limits the value of PWM1_change
if (PWM1_change<=0)
{
if ((int_pwm1_dutyhigh+PWM1_change)<10)
int_pwm1_dutyhigh=10;
else int_pwm1_dutyhigh= int_pwm1_dutyhigh+PWM1_change;
}
if (PWM1_change>0)
{
if ((32760-int_pwm1_dutyhigh)<PWM1_change) int_pwm1_dutyhigh=32760;
else int_pwm1_dutyhigh= int_pwm1_dutyhigh+PWM1_change;
}
pwm1_dutyhigh=int_pwm1_dutyhigh;
pwm1_dutyhigh=pwm1_dutyhigh*2;
lastrpm1=rpm1;
reset_pwms();
return;
}
//------------------------------------------------------------------------------------------------
unsigned char get_key(void)
{
unsigned char key;
unsigned char buttonnum;
// get lower nibble of keypad
DDRC=0x0F;
PORTC=0xF0;
// we are not sure how the delay is implemented for charging
stray capacitance, so we'll do our own
#asm
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
#endasm
// delay_us(5);
key=PINC;
// get upper nibble of keypad
DDRC=0xF0;
PORTC=0x0F;
// we are not sure how the delay is implemented for charging
stray capacitance, so we'll do our own
#asm
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
#endasm
//delay_us(5);
key=key|PINC;
for (buttonnum=0; buttonnum<18; buttonnum++)
{
if (keytable[buttonnum]==key) break;
}
key=keyvalue[buttonnum];
return(key);
} // end unsigned char getkey(void)
//-------------------------------------------------------------------------------------------
unsigned char check_keypad(void)
{
unsigned char temppressed;
unsigned char sendback;
sendback='N';
temppressed=get_key();
if (keypad_state==key_released)
{
if ((temppressed !='M') && (temppressed != 'N'))
{
buttonpressed=temppressed;
keypad_state=key_wait_pressed;
}
} // end keypad_state== key_released
if (keypad_state==key_wait_pressed)
{
if (temppressed==buttonpressed)
{
keypad_state=key_pressed;
sendback=buttonpressed;
}
else
{
keypad_state=key_released;
buttonpressed='N';
}
} // end keypadstate== key_waitpressed
if (keypad_state==key_pressed)
{
if (temppressed=='N')
{
buttonpressed=temppressed;
keypad_state=key_wait_released;
}
} // end keypad_state==key_pressed
if (keypad_state==key_wait_released)
{
if (temppressed=='N')
{
keypad_state=key_released;
}
else keypad_state=key_pressed;
} // end keypad_state== key_waitreleased
return(sendback);
} // end void check_keypad(void)
//------------------------------------------------------------------------------------------------
//**************************** I N I T I A L I Z A T I O N *************************************
//------------------------------------------------------------------------------------------------
void initialize(void)
{
// Port A is output for LCD
DDRA=0xFF;
PORTA=0x00;
// Port B is some input, some output
DDRB=0xFF;
PORTB=0x00;
// Port C is input for nothing yet
DDRC=0x00;
PORTC=0x00;
// Port D is input for nothing yet
// Port D pins 2 and 3 are the external interrupt pins
DDRD=0x30; // port D 4,5 output for PWM control, all
others input
PORTD=0xC0; // pullups on D 6,7 for increase
rpm/decrese rpm circuit
// DDRD=0x00;
// PORTD=0x00;
// Timer/Counter 0 initialization
TCNT0=T0reload; // set initial value
TCCR0=T0prescale; // set prescale
// Timer/Counter 1 initialization
TCCR1A=0x00;
TCCR1B=0xC1;
// initial value of timer1
TCNT1H=0x00;
TCNT1L=0x00;
// Compare A match value
OCR1AH=0x0F;
OCR1AL=0xFF;
// Compare B match value
OCR1BH=0xF0;
OCR1BL=0x00;
// External Interrupt(s) initialization
// INT0: on -- GIMSK bit 6
// INT1: on -- GIMSK bit 7
GIMSK=0xC0;
// MCUCR = 0x0F for trigger on rising edge, 0x0A for trigger on
falling edge
MCUCR=0x0A;
GIFR=0xC0; // needed for external interrupts
// Timer(s)/Counter(s) Interrupt(s) initialization
// Interrupt on timer1 overflow, compare A, Compare B, input
capture and t0 overflow is 0xEA
TIMSK=0xEA;
// Interrupt on timer1 overflow, input capture and t0 overflow
(no compare A or B) us 0x8A
//TIMSK=0x8A;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
// initialize all of the variables that are used in the program
for (i=0; i<17; lcdline1[i++]=0x00);
for (i=0; i<17; lcdline2[i++]=0x00);
for (i=0; i<12; speedstring[i++]=0x00);
for (i=0; i<6; kpstr[i++]=0x00);
for (i=0; i<6; kistr[i++]=0x00);
for (i=0; i<6; kdstr[i++]=0x00);
for (i=0; i<10; rpm1error_hist[i++]=0);
flag=0x00;
overflows_shaft1=0;
overflows_shaft2=0;
overflows_radar=0;
pwm1val=0;
pwm2val=0;
i=0;
startcount_shaft1=0;
startcount_shaft2=0;
startcount_radar=0;
endcount_shaft1=0;
endcount_shaft2=0;
endcount_radar=0;
pwm1_dutyhigh=32767;
pwm2_dutyhigh=32767;
int_pwm1_dutyhigh=16383;
int_pwm2_dutyhigh=16383;
reset_pwms();
shaft1counts=0;
shaft2counts=0;
radarcounts=0;
rpm1=0;
rpm2=0;
desired_rpm1=500;
desired_rpm2=500;
mph=0.0;
Kprop=KpropStart;
Kint=KintStart;
Kderiv=KderivStart;
keypad_state=key_released;
// LCD module initialization
lcd_init(16);
lcd_clear();
lcd_gotoxy(0,0);
lcd_putsf("initialize");
// Global enable interrupts
#asm("sei")
PORTB=0xFF;
} // end void initialize(void)
Go Home