Appendix I

C-Code Listing

/*
Chad Potocky
Ballard Smith
EE476 Spring 2001
Final Project: Train Controller

This is the code for a train controller which runs a train motor that is connected to an H bridge. The train is run at different speeds using the PWM on timer 1. There are also optical sensors used to indicate the position of the train on the track for speed optimization and lap counting. Finally the output is to a 4x20 LCD panel connected to PORTC.

*/ 
#include <90s8515.h> 
#include <stdio.h> 
#include <string.h> //For UART comparisons 
#include <math.h> //Needed the abs function
#include <delay.h> //Needed delay_ms for ramping

#define t1 60 

#define begin {
#define end }

/* Use an 4x20 alphanumeric LCD connected
to PORTC as follows:

[LCD] [AT90S8515 DIP40]
1 GND- 20 GND
2 +5V- 40 VCC
3 VLC 10k trimpot wiper (trimpot ends go to +5 and gnd) 
4 RS - 21 PC0
5 RD - 22 PC1
6 EN - 23 PC2
11 D4 - 25 PC4
12 D5 - 26 PC5
13 D6 - 27 PC6
14 D7 - 28 PC7 
*/

#asm
.equ __lcd_port=0x15
#endasm 
#include <lcd.h> // LCD driver routines

//timer 1 constants 
#define prescale1 1 // Sets the timer 1 prescalar to 8
#define t2 20 // Timeout constant for polling the push 
buttons
#define t3 30 // Timeout constant for polling the sensors
#define ptime 100 // Amount of time the button must be pressed 
before changing velocity, in ms
#define senseDelay 60 // Amount of time to wait for sensor 
debouncing = senseDelay * t3

//the subroutines
void task1(void); //test for a UART command, and decode it
void gets_int(void); //starts command from serial line
void initialize(void); //setup the whole process
void press(void); //check for a button press
void velChange(void); //change the velocity
void checkSensor(void); //check if the train has crossed a sensor
void runProg(void); //run a program given over UART
void rampup(void); //ramp the train up to speed
void rampdown(void); //ramp the train down to 0
void rampupcorner(void); //after a corner ramp back up to speed 
before the corner
void rampdowncorner(void); //save speed and ramp down to 60

char lcd_buffer[17]; //Output to the LCD
unsigned char last_forward; //Flag if the train was last going forward
unsigned char last_backward; //Flag if the train was last going 
backward
unsigned char senseDone; //Flag if sensor 0 is done sensing
unsigned char senseDone1; //Flag if sensor 1 is done sensing
unsigned char senseDone2; //Flag if sensor 2 is done sensing
unsigned char senseDone3; //Flag if sensor 3 is done sensing
unsigned char program; //Flag if a program is being run
unsigned char curr_prog; //Index into prog_buffer
unsigned char prog_lap; //Laps left in program
unsigned char prog_buffer[16];//Buffer of commands from a UART program
unsigned char doneLap; //Flag if the train has completed a lap at 
sensor 0
unsigned char doneLap1; //Flag if the train has completed a lap at sensor 1
unsigned char doneLap2; //Flag if the train has completed a lap at sensor 2
unsigned char doneLap3; //Flag if the train has completed a lap at sensor 3
unsigned char lapCount; //Total lap count since last reset
unsigned char addin; //Flag to add or subtract from velocity
unsigned char pressed; //Flag if the button is still pressed
unsigned char presstime; //Counter to see how long a button was 
pressed
int velocity; //Percentage of 1024 to place into OCR1A
int velsave; //Saved velocity when ramped down on curves
int vel; //Used in rampup function
int vel2; //Used in rampupcorner function
unsigned char reload; //timer 0 reload to set 1 mSec
unsigned char time1; //UART command decode timer
unsigned char time2; //Button poll timer 
unsigned char time3; //Sensor poll timer 
//RXC ISR variables 
unsigned char r_busy; //recieve ISR is running
unsigned char r_index; //current string index
unsigned char r_buffer[16]; //input string
unsigned char r_ready; //flag for receive done
unsigned char r_char; //current character

//**********************************************************
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow(void)
begin 

//reload to force 1 mSec overflow
TCNT0=reload;

//Decrement the three times if they are not already zero
if (time1>0) --time1; 
if (time2>0) --time2; 
if (time3>0) --time3;
if ((pressed == 1) && (presstime > 0)) {
--presstime; 
}

end 

//**********************************************************
//UART character-ready ISR
interrupt [UART_RXC] void uart_rec(void)
begin
r_char=UDR; //get a char
UDR=r_char; //then print it
//build the input string
if (r_char != '\r') r_buffer[r_index++]=r_char;
else
begin
putchar('\n'); //use putchar to aoid overwrite
r_buffer[r_index]=0x00; //zero terminate
r_busy=0; //and clean up
r_ready=1; //signal cmd processor
UCR.7=0; //stop rec ISR
end
end

