Full Version : Gurnee & Barabas Autonomous Rover (AVR ASM)
avr >>ROBOTS & AUTONOMOUS VEHICLES >>Gurnee & Barabas Autonomous Rover (AVR ASM)


AVR_Admin- 04-22-2006
Autonomous Vehicle

A Contrast Following Rover

By Reid Gurnee and James Barabas


--------------------------------------------------------------------------------

INTRODUCTION

As technology develops, computers are making people's lives progressively easier and safer. Someday they will be able to drive automobiles, resulting in reduced deaths and accidents. We decided to make a prototype of a self controlled car. We started with a Hot Shot II radio control car and stripped out the radio receiver. Then we gained complete control over the drive and steering servos, allowing complete control of the car via the Atmel AVR8515 micro-controller.



In order to choose a non-predetermined path, we set up an array of sensors that could detect a line against a luminance contrasting surface (ie: black line on white background). The micro-controller then sensed the position of the line relative to the car and steered accordingly. To maintain constant speed, a sixth light sensor detects wheel rotation. The PWM signal to the motor is adjusted to maintain the desired speed through a digital feedback algorithm, obtaining data from the wheel.

When the car looses the line, or gets to the end of the track, it automatically stops and enters a data upload state. The speed and steering data, recorded at 10 samples/second, can be uploaded to a computer and the car's path can be reconstructed in MATLAB.



Features:

Five element sensor array
Complete control of drive and steering servos
Speed feedback to maintain desired speed
Maintains record of speed and steering to reconstruct the course
Can follow dark path on light background or light path on dark background





--------------------------------------------------------------------------------

HARDWARE



Detector circuit for one detector



Detecting the Line:

To detect the line we built an array of five infrared LED’s and sensors. Each LED illuminates the floor, and the sensors detect the light reflectance. The circuit above takes the output of the detectors and turns it into a clear 0V or 5V digital output. An output high indicates a black line below a sensor, and an output low indicates white (higher reflectance surface).

The phototransistor Q1 is connected to a virtual ground through R2. U1 measure the voltage across R3 and invert amplifies it by a factor of 50. From our measurements, the voltage change across R2 from a dark line to the tile varies from .02V to .3V. After the gain stage, this difference is 1V to 15V. To get a digital output this signal is fed into an open loop gain op-amp, U2, with a reference voltage for its other input. This results in a distinct digital output of ±Vrail. LED D2 gives a visual indication of the sensor state, while also acting as a rectifier to prevent the output from dropping below 0V. Zener diode D3 breaks down in output high, clamping the output to 5.1V, resulting in a 0V to 5V digital output independent of the power supply to the op-amps. This allowed us to test using a ±15V lab supply (built in EE 114) to save the cost of expensive 9V batteries. The final 10K resistor, R6, offers current protection against any unforeseen errors.

Because U1 is inverting, the circuit requires a dual polarity power supply. This is achieved with two 9V batteries. The current draw from the batteries is fairly high because they have to drive the board, six infrared LED’s, and six indicator LED’s. To keep power dissipation as low as possible R5 is 10K, resulting in a dim indicator LED but not sacrificing performance. The ideal values for R1 and R2, and the gain of U1 were found through experimentation. We didn't power the infrared LED's on the 8.4V car battery pack because the PWM motor controller resulting in significant switching voltage noise on the battery. This would effect the line sensor readings. We tried powering the micro-controller board on the car battery pack because it has a good, noise resistant power supply, but the motor current draw was too great and the board reset during high acceleration.

We ultimately powered the micro-controller board from the top 9V battery, V2. Because the on-board power supply uses a full-wave diode bridge the board ground is 0.6V above the battery ground. We found it necessary to clamp the board ground to the true ground of the system to eliminate the 0.6V difference, otherwise the servo's didn't work correctly.

The reference voltage allows us to set the threshold where the sensors change from detecting black to detecting white. Because of the high gain and threshold voltages, the sensors were able to detect very small differences in reflectance and output them as digital results. For example, they could differentiate black tape against dark wood. Using only one reference voltage, we experienced minor consistency problems on the five sensors. These were solved by manually selecting gain resistors for U1 to obtain consistent results.







The sensor array is shielded from outside light with a black skirt. Light produced by fluorescent bulbs oscillates at 120 cycles per second. The scope output on the left shows this oscillation being measured by one of the phototransistors. Without light isolation, this oscillation could cause a PWM waveform at the output of U2 because a reference voltage is being compared to a sine-like wave. This effect is depicted in the scope output on the right. The high gain of U1 also helps reduce this PWM effect because it ensures quick transitions.

Speed detection

We used a similar sensor scheme to detect the car’s speed. The inside of the rear right wheel shown above is half white and half black. An LED-sensor pair is mounted facing the wheel. This sensor detects the position of the wheel. By monitoring this sensor over time, we can determine the period of rotation of the car's wheels. The circuit is the same used for line detection, except it has it’s own Vref.





--------------------------------------------------------------------------------

SOFTWARE



AVR 8515

For the control system of the car, we chose the Atmel AVR 8535 micro-controller. Running at 4Mhz, this micro-controller provides us with four 8 pin ports of I/O pins, two timers, a UART, and 512 bytes of sram.

The port pins on the MCU are connected as follows:

PORTA: Connected to 8 status LEDS on development board

PORTB: Pin 0: Connected to control line on steering servo, pin 1: connected to motor controller control line

PORTC: Connected to 8 pushbuttons on development board

