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