//********************************************************** 
//Entry point and task scheduler loop
void main(void)
begin 
initialize();

//main task scheduler loop -- never exits!
while(1)
begin 
if (time1==0) task1(); //Check UART for a command 
if (time2==0) press(); //Check for a button press
if (time3==0) checkSensor(); //Check for a sensor hit
if (senseDone == 0) doneLap = 1; //If the sensor is done, 
1.8s of 0, then the next hit is a lap
if (senseDone1 == 0) doneLap1 = 1; //If the 
sensor is done, 1.8s of 0, then the next hit is a lap
if (senseDone2 == 0) doneLap2 = 1; //If the sensor is 
done, 1.8s of 0, then the next hit is a lap
if (senseDone3 == 0) doneLap3 = 1; //If the 
sensor is done, 1.8s of 0, then the next hit is a lap
if (presstime==0) velChange(); //If button 
pressed for .1s change velocity
if ((program == 1) && (prog_lap == 0)) runProg(); //If running a program and the lap is 0 run the next command
if (program == 1) { //Output program info if running one
lcd_gotoxy(10,3);
lcd_putsf("Lap ");
sprintf(lcd_buffer,"%-i",prog_lap);
lcd_puts(lcd_buffer);

//Output info to LCD, name of project, lap count, and train direction with speed
lcd_gotoxy(0,0);
lcd_putsf("B&C's Super Train");
lcd_gotoxy(0,1);
lcd_putsf("Lap Count");
lcd_gotoxy(10,1);
sprintf(lcd_buffer,"%-i",lapCount);
lcd_puts(lcd_buffer); 
lcd_gotoxy(10,2);
lcd_putsf("V = "); 
if ((abs(velocity/100)) == 1) {
sprintf(lcd_buffer,"%-i",abs(velocity));
lcd_puts(lcd_buffer);
}else if ((abs(velocity/10)) >= 1) {
lcd_putsf(" ");
sprintf(lcd_buffer,"%-i",abs(velocity));
lcd_puts(lcd_buffer);
}else {
lcd_putsf(" ");
sprintf(lcd_buffer,"%-i",abs(velocity));
lcd_puts(lcd_buffer);
}
end 
end 

/*
Reduces the velocity of the train to 60% before a corner
*/
void rampdowncorner(void){
vel2 = 10.24 * 60;
while (OCR1A > vel2) {
OCR1A--;
}


/*
Increases the velocity of the train after a corner to the
velocity it was before the rampdowncorner
*/
void rampupcorner(void){
vel2 = 10.24*(abs(velsave));
while (OCR1A < vel2) {
OCR1A--;
}
}

/*
Slowly decreases the velocity of the train to 0,
gives the train intertia
*/
void rampdown(void) {
while (OCR1A > 0) {
OCR1A--; 
delay_ms(1.5);
}


/*
Slowly increases the velocity of the train to the desired velocity,
gives the train intertia
*/
void rampup() { 
vel = 10.24 * abs(velocity);
while (OCR1A < vel) {
OCR1A++; 
delay_ms(1.5);
}
}

/***************************************************
* Run the given program
*/ 
void runProg(void) { 
//program is done
if (curr_prog >= 16)
program = 0; 
//program is done so stop
else if (prog_buffer[curr_prog] == 0) {
program = 0;
rampdown(); 
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Stop"); 
lcd_gotoxy(0,3);
lcd_putsf(" ");
TCCR1A = 0x00; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x00;
OCR1A = 0;
velocity = 0;
curr_prog = 0;
last_backward = 0;
last_forward = 0;
//program says to go forward
}else if ((char)prog_buffer[curr_prog] == 'f') {
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Forward"); 
if (last_backward == 1) rampdown();
PORTD.6 = 1;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03;
prog_lap = prog_buffer[curr_prog + 1] - 48;
velocity = 100; 
rampup(); 
OCR1A = 10.24 * abs(velocity);
velsave = 100;
rampdowncorner();
velocity = 60;
curr_prog = curr_prog + 3;
last_backward = 0;
last_forward = 1;

//program says to go backward
else if ((char)prog_buffer[curr_prog] == 'b') {
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Backward");
if (last_forward == 1) rampdown();
PORTD.6 = 0;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03;
prog_lap = prog_buffer[curr_prog + 1] - 48; 
velocity = -100; 
rampup();
OCR1A = 10.24 * abs(velocity);
velsave = -100;
rampdowncorner();
velocity = -60;
curr_prog = curr_prog + 3;
last_backward = 1;
last_forward = 0;
}
}

//********************************************************** 
//Task 1 -- detect a pusbutton
void task1(void) 
begin
int i;
time1=t1; //reset the task timer 
//PORTD.5 = PWM, PORTD.6=F/R 
//train is going backward
if ((program == 0) && r_ready && (strcmpf(r_buffer,"f")==0))
begin 
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Forward"); 
if (last_backward == 1) rampdown();
PORTD.6 = 1;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03; 
rampup();
last_backward = 0;
last_forward = 1; 
velocity = abs(velocity);
end 

//train is going backward
if ((program == 0) && r_ready && (strcmpf(r_buffer,"b")==0)) 
begin
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Backward");
if (last_forward == 1) rampdown();
PORTD.6 = 0;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03; 
rampup();
last_backward = 1;
last_forward = 0; 
velocity = -abs(velocity);
end 

//Stop the train, turn off PWM
if (r_ready && (strcmpf(r_buffer,"s")==0)) 
begin
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Stop"); 
rampdown();
PORTD.6 = 0;
PORTD.5 = 0; 
TCCR1A = 0x00;
TCCR1B = 0x00;
program = 0; 
velocity = 0; 
last_backward = 0;
last_forward = 0;
end 

//Detect a given program, set the program flags and fill the program buffer
if ((program == 0) && r_ready && ((char)r_buffer[0] == 'p')) {
lcd_gotoxy(0,3); 
lcd_putsf(" ");
lcd_gotoxy(0,3); 
lcd_putsf("Program");
for (i = 0; i<=13; i++) {

prog_buffer[i] = r_buffer[i+2];
}
PORTD.6 = 0;
PORTD.5 = 0;
program = 1; 
prog_lap = 0; 
curr_prog = 0;
}



//get another serial cmd 
if (r_ready ){
gets_int(); 
}
end 

//********************************************************** 
//Detect a button push on the input and set the button number
void press(void) {
time2 = t2; 
if (program == 0) { 
if ((PINB.0 == 0) && (pressed == 0)) { //Detect button 0 press to increase velocity 
pressed = 1; 
addin = 1; 
presstime = ptime;
}else if ((PINB.1 == 0) && (pressed == 0)) { //Detect button 1 press to decrease velocity
pressed = 1;
addin = 0; 
presstime = ptime;
}else if (PINB.2 == 0){ //Detect button 2 press to stop
pressed = 0;
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Stop"); 
rampdown();
PORTD.6 = 0;
PORTD.5 = 0; 
TCCR1A = 0x00;
TCCR1B = 0x00;
program = 0; 
velocity = 0; 
last_backward = 0;
last_forward = 0;
}else if (PINB.3 == 0){ //Detect button 3 press to go forward
pressed == 0;
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Forward"); 
if (last_backward == 1) rampdown();
PORTD.6 = 1;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03; 
rampup();
last_backward = 0;
last_forward = 1; 
velocity = abs(velocity); 
}else if (PINB.4 == 0){ //Detect button 4 press to go backward
pressed == 0;
lcd_gotoxy(0,2); 
lcd_putsf(" ");
lcd_gotoxy(0,2); 
lcd_putsf("Backward"); 
if (last_forward == 1) rampdown();
PORTD.6 = 0;
TCCR1A = 0x83; //Set PWM on and to toggle OC1A pin
TCCR1B = 0x03; 
rampup();
last_backward = 1;
last_forward = 0; 
velocity = -abs(velocity); 
} else if ((PINB.0 == 1) && (PINB.1 == 1)) pressed = 0; //If button 0 and 1 are not pressed reset pressed flag

}
/**************************************************************
* Check if the sensors are being hit
*/ 
void checkSensor(void) {
time3 = t3; 
if ((PINA.0 == 1) && (doneLap == 1)) { //Sensor 0 being hit and counting lap 
lapCount++; 
if (program == 1) prog_lap--;
if ((last_forward == 1) && (abs(velocity) > 60)) {
velsave = velocity;
rampdowncorner(); 
velocity = 60;
}else if ((last_backward == 1) && (abs(velsave) > 60)){
rampupcorner(); 
velocity = velsave;
}
doneLap = 0; 
senseDone = senseDelay;
}else if (PINA.0 == 1) //Sensor 0 being hit within 1.8s 
of another sensor hit
senseDone = senseDelay;
else if ((PINA.0 == 0) && (senseDone > 0)) //Sensor 0 not being hit, decrement senseTime
senseDone--;

if ((PINA.1 == 1) && (doneLap1 == 1)) { //Sensor 1 being hit and counting lap
if ((last_forward == 1) && (abs(velsave) > 60)) {
rampupcorner();
velocity = velsave;
}else if ((last_backward == 1) && (abs(velocity) > 60)){
velsave = velocity;
rampdowncorner(); 
velocity = -60;
}
doneLap1 = 0; 
senseDone1 = senseDelay;
}else if (PINA.1 == 1) //Sensor 1 being hit within 1.8s of another sensor hit
senseDone1 = senseDelay;
else if ((PINA.1 == 0) && (senseDone1 > 0)) //Sensor 1 not being hit, decrement senseTime
senseDone1--;

if ((PINA.2 == 1) && (doneLap2 == 1)) { //Sensor 2 being hit and counting lap
if ((last_forward == 1) && (abs(velocity) > 60)){
velsave = velocity;
rampdowncorner(); 
velocity = 60;
}else if ((last_backward == 1) && (abs(velsave) > 60)) {
rampupcorner(); 
velocity = velsave;
}
doneLap2 = 0; 
senseDone2 = senseDelay;
}else if (PINA.2 == 1) //Sensor 2 being hit 
within 1.8s of another sensor hit
senseDone2 = senseDelay;
else if ((PINA.2 == 0) && (senseDone2 > 0)) //Sensor 2 not being hit, decrement senseTime
senseDone2--;

if ((PINA.3 == 1) && (doneLap3 == 1)) { //Sensor 3 being hit and counting lap 
if ((last_forward == 1) && (abs(velsave) > 60)){
rampupcorner(); 
velocity = velsave;
}else if ((last_backward == 1) && (abs(velocity) > 60)) {
velsave = velocity;
rampdowncorner(); 
velocity = -60;
}
doneLap3 = 0; 
senseDone3 = senseDelay; //Sensor 3 being hit within 1.8s of another sensor hit
}else if (PINA.3 == 1)
senseDone3 = senseDelay;
else if ((PINA.3 == 0) && (senseDone3 > 0)) //Sensor 3 being hit and counting lap
senseDone3--;

}

//********************************************************** 
// Button 0 was pressed
void velChange(void) {
int temp;
presstime = ptime; 
if ((addin == 1) && (velocity < 100))
velocity = velocity + 5;
else if ((addin == 0) && (velocity > -100))
velocity = velocity - 5; //Increment or Decrement the set speed

lcd_gotoxy(0,2);

//If the velocity has changed positive to negative, or negative to positive
//switch directions on the train
if (velocity < 0) {
lcd_putsf("Backward");
PORTD.6 = 0;
}else{ 
lcd_putsf("Forward");
PORTD.6 = 1;
}

OCR1A = 10.24 * abs(velocity);



//********************************************************** 
// -- non-blocking keyboard check initializes ISR-driven
// receive. This routine merely sets up the ISR, which then
//does all the work of getting a command.
void gets_int(void) 
begin 
int x;
for (x = 0; x<16; x++)
r_buffer[x] = 0;
r_busy=1; 
r_ready=0;
r_index=0;
UCR.7=1;
end

//********************************************************** 
//Set it all up
void initialize(void)
begin 
//set up the ports
DDRA=0xf0; // PORT A is an input for sensors
PORTA = 0xff;
DDRD=0xe7; // PORT D is motor control 
PORTD = 0xff;
DDRC=0xff; // PORT C is an ouput 
DDRB=0x00; //PORT B is an output for leds
PORTB = 0xff;
PORTC=0; 
PORTB=0xf2; //all LEDs off and transistors off

//serial setup for commands. 
UCR = 0x18 ;
UBRR = 25 ; 

//set up timer 0 
//62.5 x (64x.25) microSec = 1.0 mSec, so prescale 64, and count 62 times.
reload=256-62; //value for 1 Msec 
TCNT0=reload; //preload timer 1 so that is interrupts after 1 mSec.
TCCR0=3; //prescalar to 64
TIMSK=2; //turn on timer 0 overflow ISR 

//set up timer 1
OCR1A = 512; //disable timer 1 until song starts 
velocity = 0;
TCNT1 = 0; //and zero the timer 
TCCR1A = 0x00; //Turn off timer 1 until needed
TCCR1B = 0x00;
PORTD.5 = 0; //Turn train off 
PORTD.6 = 0; //Turn train off
OCR1A = 0; //zero compare register

//initialize button press variables 
pressed = 0; 
presstime = ptime; 

//initialize lap count and sensor variables
lapCount = 0;
senseDone = 0;
doneLap = 0; 
senseDone1 = 0;
doneLap1 = 0;
senseDone2 = 0;
doneLap2 = 0;
senseDone3 = 0;
doneLap3 = 0;

//initialize program variables
program = 0;
prog_lap = 0; 
curr_prog = 0;

//init the task timers
time1=t1; 
time2=t2; 
time3 = t3;

putsf("\r\nStart\r\n"); 

// initialize the LCD for 16 char wide
lcd_init(20); 
// clear it
lcd_clear(); 
lcd_gotoxy(0,2); 
//Initially in the stop state.
lcd_putsf("Stop");
//crank up the ISRs
#asm
sei
#endasm 
gets_int();
end