Full Version : Iceman & Diesel RC Car Controller (AVR ASM)
avr >>ROBOTS & AUTONOMOUS VEHICLES >>Iceman & Diesel RC Car Controller (AVR ASM)


Admin5- 04-21-2006
RC Car Controller

Blair "Iceman" Lee & John "Diesel" McDonald


Overview:

We decided to build transmitter and receiver modules for a radio-controlled (RC) car, as well as implement variable-speed motor control and a continuous steering function. The simple speed controls included in most RC kits seldom offer more than three forward speeds and one reverse speed; furthermore, steering controls in most “Radio Shack” toys only offer binary steering: Either the car is turning left, right or not at all. We felt that overcoming these limitations would make any RC car more realistic to handle and ultimately more fun to drive.

The Car

A Tamiya RC model kit was bought from a Maryland hobby store, along with a rudimentary radio control unit to verify that original unit’s operation. After making sure all parts were assembled correctly, the mechanical speed control, speed control servo, and radio receiver module were all removed. The car we selected already implements continuous steering with a servo attached to the steering linkage; however, because the radio receiver was no longer being used, we decided to leave the servo in and reverse engineer its interface.

The original implementation of speed control in the car consists of a servo which mechanically moves the arm of a simple high-power potentiometer. While the motion of the servo is continuous, the circuit only produces six discrete levels: three forward, two reverse, and neutral. Hobbyists in RC cars often replace this assembly with an electronic speed control (ESC), which uses pulse width modulation to provide a smooth speed control. We chose to simulate the function of these commercially available ESCs by using the microprocessor to modulate pulse width across the motor.

Communication

An RC car is of little value if the controls are tethered to the vehicle. However, we determined during the design process that the radio system was mostly irrelevant to the actual project, and that the time required to build the system would seriously impede on the rest of the project. We decided instead to use a pair of commercially available radio modems. The devices, made by National Semiconductor and used by Laplink under the “AirShare” label, claim to establish a 115 kbps serial connection at a distance of up to 30 feet with clear line-of-sight. While this range is inferior to that of commercially available radio control units, it is more than adequate to prove that the rest of the design works.

Command Opcode Magnitude Range
Forward 0000 0000 - 1111
Reverse 0001 0000 - 1111
Left 0010 0000 - 1111
Right 0011 0000 - 1111
Start 0100 n.a.
Stop 0101 n.a.

A simple communication protocol was established to send messages from the controller to the car. Because the connection is serial, we encode each command into a byte-long packet. The top nibble denotes the command, and the lower nibble represents the level at which the command is to be executed. For discrete operations (like headlights), the second nibble determines which functions are to be toggled. Communication travels one way from the controller to the car, which cuts down on the hardware required for either unit.


Controller

The original controller used spring centered potentiometers to produce analog signals which controlled the speed and direction of the car. The analog signals were transmitted to the car via a 75 MHz AM radio link.

In order to emulate the true RC car experience, we decided that we needed something more visually intuitive than two potentiometers stuck into a breadboard. We purchased a simple PC joystick from Radio Shack and removed the "turbo" button circuitry. Using the existing interface cable, we were able to connect the internal potentiometers and pushbuttons to the receiver board without cosmetically altering the joystick.


Our design for the controller uses National Semiconductor serial ADCs to convert the analog waveform from the potentiometers to a digital signal. For example, the 8-bit value obtained from the forward/reverse potentiometer is compared against a known value for that potentiometer’s “centered” value; we can therefore determine whether the user intends to go forward or backward, and the rate at which she plans to do so. These data are used to generate the opcode and value to be sent to the car over the serial connection.

After encoding, the character is sent to the car. The controller polls the user inputs one after another, sending out data regardless of a change in state of the input.

Receiver

In many ways, the receiver unit acts like a conventional microprocessor: Instructions are fetched from the input byte stream, decoded into an operation and magnitude, and dispatched to the proper control unit. Depending on the instruction, most of the work is done by either the speed control or the turning control; all other instructions control simple on/off devices and can be controlled directly in the receiver’s main loop.

