I haven’t touched the DIY rangefinder project for a couple of weeks, but I realized I also never wrote up my latest work on it.
Receiver Comparator
After my previous post about building two amplifier stages, I intended to use one of the spare channels on the LM324 as a comparator to “digitize” the amplified receiver signal and provide a clean input to the LogoChip. Unfortunately, the first time I prototyped it was late in the evening when my brain doesn’t work too well, and I built it just like the first two stages–as a linear amplifier. I was really surprised to see more analog waveforms on my scope when I expected sharp edges. At only 24kHz, I didn’t think it should be a slew rate problem, but I wasn’t thinking clearly and just set it aside for the night.
The next day, my error was obvious, and I pulled out the feedback resistor to let the op amp run open loop with infinite gain. It cleaned up the signal, although still not as much as I would have expected. Once the basic circuit was working, I replaced the fixed voltage divider with a 20k trimpot and tuned it as well as I could, trying to find the sweet spot to weed out all the noise but pass all the real signals.
I hadn’t seen it before, but now I wondered whether I was getting some false readings from noise, as John suggested he had encountered when working on similar circuits. I looked briefly at adding a low-pass filter to the circuit (it’s already high-pass filtered by the capacitive coupling from the receiver to the first amplifier stage due to the single-ended op-amp I’m using), but haven’t done it yet. First I wanted to connect it to the LogoChip and see what kind of results I got.
PIC CCP
An ultrasonic rangefinder measures distance by emitting a “ping” and counting the time until the echo is detected; dividing by the speed of sound translates the round-trip time into a distance. Because of the short durations involved, the easiest way to do this on a PIC microcontroller is with the “capture” section of the Capture/Compare/PWM (CCP) subsystem. It uses a high-resolution timer and interrupt circuitry to give a very precise measurement of how long something takes to happen.
I started with John Harrison’s CCP code from his srf_demo.txt
example on the TechArt wiki LogoChip with Ultrasonic Sensor page. His code was using the PIC’s CCP module #1; but in my application, I was already using CCP1 to provide the 24kHz signal for my transmitter, so I started translating to use CCP2. The control and behavior of the two CCP channels are rather different, so it wasn’t a matter of simple substitution. Here’s what I ended up with:
Here’s the receiver section of the code:
;------------------------------------------------------------------------------
; Routines for manipulating CCP2 and timer 3 for capture.
;------------------------------------------------------------------------------
constants [
[CCP2CON $fba] ; capture/compare/PWM 2 control reg
[CCPxM-CAP-EVERY #0100] ; capture every falling edge
[CCPR2H $fbc] ; CCP 2 register low byte
[CCPR2L $fbb] ; CCP 2 register low byte
[PIR2 $fa1] ; peripheral interrupt request 2
[CCP2IF 0] ; timer 3 interrupt bit
[CCP2-PORT portc] ; uses portc pin 1
[CCP2-BIT 1]
[CCP2-DDR portc-ddr]
[T3CON $fb1] ; timer 3 control register
[T3CCP2 6] ; timer 3 CCP mode bit 2
[T3CCP1 3] ; timer 3 CCP mode bit 1
; 1x src CCP1+2
; 01 src CCP2
[T3CKPS1 5] ; timer 3 prescale bit 1
[T3CKPS0 4] ; timer 3 prescale bit 0
; 11 = 1:8
[TMR3ON 0] ; timer 3 enable bit
[TMR3H $fb3] ; timer 3 high byte
[TMR3L $fb2] ; timer 3 low byte
]
; Initialize CCP by configuring timer 3 and enabling capture mode.
to ccp-init
write CCP2CON CCPxM-CAP-EVERY ; config to capture every falling edge
clearbit T3CCP2 T3CON ; set T3CCP to 01 to make T3 src CCP2
setbit T3CCP1 T3CON ; and T1 src CCP1
setbit T3CKPS1 T3CON ; set T3CKPS to 11 to use 1:8
setbit T3CKPS0 T3CON ; prescalar
setbit TMR3ON T3CON ; enable timer 3
setbit CCP2-BIT CCP2-DDR ; set CCP2 (RC1) pin for input
end
; Record starting value of timer 3 and clear capture flag.
global [ccp-before]
to ccp-start
setccp-before ((read TMR3H) * 256) + read TMR3L
clearbit CCP2IF PIR2
end
; Wait for capture event and return duration.
global [ccp-after]
to ccp-wait
waituntil [testbit CCP2IF PIR2] ; wait for capture interrupt
setccp-after ((read CCPR2H) * 256) + read CCPR2L
output ccp-after - ccp-before
end
I added code to send a ping and take a measurement:
;------------------------------------------------------------------------------
; Top-level routines to tie it all together.
;------------------------------------------------------------------------------
; For the sake of testing, activate on powerup.
constants [[standalone 0]]
to powerup
pwm-init
ccp-init
if standalone [ pwm-on ]
end
; Send a short burst of tone.
to ping
pwm-on
mwait 1
pwm-off
end
; Ping and measure time to reply.
to range
ccp-start ; record start time
ping
output ccp-wait ; wait for echo and return duration
end
Wrote a little loop to display the results on the PC, and was disappointed to see that they really didn’t vary at all in proportion to the distance from the sensor to an obstacle. After a few minutes of head-scratching, I noticed that I hadn’t wired the comparator output to the LogoChip’s CCP input. Oops! And that’s when I realized I had a much bigger problem.
Overloading Port C1
The PIC’s CCP modules are implemented in hardware, and they’re hard-wired to specific pins. The input pin for CCP2 is the same as general-purpose pin C1–in fact, most if not all of the PIC’s pins have multiple functions that are selected by configuration registers.
My problem arose because C1 is already being used by the LogoChip firmware–as the Go/Stop input! Because LogoChip Logo isn’t open-source, I can’t see exactly how it’s being used, and have to guess what would happen if I tried to use it as a capture input. Since pressing a button attached to that pin makes the LogoChip stop executing user code and return to an idle state, I can’t imagine I’d have an easy time overriding that behavior and getting it to use the pin solely for CCP with no interference.
So what now? I need both CCP modules–one to use the PWM output to drive the 24kHz transmitter, and the other to watch and count time on the receiver. I don’t think I’ll be able to overload C1 with CCP2 for the receiver, so I’m left with a couple of options: swap the pins, or use CCP1 for both transmission and reception.
Swap the Pins
My first thought was to use CCP1 as the receiver (since its corresponding port, C2, is otherwise unused) and make CCP2 the transmitter (on port C1, the Run/Stop button). John says he has previously overridden C1 and used it as an output when the Run/Stop functionality wasn’t needed, so this should work.
In the long run, though, I’m a little uncomfortable giving up the Run/Stop button. I’d like to be able to integrate this technology into hobby robots, and I’d like to be able to leave them powered up but in an idle state, without having to duplicate the preexisting Run/Stop behavior myself in software. Swapping the PWM-transmit / capture-receive pins should work for a quick fix, but I’m not sure it’s where I want to be in the larger game.
Use CCP1 for Both Transmission and Reception
Alternately, I could overload CCP1 for both transmitter and receiver. There’s precedent–another student in class is using the Parallax PING))) (TM) sensor module, which has only three pins–ground, power, and bidirectional signal.
To do that, I’d need to make sure that transmission and reception don’t interfere with each other. Keeping transmissions out of the receiver circuit is easy–put a diode from the comparator output to the LogoChip so the transmitter signals don’t go “upstream” into the comparator.
Keeping comparator signals out of the transmitter driver is a little trickier, but I think I have a reasonable solution, based on something I was considering doing anyway. I’d like to be able to drive multiple sensors from one LogoChip, but there’s only one (er, two, maybe, sorta) PWM output available. To drive multiple rangefinders, I’d need to multiplex the PWM signal somehow–and there are plenty of ways to do that.
The most straightforward is to use a few LogoChip output pins to select which transmitter to drive and AND (or NAND) them with the PWM signal. Cake! And it tidily solves my receiver-interfering-with-transmitter problem–the receiver can dump whatever it wants onto the PWM output, but it won’t go anywhere harmful unless one of the transmitter select lines is activated.
Sounds like a win all around, and I think I’ll investigate it as soon as things calm down with the exhibit and I can get back to tinkering. It means a little more work on the software side (switching CCP1 back and forth between PWM and capture modes), but I’ve never been afraid of writing code.