Full Version : Hummer RC Truck (AVR ASM)
avr >>ROBOTS & AUTONOMOUS VEHICLES >>Hummer RC Truck (AVR ASM)


Admin3- 04-18-2006
For our final project, we decided to enhance the controls of a Hummer RC truck. Our main objective was to demonstrate that an Atmel microcontroller together with basic hardware building blocks can replace all of the car's original circuitry. Improving the RC truck's handling involved adding analog control over steering and speed. The original construction of the car hindered this idea and forced us to resort to some mechanical engineering (mounting a servo) to resolve the problem! Overall, the project was a great deal of fun and involved a lot of tinkering with hardware (including dangerous flirtations with nearly exploding power transistors!)

High Level Design:

In order to emulate the functionality of the original RC car, we had to use 2 microcontrollers; one at the transmitter end to process user input and transmit data and one at the receiver end to pick up that data and control the car's motors. We found a cheap but reliable way of establishing wireless communication between the two microcontrollers in the form of a receiver transmitter pair by Ming Microsystems.

We wanted to run the dc motor at the rear at variable speeds. Two options were available to us: One is to switch it on and off so fast that it appears to be running at an intermediate speed. By modulating the width of the pulse to the switch we could achieve various duty cycles. The other approach is to limit the current to the motor so as to slow it down. This requires an analog input to the motor and therefore a digital-to-analog converter. We ended up trying both ways and our results are documented below.

The car's front wheels were originally driven by a magnet. By flipping its polarity, the wheels are repelled all the way to the left or right. This didn't suit our needs. The other disadvantage of the electromagnet is that it constantly draws current from the battery (~400 mA). By using a servo, power consumption is significant only during transitions of the servo arm. The mini ball bearing FMA servo we used allowed us to have three different steering angles. More steering angles were not conceivable because of the design of the axial for the front wheels of the car.

Sampling user input is straightforward. All that is required is an ADC hooked up to the two variable voltage dividers controlled by the user.

Program/Hardware details:

Receiver:

The receiving MCU lies at the heart of the truck's circuitry. Two capacitors are used on the voltage regulator (LM78) to stabilize the input/output voltages. Communication between the MCU's is at 1200 baud and because of noise there is a possibility that the chips can lose synchronization. The receiver could be receiving the correct code but shifted or distorted by noise. To help correct this issue the transmitter must send an initialization byte signaling that the next byte is the appropriate opcode and that the transmitter is the source of the message. The receiver waits to receive this initialization code first, de-bounces to make sure it's not random noise and then takes action on the next received opcode. The receiver uses a timeout mechanism to turn off the motors if an initialization code is not received for 500 milliseconds.

When an opcode is received the byte is parsed based on the following format:

Right(1)/Left(0) Steering Magnitude Forward(1)/Reverse(0) Speed Magnitude
opcode: X XX X XXXX

The receiver first checks the magnitude and steering/speed directions to determine if the signal lights need to be turned on as shown below:

Forward Reverse Magn = 0
Speed Light GREEN RED OFF
Right Left Magn = 0
Steering Light GREEN RED OFF

PORTA was used to drive the LEDs.

Two DAC's are used; they share the same 5-bit input from the MCU but PORTC6 (reverse) and PORTC7 (forward) control their outputs. When a forward opcode is received the forward pin is set to an input and the reverse as an output to ground, which forces the reverse input to the motor to ground. This also allows for full speed to be achieved at 5 volts by setting all other outputs to zero except for the speed direction pin (6 or 7).

In order to run the dc motor in both directions a 4-switch network driven by control signals from the mcu is needed. This circuit is called an H-Bridge. By turning on two transistors on opposite branches of the bridge, the motor is driven in different directions. Turning on two transistors on the same side will create a low resistance path between Vdd and ground and will quickly lead to a colorful display of fireworks. The motor draws anywhere between 300mA to 2A depending on the resistance encountered. In order to minimize consumption at the 9V alkaline battery powering the mcu, we resorted to using power transistors to achieve high gain. A first stage amplifier (QN3904) is needed to turn on the bases of the TIP power transistors.

To control the speed of the motor, we first envisioned using PWM as described above. The timer code was written for this setup and the result did not match out expectations. The motor either stayed on most of the time at high frequency pulses or turned on and off in a jerky motion at lower frequencies. We then decided to use a different strategy; by varying the base voltage, instead of having logical 1's and 0's, we could deprive the motor of current and slow it down. The base resistors were chosen to minimize current sourced from the mcu pins and to achieve the biggest variation in speed over the range 0-5V.