Steering Control

As stated above, steering is controlled by a servo. Rather than supplying a simple voltage, servos operate on a fixed voltage source and a control line that is pulsed to dictate the turn angle. Previous work on the servo left us with a DAC circuit, and we could have added a 555-based timer circuit for pulse width modulation; however, we decided to simplify the design by driving the servo off the mcu. We used the Timer0 interrupt subroutine in conjunction with user input from the transmitter to determine the pulse width to be applied and sent through the mcu's port pins.



Speed Control

The received four-bit digital signal denotes the desired speed of the motor. Instead of using a digital-to-analog converter, we decided to drive the motor at full voltage and use pulse width modulation to control speed. Several products exist which accomplish this task; however, it is a simple matter to implement PWM on the microcontroller. A timer interrupt is used to count 16 “ticks”, and the given magnitude determined how many of those ticks the motor will be driven.


Due to the motor’s excessive power, it is impossible to drive the motor directly from the microcontroller. The speed control circuit solves this problem by amplifying the signals generated by the Atmel 4414 chip to allow large power transistors to drive the motor. The motor can draw a significant amount of current, roughly 7-8 amps, when turned on. The amplifier uses 200W rated BJTs in a class B push-pull configuration to drive this load. Since the base current to the power BJTs is still on the order of several hundred mAs, intermediate transistors were used to drive the base currents, which turn on and turn off the large BJTs. The intermediate transistors are wired together with the power BJTs in pairs so that the gain of each pair is significant enough to allow it to be driven by the output ports on the microcontroller.

Results

In short, the system works. The joystick is able to move the car forwards and back, and turn left and right. However, it was a long road getting to this point.

The pulse width modulation, as it was originally conceived, was far too fast for the motor to turn at all. Slowing down the period to approximately one second, up from 1 millisecond, solved the problem; however, there is a noticeable "jerk" to the forward and backward motions of the car. It may be possible to reduce the period, perhaps in half or even smaller; this was not tested in time for the project deadline. There was some initial irregularity in the pulse itself; instead of regular intervals, it seemed as though the pulses were being interrupted by some external stimulus. Careful programming to avoid register clobbering solved much of the problem, but irregularities occasionally appear at unpredictable times.

Turning the car is nicely variable but not particularly smooth. The same irregularities found in the speed control manifest themselves to a greater extent in the servo, causing the wheels to jerk slightly left and right of the desired turn angle. We suspect the problem to be extremely difficult to solve with the Atmel mcu, as pulse widths for the servo vary from 1 to 2 milliseconds; at those small periods, it is difficult to get very accurate timing with the Timer0 interrupt. Perhaps a 555 circuit would have solved this problem.

The software portion of the project was easy to design, and the implementation is quite simple. By avoiding complicated code, we are able to concentrate on hardware issues. Because most of this project relies on carefully designed circuitry, the reliability of the program greatly simplifies the debugging process.

Easily the most contentious and dangerous piece of hardware in the project, the motor control circuitry is both particularly frustrating to design and test. The lack of high power, high current handling MOSFETs in Ithaca made the task significantly more challenging. Many combinations of high power BJTs were used, and a few can drive the car forward. Throughout the testing process we experienced excessive current draw in any number of the BJTs despite transistor biasing and current limiting attempts. Several high power transistors were rendered unusable after a couple seconds of exposure in these circuits. Several fingers bear the mark of instantaneous thermal transfer from contact with the now defunct BJTs. I even saw a 16 gauge wire solder itself in a few seconds when a previous attempt with a 30 watt soldering iron for 20 min was unsuccessful.

Prior to the development of the working push-pull amplifier, none of the circuits was able to drive the car in both the forward and reverse directions. Many disastrous results were experienced before a successful combination of components was discovered. Sometimes the behavior of the circuits defied even Pspice. One of the circuits can be pulse width modulated between fast and very fast, but could not be turned off. The lesson learned is that the trick to working with BJTs is having a very tight control over the base currents. Again, power MOSFETs would have made this significantly easier.

