The quit description. Send out 16 pulses at 40Khz through one ultrasonic transducer. Time the echos as they come back. Send the time in ASCII out the serial port at 9600 baud. Repeat.
The thick green line is at the transition between zero and one. That transition happens to be at 1.23volts in my picture, but it slowly changes. I doesn't change much in 3 milliseconds. It does vary part to part, and with temperature and probably many other factors. The circuit self calibrates before each reading. That voltage can drift without affecting operation. Likewise, the resistors and capacitors in the circuit can drift. The circuit doesn't need precision components, or manual adjustment.
Ok, the "N" in N pulses is adjustable. But that is in software and can be changed over the serial port.
A 40Khz squarewave has 80,000 transitions per second. This is one transition every 12.5 microseconds. With a 10 Mhz crystal, the PIC needs to make one transition every 31.2 cycles. Reality gets in the way. The code actually does one transition every 31 cycles, Making the output frequency be 40.3Khz.
I arbitrarily chose 16 cycles. This works, so I haven't experimented with other values.
Here is the code for the output routine "osc16".
Copies of TRISB and the output bits of PORTB are kept in file registers
shadowtb and shadowb respectively. The loop
counter is kept in the file register ocnt. The subroutine
del26 takes exactly 26 cycles, including the call and return
statement.
Instructions that change the program counter take 2 cycles, the rest take
1 cycle. So a nop instruction takes 1 cycle, jumping to the
next instruction, goto $+1, takes 2 cycles.
UTX1 equ 7
UTX2 equ 6
osc16
movlw 16 ; 16 cycles at 40Khz
movwf ocnt
movlw 1
bsf shadowb,UTX1 ;
bcf shadowb,UTX2 ; This pin is the negation of UTX1
bcf shadowtb,UTX1
bcf shadowtb,UTX2
movf shadowtb,w
tris PORTB
oloop ;0
movf shadowb,w ;1
movwf PORTB ;2
call del26 ;28
movf shadowb,w ;29
xorlw (1<<UTX1)|(1<<UTX2) ;30
goto $+1 ;31
movwf PORTB ;33 Should be 31 cycles between writes to PORTB
call del26 ;59
decfsz ocnt,F ;60
goto oloop ;61/62
bsf shadowtb,UTX1
bsf shadowtb,UTX2
movf shadowtb,w
tris PORTB
return
The input routine wants to see an alternating like pattern 10101 when it samples at 80Khz.
That scheme will fail if the samples are close to the zero crossing of the input wave.
The input routine actually samples at 160Khz, keeping 2 independent counters,
hits1 and hits2. If either counter sees 4 transitions at
80Khz, the routine returns success. I check to see if a counter has reached 4
by checking bit number 2 of the counter.
The loop is 31 cycles long, 12.4 microseconds, one half of a 40.3Khz wave. The receiver routine counts each time through the loop. After 255 passes through the loop, it gives up.
The speed of sound is about 1 foot per millisecond. This system times the round trip of the sound pulse. If there is an object 1 foot away, the sound will take a millisecond to get there, and a millisecond to come back, for a total of 2 milliseconds. That is a count of about 160. So the maximum range of this software is only 18 inches. The drive circuit is wimpy enough, that 18 inches is also about the range of the hardware.
echotime ; Time until we see an echo.
clrf ecnt
clrf hitsa
clrf hitsb
; This loop is exactly 31 cycles long, which is half of a 40Khz cycle
echoa ; 0
btfsc flags,LASTA ; 1
goto lasta1 ; 2/3
lasta0 ;2
nop ; equalize path length ;3
btfss PORTB,URX ;4
goto samea ;5/6
bsf flags,LASTA ;6
goto diffa ;8
lasta1 ;3
btfsc PORTB,URX ;4
goto samea ;5/6
bcf flags,LASTA ;6
goto diffa ;8
samea ;6
clrf hitsa ;7
call del4 ;11
goto echob ;13
diffa ;8
incf hitsa,f ;9
btfsc hitsa,2 ;10 Did we see 2 whole cycles (4 half cycles)
goto echofound ;11/12
goto $+1 ;13
echob ;13
goto $+1 ;15
btfsc flags,LASTB ;16
goto lastb1 ;17/18
lastb0 ;17
nop ; equalize path length ;18
btfss PORTB,URX ;19
goto sameb ;20/21
bsf flags,LASTB ;21
goto diffb ;23
lastb1 ;18
btfsc PORTB,URX ;19
goto sameb ;20/21
bcf flags,LASTB ;21
goto diffb ;23
sameb ;21
clrf hitsb ;22
call del4 ;26
goto next ;28
diffb ;23
incf hitsb,f ;24
btfsc hitsb,2 ;25 Did we see 2 whole cycles (4 half cycles)
goto echofound ;26/27
goto $+1 ;28
next ;28
incfsz ecnt,F ;29
goto echoa ;30/31
movlw 255
return
echofound;
movf ecnt,w
return
setcap, near the bottom. Note that
pulseconstant is set by a command over the serial port. I hope to
automate away that kluge.
pulsecap ; make the cap pin an output for 3 cycles.
bcf shadowtb,CAP
movf shadowtb,w
tris PORTB
bsf shadowtb,CAP
movf shadowtb,w
tris PORTB
return
longpulsecap ; make the cap pin an output for 29 cycles.
bcf shadowtb,CAP
movf shadowtb,w
tris PORTB
call del26
bsf shadowtb,CAP
movf shadowtb,w
tris PORTB
return
capto0
btfss PORTB,CAP ; loop until the pin is at 0
return
bcf shadowb,CAP
movf shadowb,w
movwf PORTB
call pulsecap
goto capto0
capto1
btfsc PORTB,CAP ; loop until the pin is at 1 on input
return
bsf shadowb,CAP ; Make cap pin a 1 when output
movf shadowb,w
movwf PORTB
call pulsecap
goto capto1
setcap ; Charge up the cap until it is right at the transition from 0 to 1
call capto1
call capto0
call capto1
call capto0
bsf shadowb,CAP ; Make cap pin a 1 when output
movf shadowb,w
movwf PORTB
movf pulseconstant,w
movwf ncnt
setcapl
call longpulsecap
decfsz ncnt,f
goto setcapl
return
Right now, the system checks for a start bit in the delay routines (del26 in
the above code snippits). The system should also be check during the echotime
routine.
My hardware is pretty icky right now. The receiver sees a weak echo through the mechanical transducer mounting. This is the real limit on range. If I get better mechanical mounting for the transducers, I can improve the range dramatically.
I could get much longer range with a more powerfull driver circuit. I wanted to prove that I didn't need all that stuff. Now that I've proved it, I can add some oomph.
I'm open to other suggestions.