The GPMPU28 boards are highly customizable and contain all you need to make permanent or breadboard PIC projects using most Microchip 28 pin PICs (including the PIC16F873 shown above). The above board is "fully loaded" with the power supply option, an optional terminal block, and an optional reset switch -- you can customize these boards in lots of ways (just see the manual for some ideas).
Our first kit based around this board is a Morse code beacon and keyboard keyer. This is ideal for radio location beacons (foxes), beacons, or any of a number of other items. Here's how it works:
When you first power the board up (or reset it) you have 5 seconds to send a character down the serial port at 19,200 baud (8 bits, no parity, 1 stop bit). Usually you'll use Hyperterminal set for hardware handshaking and turn local echo ON so you can see what you are typing!
If the device has never been set up, or you hit any key except for a K within 5 seconds, you enter programming mode. This lets you set a message (up to 75 characters), a delay in seconds between messages, and a speed in words per minute (approximate). Sorry, but there is no backspace allowed! However, if you mess up you'll have an opportunity to reenter the data.
Once the data is entered, the beacon plays the message and asks for your approval. You can reenter the data, hear the message again, or approve it. If you approve, the chip writes the parameters into onboard EEPROM and you never have to set it again (unless you want to).
Once you are done (or if you didn't hit a key within 5 seconds) the chip enter beacon mode. It plays your message, pauses for the delay, and then repeats it. The delay is not atomic clock precise, but it isn't bad either. There are several outputs and inputs:
The LED on the board blinks the code.
One pin on the edge connector also goes high with the code (this could key a transmitter).
One pin on the edge connector goes high 50mS before sending and goes low 50mS after sending is complete. This can control the transmitter's PTT line.
There is a manual trigger input. If it goes low, the beacon sends immediately and restarts its delay count. If you don't want to use the trigger, just tie the pin high. If you want the chip to only send on trigger events, set the delay parameter to 0.
There is provisions for a speed control pot (say, a 5K pot although it isn't critical). Just tie one side to +5V and the other side to ground. The wiper connects to the speed input. To use this to control the speed, set the WPM parameter to 0, otherwise the pot has no effect (and can be omitted).
When entering code you can use = for BT, ; for KN, ' for SK, and @ for AR. Upper or lower case letters are OK. The program also knows how to send periods, commas, slashes, and question marks. Any other character generates a space.
If you enter a K (or a k) at startup, the chip enters keyboard mode. Whatever you type on the terminal will be sent in the usual way. In this mode you have all the same characters, plus the backspace will generate 8 dots.
The + key increases your speed and the - key decreases it. You can also use the ~ key to switch between keyboard speed control and pot control (if you have a pot mounted).
When using the keyboard mode you also directly control the PTT line. Use ! to assert the PTT and * to return to receive mode. The chip will still send code even if PTT is off.
There are no modifications required to the board. A PIC16F873A forms the brains. You can follow the directions and the schematic in the manual. If you are breadboarding, you can just stick the board into your breadboard and then wire the following optional components:
A piezo speaker on PortC.2 (pin 2 of JP1). The PWM that drives the speaker sounds buzzy, but it is acceptable. If you wanted to filter and amplify it, you could, but it is typically not necessary. If you omit the speaker, you'll still be able to see the code flashing on the onboard LED.
If you want to drive a transmitter, you can take the output from pin 3 (PortC.1) which matches the onboard LED's Morse code output. Usually, you'd have this output drive a 2N2222 which would simulate a key to the radio. Of course, if you don't connect to a radio, you can omit this.
Pin 4 (PortC.0) goes high 50mS before transmitting in beacon mode and stays high for 50mS after the beacon completes a message. This will also usually drive a 2N2222. Some radios won't need this output and if you aren't driving a radio, you can certainly ignore this output.
Pin 6 (Port A.4) is the manual trigger. If you don't want to use this, connect it to 5V. If you want a switch, place a pull up resistor (say, 10K) to +5V and the switch to ground. The chip assumes that the trigger will be shorter than the message. If it isn't the message will repeat almost immediately until the trigger returns high. This is optional.
Pin 10 (Port A.0) is the speed pot input. This allows a 0 to 5V voltage to set the speed to about 5WPM to 35WPM. This is read by the PIC's analog to digital converter and is optional since both beacon and keyboard mode can set their own speed numerically.
Of course, if you are building this as a permanent fixture, you can solder a general purpose PC board to the breadboard holes or use the holes to accommodate a cable to another board. In this case, I designed the software around the hole layout, but the board has a unique feature that lets you easily reroute other signals to the edge if you need them.
The software using Microengineering Lab's PIC Basic Pro compiler. You could probably convert this to run on a Basic Stamp pretty easy. You'd need to change all the HSERIN/HSEROUT commands to use SERIN and SEROUT instead. BRANCHL becomes BRANCH. You'd have to modify the pot reading commands to use RCTime (and add a capacitor to the circuit). Or you could just ditch the pot, which would be easier!
The code is relatively straightforward. The only problem is the limited EEPROM space on the PIC. I wanted to make most of it available for message storage. But I also needed a table that tells me how to convert letters to dots and dashes. I've done this in the past using a pure data table and interpreting it (for example, in our PAK-VI application note) but this time I tried something different.
There are two routines that generate dots and dashes called... drum roll please.... dots and dashes. Each routine takes a count of how many to make and makes them. There is also a piece of code known as lastdot. This subroutine plays a certain number of dots, pauses between characters, and goes back for more. The lastdash routine does the same for letters that end in dashes.
So to send an "E" (one dot) you set n=1 and go to lastdot. An "I" (two dots) takes n=2. When you have a mix of dots and dashes, I try to save space by jumping to a tail end letter. For example, an "R" (dot dash dot) is a single dot (n=1, call dots) and then a jump to the letter "N" (dash dot). In many cases, the letters just fall through into the tail end letter instead of executing a jump.
In retrospect, I think I like the other method better. However, this works, and there is plenty of code space so I've traded code space to get more data space. Another reason I had thought about this approach is that a ham friend of mine (hi Tom) was talking about doing this with landline Morse code. Landline Morse has things like long dashes and gaps in it, so using a code-based method like this give you a lot of flexibility. It would be very simple to change this code to generate letters that had odd spacing or elements.
Of course, any changes you want to make in the dot and dash generation is just a matter of changing dots and dashes. So the code isn't as pretty as the CW keyboard code in the PAK-VI application note, but it is very flexible.
' Morse code beacon (c) 2003 by Al Williams. All Rights Reserved ' This version supports a keyboard input mode ' Hardware: GPMPU28 (unmodified) ' Extra hardware: ' Piezo speaker (optional) on PortC.2 ' Transmitter key circuit (optional) on PortC.1 ' Transmitter keying circuit (optional) on PortC.0 ' Pot (optional) on PortA.0 ' Trigger on PortA.4 (low to manually trigger beacon) ' ' Operation: On power up, hit a key on the terminal within 5 seconds. ' A k or K will enter keyboard mode. Any other key will enter program ' mode. If the device is empty, it will start program mode in 5 seconds ' of no action. The device expects a 19,200 baud/8/n/1 ' serial terminal for programming. Hardware handshaking is used. ' ' You can insert a string (upper or lower case is OK), a delay in seconds ' and the words per minute you wish to send. The delay is the time ' between beacon messages. If the WPM is 0, the speed is read ' from a 0-5 voltage on PortA.0 (probably from a pot). 0V is 5WPM and ' 5V is 35 WPM. ' ' When you finish entering the data, the unit plays the data back ' (without keying the transmitter). You can approve it, listen again, ' or reenter the message. ' ' Once approved -- or if 5 seconds elapses with no input after reset -- ' the beacon sends its message. There is a 50mS delay before and after the ' message that the transmitter is keyed. Then the delay time must expire ' before the message is transmitted again. ' ' The parameters are stored in EEPROM so once programmed, no connection ' to a PC or terminal is required. For best results, enable local echo ' on the serial terminal. ' If on power up, the mode pin is high, then the device operates as an ' RS232 to Morse code terminal. ' Characters A-Z and a-z 0-9 , . / ? ' Prosigns: ' @ - AR = - BT ; - KN ' - SK ' In keyboard mode, + and - adjust internal speed up and down ' ~ switches to pot control of WPM ' ! turns on PTT and * turns it off, ' backspace sends 8 dots ' Set serial port DEFINE OSC 20 DEFINE HSER_RCSTA 90h DEFINE HSER_TXSTA 20h DEFINE HSER_BAUD 19200 ' harmless to run under ICD or not INCLUDE "icddefs.inc" ' IO definitions LED var PortC.3 SPEAKER var PortC.2 PTT var PortC.1 ' PTT output to transmitter KEY var PortC.0 ' key output to transmitter SPDCTL con 0 ' A/D channel 0 - speed control CTS var PortB.2 ' RS232 CTS TRIGGER var PORTA.4 ' trigger input MSGLEN con 75 ' Maximum message length KEYDELAY con 50 ' delay (ms) between PTT and first or last element TONE con 1000 ' sidetone in Hz STARTDLY con 5000 ' timeout for start command c var byte ' character ch var byte ' character dly var word ' delay between messages (if $FFFF we haven't been programmed) cdly var word ' current delay time rmsg var byte[MSGLEN] ' the message wpm var byte ' words per minute n var byte ' general byte dot var word ' length of a dot in ms dash var word ' length of a dash in ms (3*dot) chspace var word ' character space (1.5*dot) msgoffset con 3 ' offset of message in EEPROM HIGH CTS ' disable serial in LOW PTT LOW KEY ' Set unused pins low LOW PortC.4 LOW PortC.5 Low PortB.0 ' Port B 1 is input from serial port Low PortB.3 Low PortB.4 Low PortB.5 Low PortB.6 Low PortB.7 ' On start up we listen for some sort of serial input for 5 seconds. If we don't get ' any we just go do our thing HSEROUT ["5 seconds to beacon mode; press K for keyboard, other key to program",13,10] LOW CTS HSERIN STARTDLY,start, [c] if c="k" or c="K" then keyboard HIGH CTS ' ok we are in program mode reprogram: ' Clear RAM message for c=0 to msglen-1 rmsg[c]=0 next HSEROUT ["BEACON MESSAGE> "] LOW CTS HSERIN [str rmsg\msglen\13] HIGH CTS HSEROUT ["Message: ",str rmsg\msglen,13,10] HSEROUT ["DELAY (seconds)> "] LOW CTS HSERIN [dec dly] HIGH CTS HSEROUT ["Delay: ", dec dly,13,10] HSEROUT ["WPM> "] LOW CTS HSERIN [dec wpm] HIGH CTS HSEROUT ["WPM: ", dec wpm, 13,10] replay: HSEROUT ["Playing...",13,10] gosub play HSEROUT ["Return to accept, ? to replay, Esc to reenter",13,10] wloop: LOW CTS HSERIN [c] HIGH CTS if c=="?" then replay if c==$1b then reprogram if c<>13 then wloop ' save into EEPROM write 0,dly.highbyte write 1,dly.lowbyte write 2,wpm for c=0 to msglen-1 write c+msgoffset,rmsg[c] next HSEROUT ["Accepted",13,10] ' Here is where we go into beacon mode start: HIGH CTS ' shut up serial port ' Load delay parameter read 0,dly.highbyte read 1,dly.lowbyte read 2,wpm ' Get WPM if dly=$FFFF then reprogram ' no point in playing garbage! if dly=0 then trigscan ' Load message from EEPROM once for c=0 to MSGLEN-1 read c+msgoffset,rmsg[c] next ' This is the main beacon code runloop: sendit: HIGH PTT ' turn on transmitter pause keydelay gosub play pause keydelay LOW PTT cdly=dly*1000 trigscan: if trigger=0 then runloop if dly=0 then trigscan PAUSE 250 cdly=cdly-250 ' since *1000 this will always reach zero if cdly<>0 then trigscan goto runloop ' Play the message play: for c=0 to MSGLEN-1 gosub setwpm ' set WPM each time in case pot has moved ch=rmsg[c] if ch>="a" then ch=ch & $DF ' fold lower case endif if ch=0 or ch=13 then ret ' 0 or 13 marks end of string gosub send_char next return ' The code starting here generates the characters ' Any character <32 or >"z" is a space ' Any character "a" or above is folded to its upper case equivalent ' including the few special characters we don't support above "z" ' The branches vector to routines that compose each character ' This is not ideal, but allows us to trade flash space ' for data storage :-) ' For these to be branches instead of branch L's the targets need to be on the same page ' otherwise make them branchl send_char: BRANCHL ch-33,[none,none,none,none,none,none,prosk] ' 33-39 BRANCHL ch-40,[none,none,none,none,comma,none,period,slash,zero,one] ' 40-49 BRANCHL ch-50,[two,three,four,five,six,seven,eight,nine,none,prokn] ' 50-59 BRANCHL ch-60,[none,probt,none,quest,proar,leta,letb,letc,letd,lete] ' 60-69 BRANCHL ch-70,[letf,letg,leth,leti,letj,letk,letl,letm,letn,leto] ' 70-79 BRANCHL ch-80,[letp,letq,letr,lets,lett,letu,letv,letw,letx,lety] ' 80-89 BRANCHL ch-90,[letz] none: space: PAUSE 2*dash ret: return ' Set the wpm parameters setwpm: n=wpm if wpm<>0 then setwms ' not set by pot ' Read pot ADCON1.7=0 ADCIN SPDCTL,n n = (30*n)/255 + 5 ' convert to WPM ( 5 to 35 wpm) ' Either way we now have WPM in n so we compute dot/dash/chspace setwms: dot = 60000/(50 * n) dash=dot<<1+dot '(dash = dot * 3 ) chspace = dot + (dot >>1) ' 1.5* dot return 'Each character generator is either a digit/punctuation name (one, two) or prefixed with let ' for letter or pro for prosign. So leta is A, letz is Z and probt is BT. ' The generators make as much as they have to and then jump to another letter ' to finish. So U (.--) might be dot + M. ' This could be further optimized as I've done for U/V/A ' and others but since we have lots of room I didn't do every possible one zero: n=5 lastdash: ' This is where characters that end with a dash wind up gosub dashes endchar: ' This is the end of all characters pause chspace return one: ' dot + 4h n=1 gosub dots n=4 goto lastdash two: ' dot dot O n=2 letjopt: gosub dots leto: n=3 goto lastdash three: ' dot dot dot M n=3 dotletm: gosub dots letm: n=2 goto lastdash four: ' dot dot dot dot T n=4 letuv: gosub dots lett: n=1 goto lastdash five: n=5 lastdot: ' all characters that end in dots wind up here gosub dots goto endchar six: ' dash H n=1 gosub dashes leth: n=4 goto lastdot seven: ' dash dash S n=2 letbopt: gosub dashes lets: n=3 goto lastdot eight: ' dash dash dash i n=3 dashi: gosub dashes leti: n=2 goto lastdot nine: ' dash dash dash dash E n=4 goto dashe leta: ' dot T n=1 goto letuv letb: ' dash S n=1 goto letbopt letc: ' dash R n=1 gosub dashes letr: ' dot N n=1 gosub dots letn: ' dash E n=1 dashe: gosub dashes lete: n=1 goto lastdot letd: ' dash i n=1 gosub dashes goto leti letf: ' dot N n=2 gosub dots goto letn letg: ' dash dash E n=2 goto dashe letj: ' dot O n=1 gosub dots goto leto letk: ' dash A n=1 gosub dashes goto leta letl: ' dot D n=1 gosub dots goto letd letp: n=1 gosub dots goto letg letq: ' dash dash A n=2 gosub dashes goto leta letu: ' dot dot T n=2 goto letuv letv: ' dot dot dot T n=3 goto letuv letx: ' dash U n=1 gosub dashes goto letu lety: ' dash W n=1 gosub dashes letw: ' dot M n=1 goto dotletm letz: ' dash dash i n=2 gosub dashes goto leti probt: ' dash + V n=1 gosub dashes goto letv proar: ' dot + C n=1 gosub dots goto letc prokn: ' dash + P n=1 gosub dashes goto letp prosk: ' dot dot dot + K n=3 gosub dots goto letk comma: ' dash dash dot dot M n=2 gosub dashes n=2 goto dotletm period: ' dot dash dot dash A n=1 gosub dots n=1 gosub dashes n=1 gosub dots n=1 gosub dashes goto leta slash: 'dash F n=1 gosub dashes goto letf quest: ' dot dot Z n=2 gosub dots goto letz dashes: if n=0 then ret HIGH LED HIGH KEY FreqOut SPEAKER,dash,tone LOW LED LOW KEY pause dot n=n-1 goto dashes dots: if n=0 then ret HIGH LED HIGH KEY FreqOut SPEAKER,dot,tone LOW LED LOW KEY pause dot n=n-1 goto dots ' Keyboard mode keyboard: wpm=15 HSEROUT ["Entering keyboard mode",13,10] inloop: ' Read a character LOW CTS HSERIN [ch] HIGH CTS gosub setwpm ' fold to upper case if ch<"a" then uppercase ch=ch & $DF ' make upper case! uppercase: ' check for local commands if ch="+" then speedup if ch="-" then speeddown if ch="~" then if wpm=0 then wpm=15 goto showspeed else wpm=0 hserout ["Manual speed control",13,10] endif goto inloop endif if ch="!" then high ptt goto inloop endif if ch="*" then low ptt goto inloop endif if ch=8 then ' send error gosub proerr goto inloop endif ' ok send character gosub send_char goto inloop proerr: n=8 goto lastdot speedup: if wpm<35 then wpm=wpm+1 endif showspeed: hserout ["WPM=",dec wpm,13,10] goto inloop speeddown: if wpm>1 then wpm=wpm-1 endif goto showspeed
Site contents © 1997-2012 by AWC, 310 Ivy Glen, League City, TX 77573 (281) 334-4341