Link:http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s1999/blair/RCcar.html

CODE

;Blair Lee
;John McDonald
;EE 476 Final Project
;RC Car - TRANSMITTER

;PORTA is used for speed control input.
;PORTB is used for pushbutton input.
;PORTC is used for steering control input.
;PORTD is used for serial output.

.include "4414def.inc"

.def    savSREG   =r16         ;save the status register
.def    TXbusy    =r17         ;transmit busy flag
.def    TXflash   =r18         ;text to be sent is in flash if <> 0
.def    command   =r19     ;command register
.def    temp      =r20     ;general-purpose temporary register
.def    Volt      =r21         ;ADC sample value
.def    Count     =r22         ;a counter
.def    LEDReg    =r23         ;register to store LED output (debug)
.def    debounce  =r24     ;debounce ON or OFF
.def    DBcnt     =r25         ;debounce counter
.def    status    =r26         ;running and lights status
.def    release   =r27         ;button release register
.def    temp2     =r28         ;another temporary register

.equ    baud96   =25    ;9600 baud constant for 4Mhz crystal
.equ    SpdMid   =0x0c  ;midpoint for fwd-back control
.equ    StrMid   =0x0c  ;midpoint for left-right control
.equ    FwdOffset=0xfd ;forward offset
.equ    RevOffset=0xfc ;reverse offset
.equ    LftOffset=0xfd ;left offset
.equ    RgtOffset=0xfd ;right offset
.equ    OFF      =0x00 ;off
.equ    ON       =0xff ;on

.equ    AADCDO   =1;ADC data out
.equ    AADCCLK  =2;ADC clock
.equ    AADCCS   =3;ADC chip-select
.equ    AADCDI   =4;ADC data in


.equ    CADCDO   =1;ADC data out
.equ    CADCCLK  =2;ADC clock
.equ    CADCCS   =3;ADC chip-select
.equ    CADCDI   =4;ADC data in

.equ    TOP      =4     ;top hat switch
.equ    TRIG     =5     ;trigger switch
.equ    SWmask   =0x30  ;switch mask
.equ    Maxlights=0x04  ;nuumber of light modes

.equ    fwdcmd   =0x00  ;forward prefix
.equ    bakcmd   =0x10  ;backward prefix
.equ    lftcmd   =0x20  ;left prefix
.equ    rgtcmd   =0x30  ;right prefix
.equ    gocmd    =0x40  ;lights prefix
.equ    stpcmd   =0x50  ;other prefix

.equ    But1    =0x01;
.equ    But2    =0x02;
.equ    But3    =0x04;
.equ    But4    =0x08;
.equ    But5    =0x10;
.equ    But6    =0x20;
.equ    But7    =0x40;
.equ    But8    =0x80;

;========================================================
;   Toggle ADC Clock - PORTA
;ADC reads and outputs data on falling edge of the clock

.MACRO  A_CLOCKPULSE
   sbi     PORTA, AADCCLK             ;CLK high
   nop
   cbi     PORTA, AADCCLK             ;CLK low
   nop
.ENDMACRO

;========================================================
;   Toggle ADC Clock - PORTC
;ADC reads and outputs data on falling edge of the clock

.MACRO  C_CLOCKPULSE
   sbi     PORTC, CADCCLK             ;CLK high
   nop
   cbi     PORTC, CADCCLK             ;CLK low
   nop
.ENDMACRO

;========================================================
;************************
.dseg

;define variable strings to be tranmitted from RAM
cntstr: .byte   2      

;************************
.cseg

.org $0000
       rjmp    Reset  ;reset entry vector
       reti
       reti
       reti
       reti
       reti
       reti
       reti
       reti
       reti
       rjmp    TXempty;UART buffer empty
       rjmp    TXdone ;UART transmit done
       reti