The control signal to the servo is set by doing a table lookup and adjusting the timer1 compare B register. The 1-2 millisecond pulses to control the servo are generated by timer1. On a compare B interrupt the pulse is set high and on compare A it is set low. The servo voltage source is provided from the 9.6-volt nickel battery and a different voltage regulator because of the amount of current drawn by the servo.

Transmitter:

The transmitting MCU uses timer1 to start ADC conversions from the potentiometers. The spacing between samples is set to 500 milliseconds and the ADC sampling rate is set to 125khz. Once one conversion is done the ADC channel is switched. When both values are done they are transformed into our opcode format. Before the opcode is transmitted an initialization byte is sent.

Results:

The analog control is a big leap over what the car originally had to offer. The speeds that can be achieved range all the way from a snail's pace to a speed nearly faster than the original car. Steering control is less reliable because of the rickety front wheels. Even with the bulky breadboards, the entire assembly fits inside the spacious case and the final product has a polished look to it. Unfortunately, the same is not true for the user interface (the controller) which consists of two shabby looking potentiometers stuck in a board.

The communication between the mcu's is nearly foolproof. The receiver responds without a hitch. The redundant parity bits we use prevent the car from going off on its own when it's out of touch with the transmitter.

What Next?

One obvious thing we should have done was to beautify the user interface. The potentiometers are both hard to turn and liable to pop out of the board. A dual-axis spring centered joystick would have been a much more aesthetic alternative. The real next big thing was to add sensors to the truck! Using an ultrasonic ranger, the truck can estimate how far it is from obstacles on one or more sides and react to that based on a simple AI routine. We thought about this option when we were coming up with a project proposal but decided to defer it until we had everything else working properly. Unfortunately, by then, very little time was left for any meaningful work on that idea… Maybe that's something for the next generation of EE476 students to do!

Link: http://instruct1.cit.cornell.edu/courses/e...ameer/index.htm

CODE

;Ameer Ali & Paul Lahham
;EE476 Final Project - Hummer Transmitter

.nolist
.include "c:\avrtools\appnotes\8535def.inc"
.list

.def save =r1;SREG temp reg
.def temp =r16;temporary register
.def AnaLo =r17;A to D result
.def AnaHi =r18
.def opcode =r19
.def sendcode =r20

.equ samp_rate = 500 ;sample and send every 1000 milliseconds (up to 4194)
.equ baud12 =207 ;1200 baud rate for wireless transmission

.equ init_code = 0b10010000

; *** MACROS ***

; Set OCR1A using sampling rate
.MACRO SET_OCR1A
ldi temp, HIGH((@0*1000)/64)
out OCR1AH, temp
ldi temp, LOW((@0*1000)/64)
out OCR1AL, temp
.ENDMACRO

;Returns opcode for given channel in top nibble of AnaLo
.MACRO GET_OPCODE

in AnaLo, ADCL
in AnaHi, ADCH

lsr AnaHi
ror AnaLo
lsr AnaHi
ror AnaLo

ldi temp, 0b01111000

sbrs AnaLo, 7 ;if bit 7 is off
eor AnaLo, temp ;flip bits 5,6

andi AnaLo, 0b11111000

.ENDMACRO

;***** Initialization
.cseg
.org $0000
rjmp  RESET;reset entry vector
reti  
reti
reti
reti
reti
rjmp t1match
reti
reti
reti
reti  
reti
reti
reti  
reti
reti
reti


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

;set up port B to run LEDS
ser temp ;set PORTB to be
out DDRB,temp;all outputs to LEDs
out PORTB, temp

;enable TX and set baud rate at 1200
ldi temp, 0b00001000
out UCR, temp
ldi temp, baud12
out UBRR, temp

;set up Timer 1 to interrupt on compare A match
;and set the compare time to 62500 ticks
ldi temp,0b00010000;enable t1 matchA interrupt
out TIMSK, temp

SET_OCR1A samp_rate

ldi  temp,0b00001100;prescale timer by 256 (one tick=64 microsec)
out  TCCR1B, temp;and clear-on-matchA

;enable analog converter and set at 125kHz
ldi temp, 0b10000101
out ADCSR, temp

clr sendcode

sei;enable all interrupts

;Send synchronization sequence to receiver

;*** Rear wheel speed reading ***

ADC_sta:;set up analog converter to read channel zero
ldi temp, 0
out ADMUX, temp

ch0_sta:sbis ADCSR, ADSC;Start conversion when ADSC bit is set
rjmp ch0_sta ;this bit is set by t1match interrupt

