Appendix A: Code Listing#include <90s8515.h> /* speed and direction output pins */ #define speed OCR1B //use PWM for speed control #define drivedir PORTA.0 #define turndir PORTA.1 #define turn PORTA.2 /* direction signal values */ #define fwd 1 #define rev 0 #define dirleft 1 #define dirright 0 /* state definitions */ #define center 0 #define left 1 #define farleft 2 #define right 3 #define farright 4 #define start 5 /* photosensor values */ #define LEDS ~PINC //inverted since "on" is for a black line #define LLL (0x01 & LEDS) == 0x01 //far left sensor #define LL (0x02 & LEDS) == 0x02 //slightly left sensor #define CC (0x04 & LEDS) == 0x04 //center sensor #define RR (0x08 & LEDS) == 0x08 //slightly right sensor #define RRR (0x10 & LEDS) == 0x10 //far right sensor /* other definitions */ #define t0reload 256-62 //1 ms timebase @ 4MHz #define deltaT 10 //state change time unsigned char state; unsigned int watchdog; //stops car if no line is found after time unsigned int farcount; //after backing up, car steers and waits before going forward unsigned int t_statemachine; //task timer void initialize(void); void steer_off(void); void steer_left(void); void steer_right(void); void reset(void); void statemachine(void); void steer_off(void) { turn = 0; } void steer_left(void) { turndir = dirleft; turn = 1; } void steer_right(void) { turndir = dirright; turn = 1; } void reset(void) //turn off steering and stop car; return to start state { steer_off(); speed = 0; state = start; } void statemachine(void) { t_statemachine = deltaT; //reset machine timer if(LEDS == 0x1f) //all LEDS off (car has been picked up in the air) { reset(); } /* We want to reset the watchdog timer to 0 as long as * at least one sensor is on the line. */ if(LEDS != 0) { watchdog = 0; } /* If the watchdog counter reaches 1.5 seconds, the car should be stopped. However, * if we're backing up to find the line again, let it go until 3 seconds have passed. */ if((watchdog++ == 1500/deltaT) && (drivedir != rev)) { reset(); } else if(watchdog == 3000/deltaT) { reset(); } switch(state) { case start: //initial state if(LEDS == 0x04) //do nothing until only center sensor is on { drivedir = fwd; speed = 200; state = center; } break; case center: //center of car is on the line if(LL) //near left sensor on, steer left to correct { steer_left(); state = left; } else if(RR) //near right sensor on, steer right to correct { steer_right(); state = right; } break; case left: if(CC) { steer_off(); //we're back on track, go straight state = center; } else if(LLL) //we're way off track, back up and turn the { //wheels in the opposite direction to correct drivedir = rev; speed = 255; steer_right(); state = farleft; } break; case right: if(CC) { steer_off(); state = center; } if(RRR) { drivedir = rev; speed = 255; steer_left(); state = farright; } break; case farleft: //we're currently backing up to find the line if(LL) //the inner left sensor is back on the line, so { //go forward again & flip the steering steer_left(); drivedir = fwd; state = left; } break; //otherwise, continue backing up case farright: if(RR) { steer_right(); drivedir = fwd; state = right; } break; } } interrupt [TIM0_OVF] void t0(void) //establishes 1msec time base { TCNT0 = t0reload; if(t_statemachine > 0) t_statemachine--; } void main(void) { initialize(); while(1) { if(t_statemachine == 0) statemachine(); //fire off state machine if it's time } } void initialize(void) { DDRA = 0xff; //PORTA is an output DDRC = 0x00; //PORTC is an input PORTC = 0xe0; //internal pullups off, except for the top 3 unused bits /* set up PWM to cycle every ~32 msec */ TCCR1A = 0x20 + 0x01; //non-inverted, 8-bit PWM TCCR1B = 0x04; //prescale to 256 /* set up timer 0 to interrupt every 1 msec */ TIMSK = 0x02; //timer 0 overflow interrupt TCCR0 = 0x03; //prescale to 64 TCNT0 = t0reload; watchdog = 0; t_statemachine = deltaT; //initialize task timer state = start; //initialize state variable #asm("sei") //fire up the interrupts } |