As part
of the ongoing effort to improve Red Menace,
I wanted to be able to directly read the Gyro, and use the IFI controller to
process the gyro output.
So, cribbing from my old ultrasonic code, I hooked up a Basic Stamp
Board of Education to one of the IFI outputs (white = signal, black = gnd,
and red = +5V if you're feeding it into the IFI), and wrote some code to
measure the length of the pulse and send it out over the program/debug
port. Seems to work fine, but more testing is going to be needed. The following
code is hopefully self-explanatory, enjoy!
' PROGRAM: BS2SX PWM Read Test
' Written by: Robert J Woodhead
' Date: 20 SEP 02
'
' Define BS2-SX Project Files
'
' {$STAMP BS2SX}
'
' Reads an incoming PWM signal, converts it to fracBase
' coordinates, outputs it as a packet ($FF,value) over
' the program/debug port.
'
' fracBase is the internal coordinate system used by my
' other code. Instead of using 0..255 as is normal
' with a lot of IFI code, I use 0..250 to avoid overflow
' problems. This also avoids the deadly 255 value being
' sent to the victors. You can change fracBase to 256 if
' you want, it's entirely up to you. However, using a
' smaller range lets you use an out-of-range value (I use
' fracBase+1) as a signal that means "no valid data"
'
' Another nice effect is that since 0..250 has an odd
' number of steps, the central step (125) is the true
' center of the range.
fracBase con 250 ' fractional range base, same as driver code
fracHalf con 125 ' half of fracBase
fracBaseP1 con fracBase+1 ' utility constants
fracHalfP1 con fracHalf+1
' From empirical testing, I've found out the minimum and
' maximum pulse length (probably need to test it again
' to make sure). From these numbers, I can precompute
' some constants to make the math go faster
minPulse con 1120 ' minimum pulse width
maxPulse con 2658 ' maximum pulse width
maxPulseM1 con maxPulse - 1 ' useful to have -1 value
rngPulse con maxPulse - minPulse ' max pulse range
badPulse con 500 ' bad pulse threshold
' In order to do precise math, when scaling from the pulse
' range to the fracBase range, I use a trick. In order to
' avoid integer truncation problems, I first multiply the
' 0..rngPulse pulse width by some number greater than
' (rngPulse / fracBase), then divide it out again later.
' This in effect lets me do division with a fractional
' component. As long as this multiplier is such that
' rngPulse * multipler < 65536, we'll never get an overflow.
'
' Since rngPulse is about 1500, as long as this number is
' above 6, and below about 40, we will get the desired
' accuracy. I picked 32 because it's a number with only one
' 1 bit, and thus multiplies faster.
mulPulse con 32
divPulseM con mulPulse * rngPulse
divPulse con divPulseM / fracBase
' So, the pulse, scaled to 0..rngPulse-1, multipled by
' mulPulse and then divided by divPulse, gives us our
' final value.
pulse var word ' the actual pulse
result var byte ' massaged result
' Right now, for testing, I actually output human readable
' text to the debug port. In the final working code, these
' will be removed.
debug 0 ' clear the screen
' Main loop. Read PWM, display results
main:
' Read a pulse from input line 0, low->high start,
' and store the length in pulse
pulsin 0,1,pulse
' If no pwm received, indicate error (fracBase+1)
if pulse <= badPulse then p_bad:
' Pin results to the known good range. I max out
' at maxPulse - 1 to avoid "going over the top"
' problems when doing the scaling.
pulse = (pulse max maxPulseM1) min minPulse
' Scale the pulse to 0..fracBase.
result = ((pulse - minPulse)* mulPulse) / divPulse
goto send_packet:
p_bad:
' Set to our arbitrary "bad pulse" value
result = fracBaseP1
send_packet:
' Human readable output (home, output original and scaled
' result, erase to end of line
debug 1,dec5 pulse," ",dec3 result,11
' Code you'd use to send to the basic stamp inside the
' IFI. You just connect the program/debug port of the
' IFI to the program/debug port of your sensor manager
' stamp (running this code).
'
' Amusingly, serout 16,16224 is the same as debug, but
' it looks cooler this way.
' serout 16,16624,[ $FF,result ] ' send packet
' How the comm link works:
'
' This code is going to send out, without handshaking,
' byte pairs a couple of hundred times a second.
'
' On the receiving end, in the code running on the IFI,
' you have the following statement:
'
' serin 16,16624,50,timeout,[ WAIT($FF),STR pwm\1 ]
'
' This waits until it sees a $FF byte, then reads 1
' byte into byte variable pwm. If it doesn't finish
' within 50 msec (1/20 second), it jumps to label
' timeout.
'
' That timeout is probably generous, you can probably
' reduce it (haven't done testing yet).
'
' The cute thing is that because the packet starts with
' $FF, and because the data byte can't be bigger than
' 251 (and thus, never $FF), even if the serin starts
' reading in the middle of the send of a packet, there's
' no combination of data bytes [or start/stop bits] that
' can generate a $FF byte. So the serin will simply
' keep waiting until it sees the next packet. You can
' create, send and add a checksum if you want. If you
' do more complex stuff (I did some code reading 8
' ultrasonic rangefinders) you can send multibyte packets
' as well. It's a lot cleaner than trying to generate
' a voltage for the IFI to convert back into a digital
' value.
'
' Note also that you can do some preprocessing in this
' basic stamp. In my ultrasonic code, what got sent to
' the IFI was not the raw readings, but a massaged and
' filtered driving recommendation, in the form of two
' virtual joystick values!
goto main: