Robot program

#define F_CPU 16000000UL

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/io.h>
// for some reason _delay_ms(n) doesn't work, but this does
void delay_ms(unsigned int n) {for(int j = 0; j < n; j++) _delay_ms(1);}
void delay_us(unsigned int n) {for(int j = 0; j < n; j++) _delay_us(1);}

void home_line2(void);
void string2lcd(char *lcd_str);

// constants used for setMotor
#define LEFTMOTOR 0
#define RIGHTMOTOR 1
#define OFF 0
#define HALFFORWARD 127
#define HALFREVERSE -127

// constants returned by readIR, readLight, and getDirection
#define SUCCESS 0
#define LEFT 0
#define STRAIGHT 1
#define RIGHT 2
#define STOP 3
#define TIME_OUT -1
#define OVERFLOW_ERROR 8 // same as _BV(DOR) (Data Overrun signal from USART)

// constants used for mode
#define IR_MODE 0
#define LIGHT_MODE 1

typedef unsigned char byte; // because I don't like calling bytes char's

// changes the speed of one of the motors
void setMotor(char whichMotor, signed char speed);
// changes the speed of both motors
void setMotors(signed char l, signed char r);

// mode: 0: follow IR remote; 1: follow visible light
int mode;
// variable used for getDirection in IR mode
int dir = 0;

// FUNCTION DEFINITIONS - IRCF

/* readIR
   puts the IR Control Freak into Beacon Mode and waits for a result.
   out - pointer to an array of 3 bytes
   If successful, this method returns 0 and writes the result to out
   (no attempt is made to interpret the data)
   If no remote was seen, returns STOP (3)
   If an error occurs, returns TIME_OUT (-1) or the relevant bits from UCSR0A
*/
int readIR(byte* out) {
        loop_until_bit_is_set(UCSR0A, UDRE);
        UDR0 = 36;
        // The IRCF responds to this code with groups of 3 bytes.
        // It stops when something is detected or after 99 times,
        // whichever comes first.
        for(int i = 0; i < 99; i++) { // (NOTE: must be in C99/GNU99 mode)
                if(i==10)
                        setMotors(0,0);
                byte done = 0;
                for(int j = 0; j < 3; j++) {
                        for(long k = 0; !(UCSR0A & _BV(RXC)); k++) {
                                if(k>=0x10000L)
                                        return TIME_OUT;
                        }
                        int x, y;
                        x = UCSR0A;
                        y = UDR0;
                        // 0x1c = 0b00011100 = all errors (frame,overrun,parity)
                        if(x&0x1c)
                                return x&0x1c; // 8: buffer full
                        out[j] = y;
                        if(y)
                                done = 1;
                }
                if(done)
                        return SUCCESS;
        }
        return STOP; // got nothing
}

/* readIR
   puts the IR Control Freak into Light Mode and read the result.
   out - pointer to an array of 2 bytes
   If successful, this method returns 0 and writes the result to out
   (no attempt is made to interpret the data)
   If an error occurs, returns TIME_OUT (-1) or the relevant bits from UCSR0A
*/
int readLight(byte* out) {
        loop_until_bit_is_set(UCSR0A, UDRE);
        UDR0 = 37;
        // The IRCF responds to this code with 2 bytes
        for(int j = 0; j < 2; j++) {
                for(long k = 0; !(UCSR0A & _BV(RXC)); k++) {
                        if(k>=0x10000L)
                                return TIME_OUT;
                }
                int x, y;
                x = UCSR0A;
                y = UDR0;
                if(x&0x1c)
                        return x&0x1c; // 8: buffer full
                out[j] = y;
        }
        return SUCCESS;
}