ch0_end:sbic ADCSR, ADSC;Wait for A to D done by checking is ADSC if cleared
rjmp ch0_end ;this bit is cleared by the AtoD hardware

GET_OPCODE

lsr AnaLo
lsr AnaLo
lsr AnaLo

mov opcode, AnaLo

;*** Front wheel direction reading ***

ch1_sta:ldi temp, 4 ;switch to channel 4
out ADMUX, temp
sbi ADCSR, ADSC;Start A to D conversion on Ch1
 
ch1_end:sbic ADCSR, ADSC;Wait for A to D done by checking if ADSC is cleared
rjmp ch1_end ;this bit is cleared by the AtoD hardware

GET_OPCODE

andi AnaLo, 0b11100000;only need top 3 bits of direction
or opcode, AnaLo

ldi temp, init_code
clr sendcode

send_init:
out UDR, temp

tst sendcode
breq send_init

mov temp, opcode
com temp
out PORTB, temp
clr sendcode

send_op:out UDR, opcode

tst sendcode
breq send_op
rjmp ADC_sta

;**** timer 1 compare A match 1/sec
t1match:in save, SREG
ser sendcode
sbi ADCSR, ADSC;start ADC conversion
out SREG, save
reti



The Reciever Code:
CODE

;Ameer Ali & Paul Lahham
;EE476 Final Project - Hummer Receiver

.include "c:\avrtools\appnotes\8535def.inc"

.def save =r1;SREG temp reg
.def temp =r16;temporary register
.def RXcode =r17;received opcode
.def DACin =r19;used to output to spd DAC
.def count =r20;timer0 count
.def RXtimes =r21;debouncing counter for init code


.equ baud12 = 207;1200 baud constant for 4Mhz crystal
.equ D_F = 0;PortC0-3 used for forward speed
.equ D_R = 1;PortC4-7 used for reverse speed
.equ SVO = 7;Servo control bit = PORTD7

;Light 1
.equ L1G_F = 0;green = forward
.equ L1R_R = 1;red = reverse
;Light 2
.equ L2G_R = 2;green = right
.equ L2R_L = 3;red = left

.equ init_code = 0b10010000

.equ cycle = 10750;so each cycle is 20 millisecs
.equ cyc = 750;add 1.5ms, pulse off for 20ms
;***** Initialization
.cseg
.org $0000
rjmp  RESET;reset entry vector
reti  
reti
reti
reti
reti
rjmp T1CompA
rjmp T1CompB
reti
rjmp T1ov
reti
rjmp RXdone;UART receive done
reti
reti  
reti
reti
reti

StrTR:;3 different steering magnitudes, OCR1B (TABLE OF OFF TIMES)
.db high(9200+cyc), low(9200+cyc)
.db high(9150+cyc), low(9150+cyc)
.db high(9100+cyc), low(9100+cyc);turn right all the way

StrTL:;3 different steering magnitudes, OCR1B (TABLE OF OFF TIMES)
.db high(9300+cyc), low(9300+cyc)
.db high(9350+cyc), low(9350+cyc)
.db high(9400+cyc), low(9400+cyc);turn left all the way

SpdV:;15 different speed voltages
.db 0b00000000
.db 0b00001100;1.875V
.db 0b00001101;2.03V
.db 0b00001111;2.34V
.db 0b00010000;2.5V

.db 0b00010001;2.656V
.db 0b00010011;2.969V
.db 0b00010100;3.125V
.db 0b00010110;3.4375V
.db 0b00011000;3.75V

.db 0b00011001;3.906V
.db 0b00011010;4.06V
.db 0b00011011;4.218V
.db 0b00011101;4.53V
.db 0b00011111;5V

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

;setup UART -- enable RX ISR & pin
;9 bits enabled
ldi  temp, 0b10010000
out  UCR, temp
;set baud rate to 1200
ldi temp, baud12
out UBRR, temp

;Enable timer1 compare A & B  match interrupt
ldi temp, 0b00011001
out TIMSK, temp
;prescale timer1 by 8 & clear on matchA
ldi temp, 0b00001010
out TCCR1B, temp;each tick = 2 microsec

;T0 prescale by 256
ldi temp, 0b00000100
out TCCR0, temp

ldi temp, high(cycle)
out OCR1AH, temp
ldi temp, low(cycle)
out OCR1AL, temp

;give OCR1B an initial value to center servo
ldi temp, high(cycle-750); 750*2usec=1.5msec
out OCR1BH, temp
ldi temp, low(cycle-750)
out OCR1BL, temp
 
;PortD as output & power for Receiver
ser temp
out DDRD, temp

;PORTC as a output for rear motor
ser temp
out DDRC, temp
clr temp
out PORTC, temp;turn it off