PORTD: Pins 0,1: Connected to serial port, pin 2: wheel sensor, pins 3-8: line sensor





Servo Control

We found specs for servo control of the web. Depicted below is the timing diagram. The servo takes a pulse between 1ms and 2ms followed by a 18 to 25ms pause before the next pulse. The pulse width determines the servo position. TIMER1 is ideal for controlling two servos. We found that we needed a 20ms or greater period for the servos to operate correctly. Using TIMER1 with a prescale of 1 only results in a 16ms period, so we had to set the prescaler to 8. The timer is preloaded to 2^16-10000, and both servo controller pins (speed and steering) are set high. When compareA interrupts the speed control pin is pulled low, and when compareB interrupts the steering control pin is pulled low. When the timer overflows it is re-initialized to 2^16-10000 and the pins are pulled high. This results in independent control of both servos using only one timer and interrupts. At any point in our program, we can set the servo position by feeding timer 1 compare match values into four intermediate registers. Next time the timer 1 interrupt is executed, it loads these intermediates into the four timer 1 compare match registers to set servo position.



Steering

The steering position is a direct function of the sensor readout. The table below lists the steering positions and the corresponding sensor readings. The sensor array is samples at 100 times per second, resulting in almost no delay time to send the servo the appropriate reading. In the case of a faulty reading the steering stays on it's last value.

Steering Position
Sensor Reading

Servo Left Max
10000

Servo Left3
11000

Servo Left2
01000

Servo Left1
01100

Servo Center
00100

Servo Right1
00110

Servo Right2
00010

Servo Right3
00011

Servo Right Max
00001




We attempted more complicated steering algorithms. Our first one samples at a constant rate and kept a steering state variable. It added or subtracted from the variable depending on the sensor reading. If the sensor reading was at center, it made no change, regardless of the position of the wheels. As the sensor reading moved towards the extremes, the steering changed by a larger amount in the direction opposite the deviation. This technique did not work as well as the simpler one we implemented.

Getting the steering to function reliably also involved optimizing constants corresponding to steering positions. We needed to make sure that when we entered a curve of a certain radius, the steering adjusted to close to that radius. When steering is configured in this way, the car does not oscillate as much between steering positions when it is on a curve. Currently, the steering positions are separated by a constant servo angle. The maximum steering positions were carefully selected to correspond to the physical steering limits of the car.