RESET:  
       cli
       ldi     temp, LOW(RAMEND);setup stack pointer
       out     SPL, temp
       ldi     temp, HIGH(RAMEND)
       out     SPH, temp

      ;initial conditions
       clr     TXbusy         ;start out not busy on TX

      ;setup UART -- enable TXempty & RXdone int, and RX, TX pins
       ldi     temp, 0b00101000
       out     UCR, temp
      ;set baud rate to 9600
       ldi     temp, baud96
       out     UBRR, temp

      ;initialize ADCs on ports A and C
   ldi temp, 0b00011101       ;power ADC and set AADCCS
   out DDRA, temp
   ldi temp, 0b00001001       ;power ADC and set AADCCS
   out PORTA, temp
   ldi temp, 0b00011101       ;power ADC and set CADCCS
   out DDRC, temp
   ldi temp, 0b00001001       ;power ADC and set CADCCS
   out PORTC, temp

      ;set up leds on port B
   ldi     temp, 0b00000011               ;input from switches on joystick
   out DDRB, temp
       out     PORTB, temp

      ;debounce setup
       clr     debounce     ;debounce on or off
       clr     DBcnt        ;debounce counter
       clr     status       ;initialize car to off

   sei


;*********************Setup and Read Speed*********************
Getspd:
       clr Volt
   ldi Count, 5       ;get top 5 bits

       A_CLOCKPULSE
       cbi PORTA, AADCCS      ;initiate conversion
       A_CLOCKPULSE
       sbi PORTA, AADCDI      ;startbit
       A_CLOCKPULSE
       sbi PORTA, AADCDI      ;single ended
       A_CLOCKPULSE
       cbi PORTA, AADCDI      ;channel 0
       A_CLOCKPULSE

ReadA:  A_CLOCKPULSE
   clc            ;clear carry
   sbic    PINA, AADCDO
   sec            ;set carry if high
   rol Volt
   dec Count
   brne    ReadA          ;more bits remaining
   
   ldi Count, 8

IgnoreA:A_CLOCKPULSE
   dec Count
   brne    IgnoreA                ;ignore lsb first data

   sbi PORTA, AADCCS      ;conversion complete

;***********************Transmit Speed***********************

_bakCmd:
       cpi     Volt, SpdMid
       brlt    _fwdCmd
       cpi     Volt, SpdMid
       breq    _stpCmd
       lsr     Volt
       subi    Volt, RevOffset
       ori     Volt, bakcmd
       mov     command, Volt
       rjmp    Sendspd

_fwdCmd:
       ldi     temp, SpdMid
       sub     temp, Volt
       subi    temp, FwdOffset
       ori     temp, fwdcmd
       mov     command, temp
       rjmp    Sendspd

_stpCmd:
       ldi     command, 0x00

Sendspd:
;        rcall   LEDSt
       rcall   SendMsg


;********************Setup and Read Steering********************
Getstr: clr Volt
   ldi Count, 5       ;get top 5 bits

   C_CLOCKPULSE
   cbi PORTC, CADCCS      ;initiate conversion
   C_CLOCKPULSE
   sbi PORTC, CADCDI      ;startbit
   C_CLOCKPULSE
   sbi PORTC, CADCDI      ;single ended
   C_CLOCKPULSE
   cbi PORTC, CADCDI      ;channel 0
   C_CLOCKPULSE

ReadC:  C_CLOCKPULSE
   clc            ;clear carry
   sbic    PINC, CADCDO
   sec            ;set carry if high
   rol Volt
   dec Count
   brne    ReadC          ;more bits remaining
   
   ldi Count, 8

IgnoreC:C_CLOCKPULSE
   dec Count
   brne    IgnoreC                ;ignore lsb first data

   sbi PORTC, CADCCS      ;conversion complete

;***********************Transmit Steering***********************

_rgtCmd:
       cpi     Volt, StrMid
       brlt    _lftCmd
       cpi     Volt, StrMid
       breq    _ctrCmd
       lsr     Volt
       subi    Volt, RgtOffset
       ori     Volt, rgtcmd
       mov     command, Volt
       rjmp    Sendstr

