;Blair Lee ;John McDonald ;EE 476 Final Project ;RC Car - RECEIVER .include "4414def.inc" .def spdPulse=r16 ;speed pulse counter for PWM (0..16) .def spdPW =r17 ;speed pulse width (0=stop, 15=full on) .def strPulse=r18 ;steering pulse counter .def strPW =r19 ;steering pulse width (14=left, 30=right) .def savSREG =r20 ;save the status register .def RXchar =r21 ;a received character .def state =r22 ;state machine variable .def Spdreg =r23 ;speed register (Forward, Reverse) .def temp =r24 ;temporary register .def temp2 =r25 ;another temporary register .def running =r26 ;flag to denote running or not running status .def timtemp =r27 ;timer temporary register .def lights =r28 ;lights status register .def temp3 =r29 .equ baud96 =25 ;9600 baud constant for 4Mhz crystal .equ read =0 .equ fwd =1 .equ back =2 .equ left =3 .equ right =4 .equ go =5 .equ stop =6 .equ mask =0x0f ;command line prefixes, shifted right 5 bits .equ fwdcmd =0x00 ;forward prefix .equ bakcmd =0x01 ;backward prefix .equ lftcmd =0x02 ;left prefix .equ rgtcmd =0x03 ;right prefix .equ gocmd =0x04 ;go prefix .equ stpcmd =0x05 ;stop prefix .equ Center =0x16 .equ MaxWidth =0x3f .equ SpdOffset =0xfd .equ OFF =0x00 .equ ON =0xff .equ Forward =0x0f .equ Reverse =0xf0 ;mask values for "other" commands .equ run_mask = 0x01 ;starts/stops car ;************************************** .dseg ;define variable strings to be transmitted from RAM cntstr: .byte 3 ;a two digit count + a zero terminate ;************************************** .cseg .org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti rjmp Timer1 ;Timer1 overflow - speed control rjmp Timer0 ;Timer0 overflow - steering control reti rjmp RXdone ;UART receive done reti reti reti ; PORTA is for speed control ; PORTC is for steering control ; PORTB controls the lights ; PORTD is the serial receiver ;Timer0 speed and steering control interrupt Timer0: cli ;disable interrupts inc strPulse ;increment the steering Pulse counter brmi StrOff ;turn steering pulse off cp strPulse, strPW ;compare strPulse to strPulseWidth brge StrOff ;turn steering pulse off tst strPulse ;check if counter is negative brmi StrOff ;turn steering pulse off ldi timtemp, ON out PORTC, timtemp ;turn steering pulse on sei ;enable interrupts reti StrOff: ldi timtemp, OFF out PORTC, timtemp ;turn steering pulse off sei ;enable interrupts reti Timer1: cli ;disable interrupts inc spdPulse ;increment the speed Pulse counter cp spdPulse, spdPW ;compare spdPulse counter to spdPW brge ShutOff ;branch to motor shutoff out PORTA, SpdReg ;turn motor on sei ;enable interrupts reti ;return from timer0 interrupt ShutOff: ldi timtemp, OFF out PORTA, timtemp ;turn motor off cpi spdPulse, MAXwidth ;compare spdPulse to MAXwidth breq TimerReset ;branch to reset the timer sei ;enable interrupts reti ;return from timer0 interrupt TimerReset: ldi spdPulse, 0x00 ;reset spdPulse out PORTA, SpdReg ;turn motor on sei ;enable interrupts reti ;return from timer0 interrupt RESET: cli ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ;initial conditions ldi RXchar, go ;start out running ;setup UART -- enable TXempty & RXdone int, and RX, TX pins ldi temp, 0b10010000 out UCR, temp ;set baud rate to 9600 ldi temp, baud96 out UBRR, temp ;enable Timer0 interrupts ldi temp, 0b10000010 ;turn on timer0 interrupt only out TIMSK, temp ldi temp, 0x01 ;prescale timer to raw clock out TCCR0, temp ldi temp, 0x00 out TCCR1A, temp ldi temp, 0b00000001 out TCCR1B, temp ;enable watchdog timer ldi temp, 0x0e out WDTCR, temp ;setup ports A and C to all output ldi temp, ON out DDRA, temp out DDRC, temp ldi temp, OFF out PORTA, temp out PORTC, temp ldi temp, 0xff out DDRB, temp ;initialize Speed ldi spdPW, OFF ldi spdPulse, OFF ;initialize Direction ldi strPW, Center ldi strPulse, OFF ldi running, OFF ldi state, read sei MainLoop: cpi state, read breq _read cpi state, stop breq _stop2 cpi state, go breq _go2 cpi state, fwd breq _fwd cpi state, back breq _back3 cpi state, left breq _left3 cpi state, right breq _right3 rjmp MainLoop _read: mov temp, RXchar ;grab a byte from stream for analysis mov temp2, temp ;don't want to clobber original temp lsr temp2 ;now shift right 4 bits (bear with us...) lsr temp2 lsr temp2 lsr temp2 cpi temp2, gocmd breq _readgo cpi temp2, stpcmd breq _readstp cpi running, ON ;check if input is to be used breq _readchng ldi state, stop rjmp MainLoop ;if not running, ignore input _readchng: cpi temp2, fwdcmd breq _readfd cpi temp2, bakcmd breq _readbk cpi temp2, lftcmd breq _readlt cpi temp2, rgtcmd breq _readrt rjmp MainLoop _readfd: ldi state, fwd rjmp MainLoop _readbk: ldi state, back rjmp MainLoop _readlt: ldi state, left rjmp MainLoop _readrt: ldi state, right rjmp MainLoop _readgo: ldi state, go rjmp MainLoop _readstp: ldi state, stop rjmp MainLoop _left3: rjmp _left2 _right3: rjmp _right2 _stop2: rjmp _stop _go2: rjmp _go _back3: rjmp _back _fwd: ; mov temp2, spdPW ; com temp2 ; out PORTB, temp2 ldi temp2, mask ;set up mask for magnitude and temp, temp2 ;extract magnitude breq _fwdstop ;stop the motor lsl temp ;scale the pulse width lsl temp cpi SpdReg, Reverse ;check if currently in reverse breq _fwdwait rjmp _fwd2 _fwdwait: rcall wait ;wait for 1ms _fwd2: mov spdPW, temp ;load PulseWidth with the appropriate value ldi SpdReg, Forward ;set current speed to forward subi spdPW, SpdOffset;add speed offset ldi state, read rjmp MainLoop _fwdstop: ldi spdPW, OFF ;set pulse width to zero ldi SpdReg, OFF ;set current speed to OFF ldi state, read rjmp MainLoop _left2: rjmp _left _right2: rjmp _right _back: ; mov temp2, spdPW ; com temp2 ; out PORTB, temp2 ldi temp2, mask and temp, temp2 ;extract magnitude lsl temp ;scale the pulse width lsl temp cpi SpdReg, Forward ;check if currently in forward breq _backwait rjmp _back2 _backwait: rcall wait ;wait for 1ms _back2: ldi SpdReg, Reverse ;set current speed to reverse mov spdPW, temp ;load PulseWidth with the appropriate value subi spdPW, SpdOffset;add speed offset ldi state, read rjmp MainLoop _left: mov temp2, temp com temp2 out PORTB, temp2 ldi temp2, mask and temp, temp2 lsr temp ;shift right to get top 3 bits ldi temp2, Center ;load Center steering position sub temp2, temp ;subtract left steering offset mov strPW, temp2 ;store steering PWM value ldi state, read rjmp MainLoop _right: mov temp2, temp com temp2 out PORTB, temp2 ldi temp2, mask and temp, temp2 lsr temp ;shift right to get top 3 bits ldi temp2, Center ;load Center steering position add temp2, temp ;add right steering offset mov strPW, temp2 ;store steering PWM value ldi state, read rjmp MainLoop _go: ldi running, ON ;turn car on mov lights, temp ;load new light settings andi lights, 0x0f ldi state, read rjmp MainLoop _stop: ldi spdPW, OFF ;stop the car ldi SpdReg, OFF ;set current speed to OFF ldi strPW, Center ;center the steering ldi lights, OFF ;turn lights off ldi state, read rjmp MainLoop ;wait before changing direction wait: cli ;disable interrupts ldi temp2, OFF out PORTA, temp2 ;turn motor off clr temp2 ;clear upper 8 bits of counter outerL: clr temp3 ;clear lower 8 bits of counter innerL: inc temp3 cpi temp3, 0 brne innerL inc temp2 cpi temp2, 16 brne outerL sei ;here the outer loop is complete; approx. 1 ms has passed ret ;return from wait subroutine ;***************************** ;interrupt routines ; UART read a character RXdone: cli wdr ;watchdog timer reset in savSREG, SREG ;save processor status in RXchar, UDR ;get the character out SREG, savSREG ;restore proc status sei reti ;back to pgm ;*****************************