In order to get as much information as possible, the width of the line needs to be just larger than the spacing between two of our sensors (about 2"). The car can follow narrower lines, but it can steer more precisely if it is able to detect the line with two sensors at once. If the line is thinner than the spacing between two sensors, the steering can only switch to every other entry in the above lookup table (the entries with only one 1) .

When the sensor reads all 0's, we start a countdown timer. If this reading remains for .5sec, the car applies the brakes (shorting the terminals of the motor), and prepares to upload it's recorded data.





Speed Control

This car was originally a high end race car. It has competed, and won, many professional races. This basically means one thing: It's very fast. Its top speed is around 35 mph. Since our ideal detection speed is in the range of 5 to 7 mph, it was necessary to implement a feedback control system to monitor and adjust its speed on the track.

As described and depicted above, the inside of the wheel is half black, half white. Every 10ms, increments a wheel period counter the car looks at the wheel sensor. If the sensor has moved from white to black, the counter is stopped, saved, and reset. When this occurs, the program looks up the period in a table and determines the car's speed. If the reading is lover than some threshold, it is assumed that there was some bounce in the sensor transition, and the reading is discarded. Once the speed has been determined, the program compares it to the desired speed. If the car's velocity is significantly different form the desired rate, the code adjusts the motor torque accordingly. Additionally, when the sensor has not changed state in .75 sec, a speedup is invoked. Using this speedup, the car starts applying no power to the motor, and increases this power until it reaches its desired speed.

Currently, the speed feedback is a little coarse. The feedback system often overcompensates for changes in speed (particularly by panicking when it starts going too fast, coming to a complete stop). To compensate, we included a minimum time between speed changes of .5 sec. This seems to work well, but the vehicle will still occasionally slow down too much, and stop.



Data

The car stores every tenth sensor reading into sram (10/sec). When the end of ram is reached, the car continues to run, but stops trying to save it's sensor readings. After the car has reached the end of the line, or the halt button has been pressed, it sends all of the recorded data though the serial port. The MCU then waits for a g character on the serial port, and upon receipt, sends the data again. The data is stored and sent as follows: The high byte contains the steering position. A 2 indicates full left and a 10 indicates full right. A zero indicates an invalid reading, indicating that the previous steering position is still valid. The low byte of the stored data contains the car's speed on the last table lookup. A zero indicates the car is stopped, and a 5 indicates about 5 MPH.

To import this data, we wrote a C program. This program sends a 'g' to the MCU, and records the incoming stream. It separates the incoming data bytes into space separated digits in Matlab file import format.

To plot this data, we wrote a short Matlab fragment. Below is a sample output.



Results

Overall, the car far outperformed our expectations. It could easily follow a black tape line on a tile floor at moderate speeds. It could also perform in non-ideal conditions. Shown above is a white line spray painted on black pavement. The line varies in width and contrast, and the background pavement is not a consistent black but a light non-uniform light gray. The robust detection circuit was also able to sense and follow this line.

The speed feedback control fell short of our expectations. We had trouble fine tuning it to obtain a consistent speed. The car would often come to a stop in the middle of the track then resume again after it detected it had stopped. The speed of the car can be easily seen from the MATLAB plot above. The data is taken at a sample rate of 10 samples/sec. The car stopped or slowed down where the data is bunched together.


--------------------------------------------------------------------------------

If we could do it again

Throughout the design process we continually redesigned our hardware and software to optimize the car. For example, the sensor detection circuit has gone through at least four revisions. Because we continually redesigned the project, we came out with a final product that needs few changes. Our biggest problems were with the speed feedback control. If we could redo that we would put more color changes on the inside of the wheel so we know more frequently how fast the car is going. If we had more time, we also would have soldiered the circuit. Though the circuit layout is fairly robust, we occasionally have problems with wires coming loose.

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


AVR_Admin- 04-22-2006
CODE

;*****************************Servo test*********************************
;.include "r:\avrtools\appnotes\4414def.inc"
;.include "c:\james\avrtools\appnotes\4414def.inc"
.include "c:\avrtools\appnotes\4414def.inc"
;.include "z:\jdb30\476\avrtools\appnotes\4414def.inc"
;.include "d:\avrtools\avrtools\appnotes\4414def.inc"
.device AT90S8515; specifies to the assembler which chip we are using

;port B0 = steering     -> t1 match a
;port B1 = drive    -> t1 match b

;port D connected to sensors, serial port
;port A to LED's

;port C to buttons

;servo max clockwise -> .35ms
;servo max counterclockwise -> 2.15 ms

.def    save        =R1    ;used to save sreg
.def    wheelstate  =R2;state of the brightness sensor on wheel
.def    speed       =R3;current speed of car
.def    torque      =R4;current power setting on motor

.def    speedup     =R5;flag contining contextual speedup
.def    steernum    =R6;steering position index

.def    speednum    =r7;speed position index

.def    speedlag    =r8;timer to pause between speed changes

.def    temp        =R16   ;temporary
.def    steerposLo  =R17   ;Preload value for timer1 matchA
.def    steerposHi  =R18   ;    "
.def    driveposLo  =R19   ;Preload value for timer1 matchB
.def    driveposHi  =R20   ;    "
.def    inpin       =R21   ;input pins
.def    count       =R22   ;software counter for timer 1
.def    itemp       =R23   ;interrupt temp
.def    lastpin     =R24   ;last state of input pins
.def    interrupt   =R25   ;timer 1 counter overflow interrupt flag
.def    invert      =R26   ;do we have a white or black line?
.def    stop        =R27   ;counter to kill car if we deviate from line for too long
.def    sample      =R28   ;sample counter for data sampling
.def    speedctr    =R29   ;counter to determine period of wheel rotation

.def    ZL      =R30   ;Z registers for memory storage
.def    ZH      =R31

;************************these get re-used in serial stage*********
.def    TXbusy      =r28   ;transmit busy flag
.def    RXchar      =r29   ;a received character
.def    TXflash     =r27   ;is transmit from flash?

;************************reused in wait stage***********************
.def    LED     =r28   ;holds status of LED's

;************************timer equates******************************

.equ    t0time      =20 ;->10ms
.equ    t0preload   =0x06

;************************memory equates*****************************
.equ    firstindex  =0x0067;first memory index to write
.equ    datastream  =0x0067;   "
.equ    lastindex   =0x0240;last memory index to write
.equ    numsamples  =490   ;number of samples to take
.equ    samplerate      =10;how many samples go by between loggings
;************************serial equates*****************************
.equ    baud96  =25;9600 baud constant for 4Mhz crystal
.equ    go  ='g'   ;0x67 ascii 'g'
.equ    azero   ='0'   ;0x30 ascii '0'



;************************equates for servo positions****************

.equ    preload     =0xFFFF - 10000

.equ    srvLmax     =500 * (82 + 0*10)/100 + preload

.equ    srvL3       =500 * (82 + 1*10)/100 + preload

.equ    srvL2       =500 * (82 + 2*10)/100 + preload

.equ    srvL1       =500 * (82 + 3*10)/100 + preload

.equ    srvC        =500 * (83 + 4*10)/100 + preload

.equ    srvR1       =500 * (83 + 5*10)/100 + preload

.equ    srvR2       =500 * (83 + 6*10)/100 + preload

.equ    srvR3       =500 * (83 + 7*10)/100 + preload

.equ    srvRmax     =500 * (84 + 8*10)/100 + preload


;****drive speeds
.equ    neutral     =500 * 109/100 + preload

.equ    brake       =500 * 85/100 + preload

.equ    slow        =500 * 120/100 + preload
.equ    med         =500 * 120/100 + preload



.equ    go1     =500 * 112/100 + preload
.equ    go2     =500 * 113/100 + preload
.equ    go3     =500 * 114/100 + preload
.equ    go4     =500 * 115/100 + preload
.equ    go5     =500 * 116/100 + preload





;**********speeds in samples between rotations******
.equ    speedtoolow = 10;less than this, and we consider it sampling noise
.equ    speed5      = 48
.equ    speed4      = 51
.equ    speed3      = 54
.equ    speed2      = 67
.equ    speed1      = 60
.equ    speed0      = 63
.equ    speedtoohigh    = 75;greater than this, and we are stopped


;a desired speed:
;10 changes in 2.3 sec
;5 revolutions in 2.3 sec (46)

.equ    speeduplag  =50;(in ms) min time to wait between speed adjustments

;************************data segment**********************
.dseg

;define variable strings to be tranmitted from RAM
;datastream: .byte numsamples


;************************code segment*********************
.cseg
.org $0000
   rjmp    RESET      ;reset entry vector
   reti        
   reti
   reti
   rjmp    t1ca       ;timer 1 compare a
   rjmp    t1cb       ;timer 1 compare b
   rjmp    t1ovfl     ;timer 1 overflow
   rjmp    t0ovfl     ;timer 0 overflow
   reti
   rjmp    RXdone     ;UART receive done
   rjmp    TXempty    ;UART buffer empty
   rjmp    TXdone     ;UART transmit done
   reti    



;**********************string constants**************
crlf:   .db 0x00, 0x0d, 0x0a, 0x00 ;null, carrage return/line feed
;**********************speed setting subroutines***********************

;*************this routine looks at the wheel-turn period,
      ;and translates it into a speed index by lookup.
      ;leaves result in temp
_getspeed:
   mov temp,speedctr      ;load the current wheel-turn period
   cpi temp,speedtoolow
   brsh    nottoosmall
   mov     temp,speed     ;use current speed if time to turn wheel was too small
   ret
   
nottoosmall:    
   cpi temp,speed5;Higher speeds here
   brsh    notget5
   ldi     temp, 5    ;period is less than speed5, store a 5
   ret
notget5:
   cpi temp,speed4
   brsh    notget4
   ldi     temp, 4
   ret
   
notget4:
   cpi temp,speed3
   brsh    notget3
   ldi     temp, 3
   ret
notget3:
   cpi temp,speed2
   brsh    notget2
   ldi     temp, 2
   ret
notget2:
   cpi temp,speed1
   brsh    notget1
   ldi     temp, 1
   ret
   
notget1:
   cpi temp,speed0    ;slowest speed
   brsh    notget0
   ldi     temp, 0
   ret
   
notget0:
   ldi     temp, 0
   ret
;*************************
                  ;increase speed by 1

_incspeed:
   mov temp,torque
   inc temp
   cpi temp, 6        ;make sure we don't go over 5
   brlo    noincovfl
   ldi temp,5
noincovfl:
   mov torque,temp    ;set new torque
   rjmp    settorque
;*************************

                  ;decrease speed by one
_decspeed:
   mov temp,torque
   dec temp
   cpi temp,0         ;make sure we don't go under 0
   brsh    nodecundfl
   ldi temp,0
nodecundfl:
   mov torque,temp    ;set new torque
   rjmp    settorque
;*************************
;get current speed, and use it to set the motor ctlr.

_setspeed:
   rcall   _getspeed  ;get current speed index from function call
   mov speednum,temp  ;save current speed index to write to ram
   cp  temp,speed ;temp holds current speed, speed holds desired speed
   breq    settorque  ;if we're at our goal speed, don't change
   brlo    _incspeed  ;otherwise,speed up or slow down
   rjmp    _decspeed

settorque:         ;if speedup is on, we load the torque that is one faster.
   mov     temp,speedup    
   cpi     temp,0xFF
   brne    loadtorque
   
loadtorqueplus1:
   mov temp,torque
;   inc temp
   rjmp    doneloadtorque
loadtorque:
   mov temp,torque
doneloadtorque:


   cpi     temp,0     ;from here, we take an desired torque, and set the motor
   brne    notsp0
   
   ldi driveposHi, high(neutral)  
   ldi driveposLo, low(neutral)    

notsp0:
   cpi temp,1
   brne    notsp1
   
   ldi driveposHi, high(go1)  
   ldi driveposLo, low(go1)        
   
notsp1:
   cpi temp,2
   brne    notsp2
   
   ldi driveposHi, high(go2)  
   ldi driveposLo, low(go2)    

   
notsp2:
   cpi temp,3
   brne    notsp3
   ldi driveposHi, high(go3)  
   ldi driveposLo, low(go3)    
   
notsp3:
   cpi temp,4
   brne    notsp4
   ldi driveposHi, high(go4)  
   ldi driveposLo, low(go4)    

notsp4:
   cpi temp,5
   brne    notsp5
   ldi driveposHi, high(go5)  
   ldi driveposLo, low(go5)    

notsp5:
   ret


;*******************initial setup*******************
RESET:
   ldi temp, LOW(RAMEND)      ;setup stack pointer
   out     SPL, temp
   ldi temp, HIGH(RAMEND)
   out SPH, temp
   
  ;*****variable init
   ldi     steerposLo, low(srvC)      ;init servo position to max left
   ldi     steerposHi, High(srvC)     ;

   ldi driveposHi, high(neutral)  ;init motor to neutral
   ldi driveposLo, low(neutral)       ;  

   clr invert             ;don't invert input by default

  ;*******ports
   ser     temp
   out     DDRB, temp         ;set port b to all outputs (pins 0,1 control servos)

   ser temp
   out     DDRA, temp         ;set port A to all outputs for LED's
       
   clr     temp
   out     DDRD, temp         ;set port d to all inputs (sensor array)
   
   clr temp
   out     DDRC, temp         ;set port C to all inputs (buttons)

  ;******TIMSK
   in  temp, TIMSK
   ori     temp, 0b11100010       ;activate t1 overflow, t1 compare a & b,0,0,0,t0overfl,0 int's
   out     TIMSK, temp
   
  ;******timer0
   ldi     Temp, 2            ;prescale timer 0 by 8
   out     TCCR0, Temp            
   ldi     count,t0time

  ;******timer1
  ;set timer 1 prescale to 1, start timer 1
  ;2^16 ticks / 4MHz = 16.384ms between overflows
   in  temp, TCCR1B
   andi    temp, 0b11110000
   ori     temp, 0b00000010       ;we just want to set the last nibble
   out     TCCR1B, temp
   rjmp    waitloop
   
getspeed: rjmp _getspeed
setspeed: rjmp _setspeed
incspeed: rjmp _incspeed
decspeed: rjmp _decspeed


;0******************************get input state & go signal******************
;this phase displays the status of the invert bit in led0.

waitloop:
   ser     LED        ;all LED's off

   mov temp,   invert
   com     temp           ;drop in invert bit
   andi    temp,   0b00000001
   com temp
   and LED,    temp
   out PORTA,  LED    ;display status bits to LEDs
   in  inpin,  PINC    

   cpi inpin,0b11111110   ;pressing button 0 sets invert
   brne    noinverton
   clr invert
noinverton:
   cpi inpin,0b11111101   ;pressing button 1 clears invert
   brne    noinvertoff
   ser invert
noinvertoff:
   cpi inpin,0b11111011   ;pressing 2 starts steering only (desired speed=0)
   brne    nospeed2
   ldi temp,0
   mov speed,temp
   rjmp    enterdrive
nospeed2:
   cpi inpin,0b11110111   ;pressing 3-7 starts car with desired speeds 1-5
   brne    nospeed3
   ldi temp,1
   mov speed,temp  
   rjmp    enterdrive
nospeed3:
   cpi inpin,0b11101111
   brne    nospeed4
   ldi temp,2
   mov speed,temp  
   rjmp    enterdrive
nospeed4:
   cpi inpin,0b11011111
   brne    nospeed5
   ldi temp,3
   mov speed,temp  
   rjmp    enterdrive
nospeed5:
   cpi inpin,0b10111111
   brne    nospeed6
   ldi temp,4
   mov speed,temp  
   rjmp    enterdrive
nospeed6:
   cpi inpin,0b01111111
   brne    nospeed7
   ldi temp,5
   mov speed,temp  
   rjmp    enterdrive
nospeed7:
   rjmp waitloop  

   

enterdrive:                ;get ready to drive car
  ;******memory
   ldi     ZL,low(datastream)     ;set memory base index
   ldi ZH,high(datastream)
  ;*********variable init
   ldi     lastpin, 0b11111111    ;init inpins
   ldi sample, samplerate     ;init samplerate
   clr temp
   mov torque,temp        ;init torque to 0
   mov speedup, temp          ;init speed boost to off
   mov speednum,temp          ;init initial speed index to 0
   ldi temp,5              
   mov steernum,temp          ;start steering indexer at "centered"
   ldi temp,speeduplag
   mov speedlag,temp          
   
  ;********get state of wheel
   in  inpin,PIND
   ori inpin,0b00000100       ;mask out speed feedback bit
   cpi inpin,0b00000100
                      ;initalize state of wheel sensor
   clr temp
   mov wheelstate,temp
                      ;set wheelstate to 0 (sensor sees black band)
   breq    white
   ser     temp
   mov wheelstate,temp        ;set it to FF if wheel is black
white:

   
   
   
   sei                ;enable interrupts  
   clr stop               ;clear line loss stop counter
;0*****************************drive car and get data************************
loop:   ser     temp           ;on interrupt, break out of loop
   cp  temp,interrupt
   brne    loop
   
   clr interrupt      ;reset interrupt flag
   
   
;**********deal with car speed  
   inc speedctr       ;inc wheel period counter

   mov temp, speedlag
   cpi temp,100       ;see if speedlag has overflowed (if so, we can change
   brlo    donewheel      ;speeds again)

   ldi temp,200       ;once we have overflowed, hold lag at 200 (>100)
   mov speedlag,temp

   cpi speedctr,speedtoohigh  ;if we haven't moved in a long time, invoke speedup
   breq    dospeedupdatebctooslow
   
   mov temp,wheelstate
   in  inpin, PIND
   andi    inpin, 0b00000100  ;get wheel feedback bit
   cpi inpin, 0b00000100
   breq    checkwheeltrue

checkwheelfalse:           ;check for low to high transitions
   cpi temp, 255      ;wheel sensor on white
   breq    wheelstatechangedupdate
   rjmp    donewheel
checkwheeltrue:
   cpi temp,0         ;wheel sensor on black
   breq    wheelstatechanged
   rjmp    donewheel
dospeedupdatebctooslow:        ;we weren't moving, so speed up
   rcall   setspeed
   clr speedctr       ;reset wheel period counter
   ldi temp,speeduplag    ;reset lag counter so we don't speed up again too soon
   mov speedlag,temp
   rjmp    donewheel

wheelstatechangedupdate:
   rcall   setspeed       ;we had a low to high transition on the wheel sensor.
                  ;time to update torque, speed
   clr speedctr       ;start period counter again
   
   ldi temp,speeduplag    ;don't change speeds again too soon.
   mov speedlag,temp
wheelstatechanged:
   com wheelstate
donewheel:
   dec speedlag

;*********deal with steering***********
   mov     lastpin, inpin     ;back-up last reading
   in  inpin, PIND    ;get sensor data



   cpi invert,0
   breq noinvert
   com     inpin          ;invert(if user requested it)
noinvert:


   andi    inpin, 0b11111000  ;ignore low 3 bits
   ori inpin, 0b00000111  ;we'll need them for the serial port & speed

   out     PORTA, stop    ;send sensors to LEDs  

   rcall   srvcase        ;get sensor reading & set servoindex accordingly


   ldi temp, high(datastream+numsamples)  ;if we are out of memory,
   cpi ZH,low(datastream+numsamples)
   cpc ZL,temp
   brlo    loopexitconds              ;don't store anything

   dec sample                 ;we only record every samplerate th entry
   cpi sample, 0              ;record smaples when "sample" reg. underflows
   brne    loopexitconds
   ldi sample, samplerate

   mov temp, steernum             ;store steering in high nibble
   swap    temp                    
   andi    temp, 0b11110000           ;store speed in low nibble
   or  temp, speednum
   st  Z+, temp               ;write byte into ram



loopexitconds:
   sbis    PINC,0         ;break out of loop on pin 7
   rjmp enddrive

   cpi stop, 50
   brsh enddrive          ;break out of loop if line lost


   rjmp loop  

enddrive:
;***********cleanup from drive

   ldi temp, 0
   st  Z+, temp           ;null terminate the data stream in memory
  ;display checkerboard on led's
   ldi temp,0b01010101
   out PORTA,temp

  ;**********apply brakes for 1 sec  
   ldi driveposLo,low(brake)  
   ldi driveposHi,high(brake)

   ldi     inpin, 255-100         ;use inpin as a counter
loopstop:  
   ser     temp               ;on interrupt,inc soft counter
   cp  temp,interrupt          
   brne    loopstop
   
   clr interrupt
   
   inc inpin              ;inc counter
   breq    donebraking        ;finish on overflow
   rjmp    loopstop

donebraking:


   cli            ;turn off all interrupts

   clr temp
   out TIMSK, temp        ;turn off all timers


;0****************************wait for g on serial port and download***************

   clr TXbusy     ;start out not busy on TX
   ldi RXchar, go ;send all of the data immediately


  ;setup UART -- enable TXempty & RXdone int, and RX, TX pins
   ldi     temp, 0b10111000
   out     UCR, temp
  ;set baud rate to 9600
   ldi temp, baud96
   out UBRR, temp
  ;intialize text pointer BEFORE turning on interrupts
  ;because RESET causes the TX empty flag to be SET
   ldi ZL, LOW(crlf<<1);do shift to convert word-addr to byte
   ldi     ZH, HIGH(crlf<<1)

   sei

;******
;now dump data whenever we get a g key on the serial port
TXloop: cpi     RXchar, go ;wait for go signal - a 'g' on the keyboard
   brne    TXloop

   
  ;now the pointer to the variable message in RAM
   ldi ZL, LOW(datastream) ;ptr to RAM
   ldi ZH, HIGH(datastream)
   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
   
                  ;send a null,cr,lf,null to end sequence
  ;setup ptr for <crlf> string
   ldi ZL, LOW(crlf<<1) ;shifted becuase pgm memory is words
   ldi ZH, HIGH(crlf<<1)
   lpm
   out UDR, r0    ;trigger the UART TX
   ser TXflash    ;text string in flash memory
   ser TXbusy     ;and set the TX busy flag
   sbi UCR, UDRIE ;enable the TXempty interrupt
   rcall   TXwait

   clr RXchar
   rjmp    TXloop




   
   
               
;*******************************sensor reading case statement********************


;note: in the code, these binary numbers are the mirror of what the car sees.

srvcase:
   cpi inpin,0b11110111
   brne    notc4          ;about to lose line. steer left hard

   clr     stop           ;we see the line again, so we don't need to stop.

   ldi     steerposLo,low(srvLmax);put timer1match vaules in registers
   ldi     steerposHi,high(srvLmax)
   
   ldi temp, 1 + 1    ;matlab will see this as a 1    
   mov steernum, temp
   
   ret

notc4:  
   cpi inpin,0b11100111
   brne    qnotc4         ;steer left 2

   clr     stop        

   ldi     steerposLo,low(srvL3)
   ldi     steerposHi,high(srvL3)

   ldi temp, 2 + 1
   mov steernum, temp

   ret
   
qnotc4:
   cpi inpin,0b11101111
   brne    anotd4         ;steer left 1

   clr     stop

   ldi     steerposLo,low(srvL2)
   ldi     steerposHi,high(srvL2)

   ldi temp, 3 + 1
   mov steernum, temp

   ret

anotd4:
   cpi inpin,0b11001111
   brne    inpin4         ;just left of center.

   clr     stop

   ldi     steerposLo,low(srvL1)
   ldi     steerposHi,high(srvL1)

   ldi temp, 4 + 1
   mov steernum, temp

   ret

inpin4:
   cpi inpin,0b11011111   ;dead center.
   brne    anotf4

   clr     stop

   ldi     steerposLo,low(srvC)
   ldi     steerposHi,high(srvC)

   ldi temp, 5 + 1
   mov steernum, temp

   ret



anotf4:
   cpi inpin,0b10011111
   brne    notf4          ;just right of center  

   clr     stop

   ldi     steerposLo,low(srvR1)
   ldi     steerposHi,high(srvR1)

   ldi temp, 6 + 1
   mov steernum, temp

   ret

notf4:  
   cpi inpin,0b10111111
   brne    rnotf4         ;steer right 1

   clr     stop

   ldi     steerposLo,low(srvR2)
   ldi     steerposHi,high(srvR2)

   ldi temp, 7 + 1
   mov steernum, temp

   ret

rnotf4:
   cpi inpin,0b00111111
   brne    notg4          ;steer right 2

   clr     stop

   ldi     steerposLo,low(srvR3)
   ldi     steerposHi,high(srvR3)

   ldi temp, 8 + 1
   mov steernum, temp

   ret
   
notg4:  
   cpi inpin,0b01111111
   brne    nota4
          ;about to lose line. steer right hard
   clr     stop

   ldi     steerposLo,low(srvRmax)
   ldi     steerposHi,high(srvRmax)

   ldi temp, 9 + 1
   mov steernum, temp

   ret



;these cases do not change steering

nota4:  cpi inpin,0b00011111       ;slow down
   brne    xnota4             ;this reading turns off speed boost
   clr temp
   mov speedup,temp

   ret
xnota4:
   cpi inpin,0b11000111       ;speed up
   brne    znota4             ;this reading turns on speed boost
   ser temp
   mov speedup,temp

   ret        
   
znota4:                    
   cpi inpin,0b11111111       ;we can't see the line.
   brne    notb4              ;maintain previous heading
           
   inc     stop               ;when stop reaches 50, we will stop.
   
   ldi temp, 0 + 1
   mov steernum, temp

   ret
notb4:      
   ldi temp, 0 + 1        ;default case
   mov steernum, temp         ;maintain heading
   ret




nowrite:
   ret


****************************interrupt routines*****************************

; UART needs a character
TXempty:in  save, 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, save ;restore proc status
   reti           ;back to pgm

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

; UART read a character
RXdone: in  save, SREG ;save processor status  
   in  RXchar, UDR;get the character
   out SREG, save ;restore proc status
   reti           ;back to pgm
;**********************Timer 1 overflow**************
;brings two servo lines hi, reloads servo position values
;
;_______|-|____
;      /\
;       |
;   overflow here
t1ovfl:
   in save, SREG          ;save status reg
   sbi     portb,0        ;set steering servo line hi
   sbi     portb,1        ;set drive servo line hi
   
   ldi     temp, high(preload)
   out TCNT1H, temp
   ldi temp, low(preload)
   out TCNT1L, temp

   out     OCR1AH, steerposHi ;load steering position for next sample
   out     OCR1AL, steerposLo
   out     OCR1BH, driveposHi ;load drive positon for next sample
   out     OCR1BL, driveposLo

   out     SREG, save     ;restore status register
   reti

;********************Timer0 overflow*****************
t0ovfl:
   in  save, SREG
   
   dec count          ;keep track of # of intr
   brne    notyet         ;=100 millsec? then:
   
   ser interrupt      ;signal code to scan
   
   ldi count, t0time          ;reset count for next 100 mSec period
notyet: ldi itemp, t0preload   ;preload timer=250 counts till overflow
   out     TCNT0, itemp       ;250x8x.25 microSec = 0.5mSec.

   
   out     SREG, save
   reti
;********************Timer 1 compare A***************
;drops steering servo line low after correct delay
t1ca:
   in  save, SREG     ;save status reg
   cbi     portb,0        ;set steering servo line low
   out     SREG, save     ;restore status reg
   reti

;********************Timer 1 compare B***************
;drops drive servo line low after correct delay
t1cb:
   in  save, SREG     ;save status reg
   cbi portb,1        ;set drive servo line low
   out SREG,save      ;restore status reg
   reti


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

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

AVR_Admin- 04-22-2006
CODE

#include <stdio.h>
#include <windows.h>
#include <commctrl.h>


///////////////////////////////////////////////////////
BOOL OpenPort();
BOOL ClosePort();
BOOL SetupPort();
unsigned char ReadSByte();
BOOL SendSByte(unsigned char);
BOOL WriteFByte(unsigned char);
BOOL OpenIfile();
BOOL CloseIfile();
unsigned char ReadFByte();
BOOL TransferFile();
BOOL SetAddr(INT baseAddr);
BOOL DownloadFile();
///////////////////////////////////////////////////////



//globals//////////////////////////////////////////////
//
//  Port name
//
char gszPort[255] = "COM2";
char ifileName[255] = "c:\\temp\\data.ext";
//
//  Port handle
//
HANDLE hComm;
//
//  Image file handle
//
HANDLE hFile;





//////////////////////////////////////////////////
//
//  Main
//
int main(int argc, char* argv[]) {

   if (argc == 3){
       strcpy(gszPort,argv[1]);
       strcpy(ifileName, argv[2]);
   } else if (argc == 2) {
       strcpy(ifileName, argv[1]);

   } else {
       printf("syntax: getdata.exe [potrnum] filename.ext\n");
       return 0;
   }
   printf("running...\n");
   
   if( OpenPort() && OpenIfile() && SetupPort()) {

       DownloadFile();
   }
   
   ClosePort();
   
   CloseIfile();
   
   return 1;
}
//Serial Functions////////////////////////////////
//
//  OpenPort
//
BOOL OpenPort() {

   hComm = CreateFile( gszPort,                    // pointer to name of the file
                    GENERIC_READ | GENERIC_WRITE,  // access (read-write) mode
                    0,                             // share mode
                    0,                             // pointer to security attributes
                    OPEN_EXISTING,                 // how to create
                    0,                             // file attributes
                    0);                            // handle to file with attributes to copy


   if (hComm == INVALID_HANDLE_VALUE) {
      printf("failed to open serial port %s \n",gszPort);
      return 0;
   } else {
       printf("serial port %s opened \n",gszPort);
       return 1;
   }
}

//
//  ClosePort
//

BOOL ClosePort() {
   if (CloseHandle( hComm )) {
       printf("Port closed\n");
       return 1;
   } else {
       printf("Port close failed\n");
       return 0;
   }
}

//
//  SetupPort
//

BOOL SetupPort() {
 
   DCB dcb;


   //printf("setting up DCB\n");
   //FillMemory(&dcb, sizeof(dcb), 0);   //initalize dcb
   //dcb.DCBlength = sizeof(dcb);
   printf("getting DCB\n");

   if (!GetCommState(hComm,&dcb)) {
      printf("getDCB failed\n");
       return 0;
   }
   
   dcb.BaudRate = 9600;
   dcb.fParity = FALSE;
   dcb.Parity = NOPARITY;
   dcb.StopBits = ONESTOPBIT;
   dcb.ByteSize = 8;
   dcb.fOutxCtsFlow = FALSE;
   dcb.fOutxDsrFlow = FALSE;
   dcb.fDtrControl = DTR_CONTROL_DISABLE;
   dcb.fRtsControl = RTS_CONTROL_DISABLE;

//  if (!BuildCommDCB("9600,n,8,1", &dcb)) {  
//      printf("Port configuration failed\n");      
//      return FALSE;  
//  }
   printf("DCB ready for use\n");
   if (!SetCommState(hComm, &dcb)) {
       printf("failed to set port state (%d)\n",GetLastError());
       return 0;
   } else {
       printf("Port setup complete\n");
       return 1;
   }
}



//
//  SendSByte
//
BOOL SendSByte(unsigned char byteToWrite) {
   DWORD dwWritten;
   if(WriteFile(hComm, &byteToWrite, sizeof(byteToWrite), &dwWritten, 0)) {
       printf("wrote byte %Xh (%c) to serial port\n", byteToWrite,byteToWrite);
       return 1;
   } else {
       printf("serial port write failed\n");
       return 0;
   }
}

//
//  ReadSByte
//
unsigned char ReadSByte() {
   DWORD dwRead;
   unsigned char lpBuf;


      ReadFile(hComm,              // handle of file to read
          &lpBuf,                  // address of buffer that receives data
          sizeof(lpBuf),           // number of bytes to read
          &dwRead,                 // address of number of bytes read
          0);                      // address of structure for data
       printf("Read byte %Xh from serial port\n",lpBuf);
       return lpBuf;
}


//Image file functions///////////////////////////////////////////

//
//OpenIfile
//

BOOL OpenIfile() {

   hFile = CreateFile( ifileName,                  // pointer to name of the file
                    GENERIC_READ | GENERIC_WRITE,  // access (read-write) mode
                    0,                             // share mode
                    0,                             // pointer to security attributes
                    CREATE_ALWAYS,                 // how to create
                    0,                             // file attributes
                    0);                            // handle to file with attributes to copy


   if (hFile == INVALID_HANDLE_VALUE) {
      printf("failed to open image file\n");
      return 0;
   } else {
       printf("image file %s opened\n",ifileName);
       return 1;
   }
}

//
//  CloseIfile
//
BOOL CloseIfile() {
   if (CloseHandle( hFile )) {
       printf("File closed\n");
       return 1;
   } else {
       printf("File close failed\n");
       return 0;
   }
}


//
//  ReadFByte
//
unsigned char ReadFByte() {
   DWORD dwRead;
   unsigned char lpBuf;


      ReadFile(hFile,              // handle of file to read
          &lpBuf,                  // address of buffer that receives data
          sizeof(lpBuf),           // number of bytes to read
          &dwRead,                 // address of number of bytes read
          0);                      // address of structure for data
       printf("Read byte %Xh from ifile\n",lpBuf);
       return lpBuf;
}


//
//  WriteFByte
//

BOOL WriteFByte(unsigned char c) {
   DWORD dwBytesToWrite;
   DWORD dwWritten;
   unsigned char buff[2];

   buff[0] = c;
   buff[1] = ' ';
   
   dwBytesToWrite = 2*sizeof(unsigned char);
   WriteFile(hFile,                    // handle to file to write to
       buff,                           // pointer to data to write to file
       dwBytesToWrite,                 // number of bytes to write
       &dwWritten,                     // pointer to number of bytes written
       0);                             // pointer to structure for overlapped I/O);
   
   if (dwWritten == dwBytesToWrite)
       return 1;
   else
       return 0;

}



//
//  TransferFile
//

BOOL TransferFile() {
   int i;
   int fileSize = 100;
   int startAddr = 0;

   SetAddr(startAddr);
   for (i=0;i<fileSize;i++) {
       SendSByte('w');
       SendSByte(ReadFByte());        
   }
   return 1;
}

//
//DownloadFile
//
BOOL DownloadFile() {
   unsigned char thechar;
   unsigned char thespeed;
   unsigned char thesteering;
   SendSByte('g');
   while ((thechar = ReadSByte()) > 0x0)
   {
       thesteering = (((thechar & 240)>>4)+'0'-1); //steering 0 maps to 0b0001XXXX
       thespeed = (((thechar & 15)+'0'));      //speed 0 maps to 0bXXXX0000
       
       WriteFByte(thespeed);
       WriteFByte(thesteering);
   }
   return 1;
}

//
//  SetAddr
//  (Not yet functional: Always starts at address 0)

BOOL SetAddr(INT baseAddr) {
   int i;
   SendSByte('a');
   for (i=0;i<8;i++) {
       SendSByte('0');
   }
       return 1;
}



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