;PORTB as output to LED's (** For testing **)
ser temp
out DDRB, temp
out PORTB, temp;turn them off

;PORTA as output for RED-GREEN LIGHTS
out DDRA, temp

clr temp
out PORTA, temp;turn them off
out TCNT1H, temp
out TCNT1L, temp
clr RXcode
clr RXtimes
sei;enable all interrupts

winit: clr count
winit_: cpi  count, 32
breq turnoff
cpi RXcode, init_code;wait for init code
brne winit_
clr RXtimes

idle_wait:
cpi RXtimes, 50
brne idle_wait

cpi RXcode, init_code
breq wcode

turnoff:ldi RXcode, 0
rjmp ex_code  

wcode: cpi RXcode, init_code
breq wcode  ;wait for code to change

ex_code:cbi UCR, RXCIE;disable UART RX ISR

mov temp, RXcode
andi temp, 0b00001111;get and set speed
breq no_spd

sbrs RXcode, 4
sbi PORTA, L1R_R
sbrs RXcode, 4
cbi PORTA, L1G_F ;turn on reverse light

sbrc RXcode, 4
cbi PORTA, L1R_R
sbrc RXcode, 4
sbi PORTA, L1G_F ;turn on forward light
rjmp _spd

no_spd: cbi PORTA, L1R_R
cbi PORTA, L1G_F ;turn on forward light

_spd: out PORTB, RXcode
mov temp, RXcode

andi temp, 0b00001111;get and set speed
cpi temp, 0b00001111
breq ful_spd

lsl temp
ldi ZL, low(SpdV*2)
ldi ZH, high(SpdV*2)
add ZL, temp;index into table based on RX mag.
clr temp
adc ZH, temp
lpm;r0

mov DACin, r0

sbrs RXcode, 4
breq rev

fwd: ldi temp, 0b10111111;Set c6 as input
out DDRC, temp
 out PORTC, DACin ;pullup on c6 turned off automatically
rjmp spd_done

rev: ldi temp, 0b01111111;Set c7 as input
out DDRC, temp
out PORTC, DACin
rjmp spd_done

ful_spd:;Forward at full speed
sbrc RXcode, 4
ldi DACin, 0b01000000
sbrc RXcode, 4
rjmp rev

;Backward at full speed
ldi DACin, 0b10000000
rjmp fwd

spd_done:;get and set steering
mov temp, RXcode
andi temp, 0b01100000
brne str

;if magn. of str=0, center the servo (1.5ms pulses)
ldi temp, high(cycle-750)
out OCR1BH, temp
ldi temp, low(cycle-750)
out OCR1BL, temp
cbi PORTA, L2G_R ;turn on right light
cbi PORTA, L2R_L
rjmp done

str: lsr temp ;shift to get integer value
lsr temp
lsr temp
lsr temp ;*2, since 2 bytes per entry
subi temp, 2 ;since there is no zero case
sbrs RXcode, 7
rjmp strL

ldi ZL, low(StrTR*2)
ldi ZH, high(StrTR*2)
add ZL, temp;index into table based on RX mag.
clr temp
adc ZH, temp
lpm;r0
sbi PORTA, L2G_R ;turn on right light
cbi PORTA, L2R_L
rjmp str_done

strL: ldi ZL, low(StrTL*2)
ldi ZH, high(StrTL*2)
add ZL, temp;index into table based on RX mag.
clr temp
adc ZH, temp
lpm;r0
cbi PORTA, L2G_R ;turn on left light
sbi PORTA, L2R_L

str_done:
out OCR1BH, r0;get values and place
adiw ZL, 1 ;them in OCR1B
lpm;r0
out OCR1BL, r0

done: sbi UCR, RXCIE;now enable UART RX ISR
rjmp winit
;========================================================================
;========================================================================
;*****************************
;interrupt routines
;**** Receive complete interrupt
RXdone: in save, SREG;save processor status
in RXcode, UDR ;Read received byt into register
inc RXtimes
out SREG, save;restore proc status
reti  ;back to pgm
;*****************************
;*****************************
;**** TIMER 1 compare B
;counts off time, this ISR turns front servo
T1CompB:in save, SREG
sbi PORTD, SVO;enable servo pulse
out SREG, save
reti
;**************************************
;**** TIMER 1 compare A
;counts on time, this ISR turns front servo
T1CompA:in save, SREG
cbi PORTD, SVO;disable servo pulse
out SREG, save
reti
;**************************************
T1ov: in save, SREG
inc count
out SREG, save
reti
;**************************************






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