_lftCmd:
       ldi     temp, StrMid
       sub     temp, Volt
       subi    temp, LftOffset
       ori     temp, lftcmd
       mov     command, temp
       rjmp    Sendstr

_ctrCmd:
       ldi     command, 0x00
       ori     command, rgtcmd

Sendstr:
;        rcall   LEDSt
       rcall   SendMsg

;***************************Get Status***************************

Getstat:
       cpi     debounce, ON     ;check if debounce is on
       breq    _debounce
       in      temp, PORTB
       cpi     release, OFF     ;check if buttons have been released
       breq    _release

      ;buttons have been debounced and released
       sbic    PINB, TRIG
       rjmp    _trig
_running:
       sbis    PINB, TOP
       rjmp    _status
       mov     temp, status
       com     temp
       andi    temp, 0xf0
       andi    status, 0x0f
       or      status, temp
       rjmp    _status

_trig:
       mov     temp2, status
       andi    temp2, 0x0f
       inc     temp2
       cpi     temp, Maxlights
       brge    _lightsoff
       rjmp    _running

_lightsoff:
       ori     status, 0xf0
       rjmp    _running

_debounce:
       inc     DBcnt            ;increment debounce counter
       breq    _DBdone
       rjmp    _release
_DBdone:
       ldi     debounce, OFF    ;turn debounce off
       rjmp    _release

_release:
       ori     temp, 0x30
       brne    _status
       ldi     release, ON

;************************Transmit Status*************************

_status:
       mov     command, status
       ori     command, 0xf0
       breq    _off
_on:
       mov     command, status
       ori     command, 0x5f
_off:
       mov     command, status
       ori     command, 0x4f

       rcall   SendMsg
       rjmp    GetSpd

;***************************Return*******************************


;******************
;Pause procedure
;******************
;_Wait:
;        cli                          ;disable interrupts
;        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, 255
;        brne    outerL
;        sei                          ;here the outer loop is complete; approx. 1 ms has passed
;        ret                          ;return from wait subroutine

;*******************
;message sending sub
;*******************
SendMsg:
       ldi     ZL, LOW(cntstr) ;ptr to RAM
       ldi     ZH, HIGH(cntstr)
       inc     ZL
       st      Z, command
       ld      r0,Z
       out     UDR, r0        ;fire off the UART transmit
       clr     TXflash        ;the string is in RAM
       ser     TXbusy         ;and set the TX busy flag
       sbi     UCR, UDRIE     ;enable the TXempty interrupt
       rcall   TXwait
       ret

;******************
;Debug procedure
;******************
LEDSt:
       mov     LEDReg, command
       com     LEDReg         ;invert to send correct output to leds
       out     PORTB, LEDReg
       ret
       

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;UART stuff follows.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; UART needs a character
TXempty:in      savSREG, SREG  ;save processor status
       tst     TXflash        ;Is the string in flash memory?
       breq    TXram          ;If not, it is in RAM
       inc     ZL             ;get the next char from flash

       lpm                    ;and put it in r0
       rjmp    TXfls
TXram:  inc     ZL             ;get the next char from RAM
       ld      r0,Z
TXfls:  tst     r0             ;if char is zero then exit
       breq    TXend
       out     UDR, r0        ;otherwise transmit it
       rjmp    TXexit         ;exit until next char
TXend:  clr     TXbusy         ;no more chars
       cbi     UCR, UDRIE     ;clear the TXempty interrupt
TXexit: out     SREG, savSREG  ;restore proc status
       reti                   ;back to pgm

; TX done -- buffer is empty  -- unused here
TXdone: in      savSREG, SREG  ;save processor status
       out     SREG, savSREG  ;restore proc status
       reti                   ;back to pgm

;*****************************
;subroutine

TXwait: tst     TXbusy         ;now wait for the tranmission to finish
       brne    TXwait
       ret



Admin5- 04-21-2006

Link to Schematics: http://instruct1.cit.cornell.edu/courses/e...Schematics.html

Code for Receiver:

CODE

;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

;*****************************




Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.