Note: the GP2D02 is no longer supported by Sharp. However, the linearisation formulas, below, will still work with the analogue units.
Connecting and reading the sensors
To conserve I/O ports on the Atmel 8515 AVR processor, I multiplexed the sensors by using one I/O bit to be the control line for all sensors and connecting the three return data lines to bits 0, 1 and 2 of an I/O port. The software initiates a reading by dropping the control line, then it polls the three data lines waiting for all three to become 1's (0x07 in my case), then it clocks the data into the CPU. Since the three sensors are connected to sequential bits of a port, the software simply rotate the bits through the carry bit and into their respective destination bytes.
Note, the Sharp sensors require 3v logic on the input pin,, hence R1 and R2.
Linearizing and calibrating the output of the sensors
The sensors have an output curve that is the inverse of the distance of the target. The farther away the target, the lower the output value. The following chart is from the Sharp GP2D02 Data Sheet.
It turns out that the curve has a 1/X relationship. In fact, the formula D = Kg/(X-Ko), where X is the output of the sensor, can give very accurate values for distance. This calibration is easy to perform: it involves one division and one subtract. On Dilbert, the results are calculated in 10th of an inch and returned in a byte value, so the maximum distance that can be measured is ~25".
Since the calibration calculation only involves two constants, one can figure out what those are using two measurements from the unit. For details on how I got these formulas, see Derivation Of GP2D02 linearization formulas.
For the linearization formula
D = Kg/(X-Ko)
Kg is the gain, or overall shape of the curve described by Kg/X
Ko is an offset, shifting the curve up or down.
Let D and X be the distance and output, respectively of your first measurement.
Let D' and X' be the distance and output, respectively, of the second measurement.
Kg = (X'-X) D'D/(D-D')
Ko = (D'X' - DX)/(D' - D)
For Dilbert, I measured the sensor output at 5" and 20", respectively. That translates into an X and X' of 50 and 200 (10th of an inch). I needed to calibrate each sensor separately. Below are the results for the three sensors on Dilbert. The values for Kg and Ko are similar, but different enough to warrant individual calibration.
hardware.inc
Code: Select all
;
; Heuristic values for our sensors. The output
; is =XXXX_FG/(SensorOutput-XXXX_FO)
;
; These values calibrate the sensor cooked output in 1/10's
; of an inch
;
FRONT_KG DEFINE 0x1DB0
FRONT_KO DEFINE 0x11
RIGHT_KG DEFINE 0x1CA5
RIGHT_KO DEFINE 0x25
LEFT_KG DEFINE 0x1B9B
LEFT_KO DEFINE 0x15
Physical mounting issues
The case of the sensors is a conductive plastic material. Don't believe me, check it out with an ohm meter. To insure reliable, noise free measurements, make sure you ground the case. If the case is not grounded and there is no target, or the return signal is weak, the sensor sometimes "thinks" there is a target and that it is wandering around. Before I put in a grounded copper (pcb) top plate on Dilbert, the distance sensors would measure anything from 15-25" when there was no target (looking down a long hallway in the fire fighting case). Grounded the sensors became much more stable with reading no target, or targets at long distances (e.g. > 36" away).
A second issue is the mounting orientation. At first glance it seems like horizontal makes sense. However, as in the Fire Fighting contest, if you point the sensor towards a corner that is reflective, you can get false readings when the sensor picks up the reflected signal. This was expressed by Dilbert thinking he was much farther away from a wall or end of passage than he really was when nearing or going around corners. The solution to this problem is to mount the sensors in the vertical direction.
Software
The following software is for the Atmel AVR processor and is written to the IAR v1.40 assembler. Note, the freeware version of IAR v1.4 is no longer available on the IAR website, however, whatever is there should work fine. Go to products, then to the AVR section. There should be free & demo downloads available there.
Sensor is a stand-alone task under the AvrX RTOS, that Dilbert is based upon. The task definition uses the context save/restore fragment from another task to preserve registers R20-R31, SREG and R0. TASK() and TIMER() declarations are macros defined in AvrX.inc. The software routine div16x8u can be found on the Dilbert web page in the file math.asm (Get the zip file of all software).
Because the Sharp sensors consume quite a bit of current when running, the task has an Sram flag that it checks before activating the sensors. There must be a better way to do this, but, at the time polling made the most sense, hence the startup code of "Sensor" When activated I want to read the sensor as fast as possible, hence the endless loop of the task.
The essence of the multiplexing routine is contained between "WaitUntilReady" and "Cook and store results". Because of the speed of the AVR processors, I needed to introduce some delays when clocking out the data from the GP2D02 sensor. The specifications for the sensor imply a much slower clocking speed but experiments done by others in the SRS have shown that a 10-16us clock period works Ok. The symbol CPUCLK is defined in the file harware.inc and is simply the crystal frequency of the CPU. I use that define when calculating UART divisors and system clock tick values. Then I can change crystals, and one define, and have everything still work when I recompile.
If you want to use this code in your own application, just remove the AvrX stuff, replace the delays with hard coded loops and replace the rjmp at the end of the routine with a ret.
hardware.inc
Code: Select all
;
; PORTB Bit Definitions
;
; PB 0-2 are the Front, Right and Left Sharp Proximity Sensors
;
Vin DEFINE PB3 ; Control signal into Sharp Proximity Sensor
;
; Initialize Vin to 1 to force the sharp sensors to reset during power up
; sequence (see Drivers.asm)
;
PORTB_INIT DEFINE 0 ; No pull up's needed
DDRB_INIT DEFINE (1<<PB7 | 1<<PB6 | 1<<PB3 | 1<<PB4); Vin is output
SHARP DEFINE PORTB
Drivers.asm
PUBLIC SensorFlag Front Right Left FrontRaw RightRaw LeftRaw
EXTERN AvrXDelay
EXTERN MctlContext
EXTERN div16x8u
TASK(Sensor, 12, 2, MctlContext, 6)
TIMER(SensorTimer)
RSEG DATA
SensorFlag: DS 1 ; Flag to control activation of sensors
Front: DS 1
FrontRaw: DS 1
Right: DS 1
RightRaw: DS 1
Left: DS 1
LeftRaw: DS 1
RSEG CODE
;+
;-------------------------------------------------------
;
; ReadProximitySensor
;
; PASSED: Nothing
; RETURNS Nothing
; USES: R0, R20-31, flags
; STACK: 2
; NOTES: Linearizes the SHARP output and stuffs values
; into the Sensor Data Structure.
;-
Sensor:
sbi SHARP, Vin
AVRX_Delay SensorTimer, 3/2 ; 1.5 ms/loop
tst SharpEnable ; Flag to enable/disable
breq Sensor
cbi SHARP, Vin ; Initiate Proximity Detection
ldi Zh, 40 ; Wait a maximum of 60ms for sensor
WaitTillReady
rcall AvrXDelay
in Xh, SHARP-2
sbr Xh, 0xF8 ; PB0-2 are our inputs
cpi Xh, 0xFF
breq SensorReady
dec Zh
brne WaitTillReady
rjmp Sensor ; Reset and start again.
SensorReady
ldi Zh, 9 ; Use bit counter so having no
; sensor wired up doesn't hang routine
ReadNextBit:
in R0, SHARP-2 ; Read pins, not pullups
sbi SHARP, Vin
ror R0 ; Bits 0-2 are Front, Right, Left
rol R20 ; R20 = Front
ror R0
rol R21 ; R21 = Right
ror R0
rol R22 ; R22 = Left
ldi Zl,(1*CPUCLK/3000000) ; 1 uS delay
dec Zl ; The loop is only 3 cycles long
brne $-2
cbi SHARP, Vin
ldi Zl, (16*CPUCLK/3000000) ; 16 uS delay
dec Zl
brne $-2
dec Zh
brne ReadNextBit
;
; Cook and Store results
;
mov Wl, R20
ldi Wh, FRONT_KO
ldi Xh, high(FRONT_KG)
ldi Xl, low (FRONT_KG)
rcall ProcessProximity
sts Front, Xl
sts FrontRaw, R20
mov Wl, R21
ldi Wh, RIGHT_KO
ldi Xh, high(RIGHT_KG)
ldi Xl, low (RIGHT_KG)
rcall ProcessProximity
sts Right, Xl
sts RightRaw, R21
mov Wl, R22
ldi Wh, LEFT_KO
ldi Xh, high(LEFT_KG)
ldi Xl, low (LEFT_KG)
rcall ProcessProximity
sts Left, Xl
sts LeftRaw, R22
;
rjmp Sensor
;+
;------------------------------------------------------------------
; ProcessProximity
;
; PASSED: X = Kg, Wh = Ko, Wl = Raw X
; RETURNS: Xl = processed value
; USES: R0, Flags
; STACK: 2
;
;-
ProcessProximity:
sub Wl, Wh
brlo PPX
rcall div16x8u
tst Xh
breq PPY
PPX:
ser Xl ; Over-range, limit to 0xFF
PPY:
ret