/* getDirection
   decides which direction to go, based on the results of readIR or readLight
   If successful, returns LEFT, RIGHT, STRAIGHT, or STOP
   If an error occurs, returns the error code from readIR/readLight
*/
int getDirection() {
        byte in[3];
        int ret;
        switch(mode) {
                case IR_MODE:
                        /* IR mode: cares about left and right sensors.
                           If both sensors are the same, goes the same
                           direction as last time.  If both sensors are
                           the same twice in a row, goes forward. */
                        ret = readIR(in);
                        if(ret==SUCCESS) {
                                if(in[0]<in[2]) {
                                        dir = -1;
                                        return LEFT;
                                } else if(in[0]>in[2]) {
                                        dir = 1;
                                        return RIGHT;
                                } else {
                                        if(dir>0) {
                                                dir--;
                                                return RIGHT;
                                        } else if(dir<0) {
                                                dir++;
                                                return LEFT;
                                        }
                                        return STRAIGHT;
                                }
                        } else
                                return ret;
                case LIGHT_MODE:
                        /* Light mode: somewhat odd; returns two values,
                           right then left, but lower numbers = darker,
                           and in complete darkness, right sensor gives
                           0x22 and left sensor gives 0x20 */
                        ret = readLight(in);
                        if(ret==SUCCESS) {
                                if(in[0]>=0x22 && in[1]>=0x20)
                                        return STOP;
                                else if(in[0]==in[1] && in[0]!=0x20)
                                        return STRAIGHT;
                                else if(in[0]>in[1])
                                        return LEFT;
                                else if(in[0]<in[1] ||
                                        (in[0]==in[1]&&in[0]==0x20))
                                        return RIGHT;
                                else
                                        return STOP;
                                /* Code from my earlier follow-light program:
                                if(c1>=0x22&&c2>=0x20) {
                                        PORTB = 1;
                                        setMotor(LEFTMOTOR,32);
                                        setMotor(RIGHTMOTOR,-32);
                                } else if(c1==c2&&c1!=0x20) {
                                        PORTB = 2;
                                        setMotor(LEFTMOTOR,127);
                                        setMotor(RIGHTMOTOR,127);
                                } else if(c1>c2) { // turn left
                                        PORTB = 0x10;
                                        setMotor(LEFTMOTOR,OFF);
                                        setMotor(RIGHTMOTOR,64);
                                } else if(c1<c2||(c1==c2&&c1==0x20)) {
                                        PORTB = 0x11;
                                        setMotor(RIGHTMOTOR,OFF);
                                        setMotor(LEFTMOTOR,64);
                                } else {
                                        PORTB = 0;
                                        setMotor(LEFTMOTOR,OFF);
                                        setMotor(RIGHTMOTOR,OFF);
                                }
                                */
                        } else
                                return ret;
                default:
                        // Should not be reached
                        return STOP;
        }
}

// FUNCTION DEFINITIONS - WRITING TO DISPLAY
// Mostly copied from sample program

//twiddles bit 3, PORTF creating the enable signal for the LCD
void strobe_lcd(void){
        PORTF = 0x08;
        PORTF = 0x00;
}

void clear_display(void){
        SPDR = 0x00;    //command, not data
        delay_us(2);    //wait for data to leave SPI port
        SPDR = 0x01;    //clear display command
        delay_us(2);    //wait for data to leave SPI port
        strobe_lcd();   //strobe the LCD enable pin
        delay_us(1900); //obligatory waiting for slow LCD
}

void home_line2(void){
        SPDR = 0x00;    //command, not data
        delay_us(2);
        SPDR = 0xC0;   // cursor go home on line 2
        delay_us(2);
        strobe_lcd();
        delay_us(1900);
}

//sends a char to the LCD
void char2lcd(char a_char){
        SPDR = 0x01;   //set SR for data xfer with LSB=1
        delay_us(2);   //wait till data has left SPI data register
        SPDR = a_char; //send the char to the SPI port
        delay_us(2);   //wait till data has left SPI data register
        strobe_lcd();  //toggle the enable bit
        delay_us(160); //wait the prescribed time for the LCD to process
}

//sends a string in FLASH to LCD
void string2lcd(char *lcd_str){
        int count;
        for(count = 0; lcd_str[count]!=0; count++) {
                SPDR = 0x01; //set SR for data
                delay_us(2);
                SPDR = lcd_str[count];
                delay_us(2);
                strobe_lcd();
                delay_us(160);
        }
}  

/* Run this code before attempting to write to the LCD.*/
void spi_init(void){
        DDRF=0x08;  //port F bit 3 is enable for LCD
        PORTB=0x00; //port B initalization for SPI
        DDRB=0x07;  //Turn on SS, MOSI, SCLK
        //Master mode, Clock=clk/2, Cycle half phase, Low polarity, MSB first
        SPCR=0x50;
        SPSR=0x01;
}

//External RAM will reside between 8000h - FFFFh.
//There will be 2 wait states for both read and write.
void init_ext_ram(void){
        PORTA=0x00;
        PORTC=0x00;
        PORTG=0x00;
        DDRA=0x00;
        DDRC=0xFF;
        DDRG=0xFF;
        MCUCR=0x80;
        XMCRA=0x42;
        XMCRB=0x80;
}

//initalize the LCD to receive data
void lcd_init(void){
        unsigned int i;
        delay_ms(15);
        for(i=0; i<=2; i++){ //do funky initalize sequence 3 times
                SPDR = 0x00;
                delay_us(2);
                SPDR = 0x30;
                delay_us(2);
                strobe_lcd();
                delay_us(6100);
        }

        SPDR = 0x00;
        delay_us(2);
        SPDR = 0x38;
        delay_us(2);
        strobe_lcd();   
        delay_us(4100);

        SPDR = 0x00;
        delay_us(2);
        SPDR = 0x08;
        delay_us(2);
        strobe_lcd();
        delay_us(4100);

        SPDR = 0x00;
        delay_us(2);
        SPDR = 0x01;
        delay_us(2);
        strobe_lcd();
        delay_us(4100);

        SPDR = 0x00;
        delay_us(2);
        SPDR = 0x06;
        delay_us(2);
        strobe_lcd();
        delay_us(4100);

        SPDR = 0x00;
        delay_us(2);
        SPDR = 0x0E;
        delay_us(2);
        strobe_lcd();
        delay_us(4100);
}

// Changes both lines on the display
void setDisplay(char* line1, char* line2) {
        clear_display();
        string2lcd(line1);
        home_line2();
        string2lcd(line2);
}

// FUNCTION DEFINITIONS - MOTORS
// Copied from committee member

void setMotors(signed char l, signed char r) {
        setMotor(LEFTMOTOR,l);
        setMotor(RIGHTMOTOR,r);
}

void setMotor(char whichMotor, signed char speed) {
   char temp;
/* This function sets a motor, determined by input parameter whichMotor.
 
   Note: portE bits 2 and 5 control the direction of the left and right motor,
   respectively.  OCR3A is power for left motor while OCR3B is for right motor
   
   This function must adjust the speed variable for the 0 - 255 range
   that is needed for the PWM counters in the micro.  The sign of the 
   variable will be used to set the direction bit for each motor, E.2 or E.5
*/

         switch (whichMotor) {
             case RIGHTMOTOR:
                 if(speed >= 0)
                 {
                        OCR3AL = 2 * speed; // boost the range up to max
                        temp = PINE | 0x04; // and set left motor to forward (1)
                 }
                 else
                 {      OCR3AL = -(2 * speed); // boost the range up to max
                        temp = (PINE & 0xFB); // left motor direction to reverse (0)
                 }
                break;
             case LEFTMOTOR:
                    if(speed >= 0)
                    {
                         OCR3BL = 2 * speed; // boost the range up to max
                         temp = PINE | 0x20; // and set right motor to forward (1)
                    }
                    else
                    {
                         OCR3BL = -(2 * speed); // boost the range up to max
                         temp = (PINE & 0xDF);  // right motor direction to reverse (0)
                    }
                break;
             }  
             PORTE = temp;       
}

//////////////////////////////////////////////////////////////////////////////
// and now for our feature presentation...
// THE DEFINITION OF MAIN!!!
int main() {
        // Lots of initializations of registers,
        // mostly copied from elsewhere
        DDRD = 0x00;
        DDRE=0xFF;
        //PORTE=0xFF;
        UCSR0A=0x00;
        UCSR0B=0x18;
        UCSR0C=0x06;
        UBRR0H=0x00;
        UBRR0L=0x67;

        // Port E initialization
        // Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out 
        // State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0 
        PORTE=0x00;
        DDRE=0xFF;
        // Timer/Counter 0 initialization
        // Clock source: System Clock
        // Clock value: 125.000 kHz
        // Mode: Normal top=FFh
        // OC0 output: Disconnected
        ASSR=0x00;
        TCCR0=0x05;
        TCNT0=0x00;
        OCR0=0x00;

        // Timer/Counter 1 initialization
        // Clock source: System Clock
        // Clock value: Timer 1 Stopped
        // Mode: Normal top=FFFFh
        // OC1A output: Discon.
        // OC1B output: Discon.
        // OC1C output: Discon.
        // Noise Canceler: Off
        // Input Capture on Falling Edge
        // Timer 1 Overflow Interrupt: Off
        // Input Capture Interrupt: Off
        // Compare A Match Interrupt: Off
        // Compare B Match Interrupt: Off
        // Compare C Match Interrupt: Off
        TCCR1A=0x00;
        TCCR1B=0x00;
        TCNT1H=0x00;
        TCNT1L=0x00;
        ICR1H=0x00;
        ICR1L=0x00;
        OCR1AH=0x00;
        OCR1AL=0x00;
        OCR1BH=0x00;
        OCR1BL=0x00;
        OCR1CH=0x00;
        OCR1CL=0x00;

        // Timer/Counter 2 initialization
        // Clock source: System Clock
        // Clock value: Timer 2 Stopped
        // Mode: Normal top=FFh
        // OC2 output: Disconnected
        TCCR2=0x00;
        TCNT2=0x00;
        OCR2=0x00;

        // Timer/Counter 3 initialization
        // Clock source: System Clock
        // Clock value: 15.625 kHz
        // Mode: Ph. correct PWM top=00FFh
        // Noise Canceler: Off
        // Input Capture on Falling Edge
        // OC3A output: Inverted
        // OC3B output: Inverted
        // OC3C output: Discon.
        // Timer 3 Overflow Interrupt: Off
        // Input Capture Interrupt: Off
        // Compare A Match Interrupt: Off
        // Compare B Match Interrupt: Off
        // Compare C Match Interrupt: Off
        TCCR3A=0xF1;
        TCCR3B=0x05;
        TCNT3H=0x00;
        TCNT3L=0x00;
        ICR3H=0x00;
        ICR3L=0x00;
        OCR3AH=0x00;
        OCR3AL=0x00;
        OCR3BH=0x00;
        OCR3BL=0x00;
        OCR3CH=0x00;
        OCR3CL=0x00;

        // External Interrupt(s) initialization
        // INT0: Off
        // INT1: Off
        // INT2: Off
        // INT3: Off
        // INT4: Off
        // INT5: Off
        // INT6: Off
        // INT7: Off
        EICRA=0x00;
        EICRB=0x00;
        EIMSK=0x00;

        // Timer(s)/Counter(s) Interrupt(s) initialization
        TIMSK=0x00;
        ETIMSK=0x00;

        // USART0 initialization
        // Communication Parameters: 8 Data, 1 Stop, No Parity
        // USART0 Receiver: On
        // USART0 Transmitter: On
        // USART0 Mode: Asynchronous
        // USART0 Baud rate: 9600
        UCSR0A=0x00;
        UCSR0B=0x18;
        UCSR0C=0x06;
        UBRR0H=0x00;
        UBRR0L=0x67;

        // pushbuttons and lights
        // I think the display causes some interference with this, but it
        // seems to work well enough
        DDRD = 0x00;  //PORTD all inputs
        DDRB = 0xff;

        spi_init();
        lcd_init();
        clear_display();
        
        // *sleeping peacefully*
        setMotor(LEFTMOTOR, OFF);
        setMotor(RIGHTMOTOR, OFF);
        setDisplay("*Yawn*","You woke me?!!");
        // Motors: MRAW!
        setMotors(64,64);
        delay_ms(100);
        setMotors(127,127);
        delay_ms(100);
        setMotors(32,32);
        delay_ms(200);
        setMotors(0,0);
        delay_ms(6000); // initial delay to get your hands away
        int nap = 0;
        mode = IR_MODE;
        while(1) {
                char str[80];
                // Take a nap.  Snoring sound made by motors at low speed
                if(nap>0x20) {
                        setDisplay("*Zzzzz*","Time for catnap!");
                        setMotors(2,2);
                        nap++;
                        if(nap>0x28) {
                                nap = 0;
                                setMotors(64,64);
                                delay_ms(100);
                                setMotors(127,127);
                                delay_ms(100);
                                setMotors(32,32);
                                delay_ms(200);
                        }
                        delay_ms(1000);
                        continue;
                }
                
                // left buttons => light; right buttons => IR
                if((PIND&0xfb)!=0xfb)
                        mode = ((PIND&0xf0)!=0xf0);
                
                int dir = getDirection();
                if(dir==STRAIGHT) {
                        setDisplay("MROW!",
                                   mode==IR_MODE?"Silly IR!":"Silly light!");
                        setMotors(127,127);
                        nap+=mode==IR_MODE?2:1;
                } else if(dir==LEFT) {
                        setDisplay("Meow","What's that?");
                        setMotors(0,64);
                } else if(dir==RIGHT) {
                        setDisplay("Meow","Ooh, over there!");
                        setMotors(64,0);
                } else {
                        if(dir==STOP)
                                setDisplay("Mew?",
                                           mode==IR_MODE?
                                           "Where did it go?":"I can't see!");
                        else if(dir==TIME_OUT)
                                setDisplay("MEEOOOWWW!",
                                           mode==IR_MODE?
                                           "I'm hungry!":"I'm cold!");
                        else if(dir==OVERFLOW_ERROR)
                                setDisplay("*Hairball*","I'm sick");
                        else {
                                sprintf(str,"SIGHRBL at 0x%x",dir);
                                setDisplay("HISS!",str);
                        }
                        setMotors(0,0);
                }
                delay_ms(100);
        }
}