; Filename: palm3x.asm ; ; Author: Chuck Olson, WB9KZY ; Company: Jackson Harbor Press ; jacksonharbor@.att.net ; http://jacksonharbor.home.att.net/ ; ; ; palm3x.asm is a VERY basic Morse code keyboard program that allows a ; PIC 16f628a to get input from a Palm portable keyboard (for the ; Palm m100, III, VII series of Palm handhelds) and transmit the ; keyed in characters as Morse code. ; ; The code speed can be set from 5 to 51 wpm by pressing the ctrl ; switch and then entering the code speed. The code speed is ; stored in EEPROM and is initialized to 16 WPM. If the code ; speed is changed, the new speed is written to EEPROM and it ; will be loaded at the next powerup sequence. ; ; Key presses (on make, release is ignored except for the ctrl key) ; are buffered in a 92 character first in - first out buffer. ; ; The sidetone is fixed at about 600 Hz, see the labels tone2 and ; tone2a to change the sidetone to about 750 Hz. Other frequencies ; may be programmed by consulting PWM section in the 16F628a manual. ; ; Mouser #: 665-AT-142 is an excellent though large, piezo speaker ; suitable for use with this project. ; ; If a Palm keyboard is attached, the PIC will send: FB after ; power is turned on. Note that the FB is sent through the ; output transistor. If no keyboard is attached, no FB is sent. ; ; All letters and numbers are supported and also 5 special ; characters are supported: ; ; key character in Morse ; --- ------------------ ; , --**-- ; . *-*-*- ; = -***- ; \ -**-* ; / **--** (unshifted /? key) ; ; More characters can be added OR the keys to which the above ; are associated with can be changed by editing the numbers table. ; ; Ideas for possible features that the user might add: ; memory play/record, use date, phone, todo, memo keys on right ? ; beacon mode, for CQ sends and regular beacons ; external serial eeprom for larger/more memories ; command mode to allow non-transmitted characters ; command sidetone, to distinguish between normal sending and commands ; variable sidetone, add a routine to set/save the sidetone frequency ; higher speeds ? ; add kill to flush buffer and stop memory play ; add sidetone OFF mode, for use with rigs that have sidetone ; LCD display of the sent code ? ; add last character delete (use back space) for buffer ; weight control ; Farnsworth lower speeds ; change program to imitate a PC keyboard, allows hooking the Palm ; keyboard to an existing PC keyboard based Morse keyboard project. ; buffer "fullness" status LEDs - turn on when x bytes are left ? ; try other brands of keyboard for the Palm m100, III and VII series ? ; pot speed control ? ; keyer functionality ; ; 16F628a Pinout: (see the file: palmkeybd.pdf for a full schematic) ; ; pin # name connection ; ----- ----- ---------- ; 1 RA2 ; 2 RA3 ; 3 RA4 ; 4 RA5 10k pullup to +3V ; 5 VSS Ground ; 6 RB0 ; 7 RB1 RX - rs232 input from Palm keyboard thru 2n7000 inv ; 8 RB2 ; 9 RB3 sidetone output to Piezo speaker ; 10 RB4 ; 11 RB5 ; 12 RB6 ; 13 RB7 hotsync input ; 14 VDD +3V supply ; 15 osc1 4 Mhz crystal plus 27pF capacitor to ground ; 16 osc2 4 Mhz crystal plus 27pF capacitor to ground ; 17 RA0 RTS output to keyboard ; 18 RA1 Morse keyed output to 2n7000 keying transistor ; ; ; List of Whys (in other words, why did he do it like that ?): ; ; The 2n7000 inverter used to feed pin 7 was added because a ; hardware uart generally expects that the data has been ; inverted by some interface hardware (such as the MAX232 chip). ; The 10k pulldown resistor on the 2n7000 gate is required. ; A software uart wouldn't need the 2n7000 MOSFET. ; ; The Hotsync (HS) signal from the keyboard is connected to ; RB7 to allow the easy use of the PORT B interrupt on change ; to wake up the PIC, assert the RTS pin and to jump to the ; serial input interrupt routine. Subsequent serial inputs ; can use serial input (uart) interrupt instead of the hotsync ; blip if needed. ; ; A 4 MHz crystal was used, a 4 MHz resonator could also be used ; but an RC clock may not have the accuracy or stability needed ; for a 9600 baud serial link. ; ; A 3 volt supply was used to keep the power usage low, below ; 1 ma total for both the keyboard and PIC, much less ( < 20 uA) ; when they are both sleeping (clock stopped). The circuit may ; work at voltages up to 5.5 volts but hasn't been tried by me. ; ; The RS-232 circuit on the interface board was not used because ; this circuit can use more power than the PIC/Palm Keyboard and ; it also is NOT required as the Palm keyboard does work with ; normal 0 to 3 volt levels. ; ; The sidetone is generated with the PWM circuitry - this allows ; the user to easily perform other tasks while the sidetone is on. ; ; The dit and dah timing is performed using timer 1. The ; interrupt flag for timer 1 is polled rather than enabling ; the timer 1 interrupt. This makes for a slightly simpler ; interrupt routine. Hey, I was taught microcomputers by ; a guy who said: "never use interrupts !" (this was in the ; days of the Intel 8080) so consider me brave for using ; interrupts at all ;) list p=16F628a ; list directive to define processor #include ; processor specific variable definitions __FUSES _PWRTE_OFF & _CP_OFF & _WDT_OFF & _XT_OSC & _LVP_OFF & _MCLRE_ON & _BODEN_OFF ; powerup timer off - still not sure why ; copy protect is off ; watchdog timer is off ; the oscillator uses an external 4 MHz crystal or resonator ; turn off the low voltage programming capability - RB4 can be used as I/O ; turn on the mclr pin ; turn off the brown out detect (boden) as operation at 3V anticipated ;Variable locations in bank0 CBLOCK 0x20 temp: 1 ;used in bin2dec char: 1 ;holder for character data - used in playchr, strtbit size: 1 ;char size counter - used in playchr, strtbit third: 1 ;holder for number of dit length spaces => ditsp temp2: 1 ;holder for element length => outdit, outdah, ditsp sammo: 1 ;temp location for speed2 tonef2: 1 ;temp location for tone frequency calcs spd: 1 ;current Morse code speed tens: 1 ;temp location used in playchr EEADDR: 1 ; used in internal eeprom routines recv: 1 ;pointer to next byte to be read xmit: 1 ;pointer to next byte to be sent inbyte: 1 ;used in interrupt routine keycod: 1 ;keycode from keyboard or buffer downcnt: 1 ;down counter for main loop prior to sleeping flags: 1 ;ram flags for various funtions newsped: 1 ;new speed holder for spedset digit: 1 ;digit holder for spedset eedatb0: 1 ;eeprom data holder byte for write (wreep) ENDC ; note that placing saves, savef and savew at the top of bank 0 ; enables easy access in any of the other banks ; these three are used in the interrupt routine to hold the ; calling state of the PIC during the actual interrupt operations CBLOCK 0x7D saves: 1 ;interrupt temp storage savef: 1 ;interrupt temp storage savew: 1 ;interrupt temp storage ENDC ; the keyboard buffer is placed into bank 1 where it is easily ; aceessed using indirection even if bank 0 is selected bufstrt equ H'A0' ;start of character buffer A0 to FC = 92 chars bufstop equ H'FC' ;end of buffer ; bit definitions w equ 0 ;destination is w register f equ 1 ;destination is NOT the w register zero equ 2 ;zero bit in STATUS register carry equ 0 ;carry bit keypr equ 0 ;key press bit ;PORTA bit definitions: owt equ 1 ;porta, bit 1 is the keyed output RTS equ 0 ;port a bit 0 is the RTS output to the palm ;PORTB bit definitions: CCP1 equ 3 ;output to piezo (tone - PWM) hs equ 7 ;hotsync input ORG 0x000 ; processor reset vector bcf STATUS,RP0 ;select bank 0 bcf STATUS,RP1 ; for direct addressing bcf STATUS,IRP ;select bank 0 and 1 for indirect addressing goto init ; start here by initializing the PIC ORG 0x004 ; interrupt vector location goto rupt ;jump into the interrupt routine ;why not have the routine in-line ? ; to be sure to allow the tables ; to exist in page 0 ; the int. routine doesn't need page 0 ; table usage is simpler in page 0 ; speed - a lookup table for the code speed. it translate the speed ; setting to a dit length. The table entry is two bytes ; wide and represents a count loaded into timer 1 - when the count ; rolls over from FFFF to 0, the element is finished - so the ; table entry is basically 65536-element length. At 4 MHz clock, ; each tick of timer1 is 1/125000 or .008 ms or 8 microseconds speed addwf PCL,f ;compute the jump value retlw d'138' ; 0 wpm (actually 5 wpm) retlw d'208' retlw d'138' ; 1 wpm (actually 5 wpm) retlw d'208' retlw d'138' ; 2 wpm (actually 5 wpm) retlw d'208' retlw d'138' ; 3 wpm (actually 5 wpm) retlw d'208' retlw d'138' ; 4 wpm (actually 5 wpm) retlw d'208' retlw d'138' ; 5 wpm retlw d'208' retlw d'158' ; 6 wpm retlw d'88' retlw d'172' ; 7 wpm retlw d'75' retlw d'182' ; 8 wpm retlw d'194' retlw d'190' ; 9 wpm retlw d'229' retlw d'197' ; 10 wpm retlw d'104' retlw d'202' ; 11 wpm retlw d'188' retlw d'207' ; 12 wpm retlw d'44' retlw d'210' ; 13 wpm retlw d'238' retlw d'214' ; 14 wpm retlw d'38' retlw d'216' ; 15 wpm retlw d'240' retlw d'219' ; 16 wpm retlw d'97' retlw d'221' ; 17 wpm retlw d'136' retlw d'223' ; 18 wpm retlw d'115' retlw d'225' ; 19 wpm retlw d'41' retlw d'226' ; 20 wpm retlw d'180' retlw d'228' ; 21 wpm retlw d'25' retlw d'229' ; 22 wpm retlw d'94' retlw d'230' ; 23 wpm retlw d'134' retlw d'231' ; 24 wpm retlw d'150' retlw d'232' ; 25 wpm retlw d'144' retlw d'233' ; 26 wpm retlw d'119' retlw d'234' ; 27 wpm retlw d'76' retlw d'235' ; 28 wpm retlw d'19' retlw d'235' ; 29 wpm retlw d'204' retlw d'236' ; 30 wpm retlw d'120' retlw d'237' ; 31 wpm retlw d'25' retlw d'237' ; 32 wpm retlw d'176' retlw d'238' ; 33 wpm retlw d'63' retlw d'238' ; 34 wpm retlw d'196' retlw d'239' ; 35 wpm retlw d'66' retlw d'239' ; 36 wpm retlw d'185' retlw d'240' ; 37 wpm retlw d'42' retlw d'240' ; 38 wpm retlw d'149' retlw d'240' ; 39 wpm retlw d'250' retlw d'241' ; 40 wpm retlw d'90' retlw d'241' ; 41 wpm retlw d'181' retlw d'242' ; 42 wpm retlw d'13' retlw d'242' ; 43 wpm retlw d'96' retlw d'242' ; 44 wpm retlw d'175' retlw d'242' ; 45 wpm retlw d'251' retlw d'243' ; 46 wpm retlw d'67' retlw d'243' ; 47 wpm retlw d'137' retlw d'243' ; 48 wpm retlw d'203' retlw d'244' ; 49 wpm retlw d'11' retlw d'244' ; 50 wpm retlw d'72' retlw d'244' ; 51 wpm retlw d'131' ; table of equivalent morse characters ; character structure from Dave Gwillym, KB2TQX ; right justified storage of the character: ; dit = 0 ; dah = 1 ; the leftmost 1 is a start bit - the byte is left shifted until a ; 1 is seen, then the dits and dahs are shifted out for a ; total of 8 shifts - this holds most normal characters. ; note that the table position is what is stored in the eeprom ; ; the exceptions to the above are non-morse characters which ; have a table entry of all zeroes AND the two space keys ; which have table entries of all ones. The all zero and all ; one entries are either ignored or decoded by the calling routines. ; ; character hex table position numbers addwf PCL,f ;compute the jump value ; ascii Y X dec retlw b'00101111' ; 1 0000000 0 retlw b'00100111' ; 2 0000001 1 retlw b'00100011' ; 3 0000010 2 retlw b'00011100' ; Z 0000011 3 retlw b'00100001' ; 4 0000100 4 retlw b'00100000' ; 5 0000101 5 retlw b'00110000' ; 6 0000110 6 retlw b'00111000' ; 7 0000111 7 retlw b'00000000' ; command 0001000 8 retlw b'00011101' ; Q 0001001 9 retlw b'00001011' ; W 0001010 10 retlw b'00000010' ; E 0001011 11 retlw b'00001010' ; R 0001100 12 retlw b'00000011' ; T 0001101 13 retlw b'00011011' ; Y 0001110 14 retlw b'00000000' ; tilde 0001111 15 retlw b'00011001' ; X 0010000 16 retlw b'00000101' ; A 0010001 17 retlw b'00001000' ; S 0010010 18 retlw b'00001100' ; D 0010011 19 retlw b'00010010' ; F 0010100 20 retlw b'00001110' ; G 0010101 21 retlw b'00010000' ; H 0010110 22 retlw b'11111111' ; space1 0010111 23 retlw b'00000000' ; caps lock 0011000 24 retlw b'00000000' ; tab 0011001 25 retlw b'00000000' ; ctrl 0011010 26 retlw b'00000000' ; 0011011 27 retlw b'00000000' ; 0011100 28 retlw b'00000000' ; 0011101 29 retlw b'00000000' ; 0011110 30 retlw b'00000000' ; 0011111 31 retlw b'00000000' ; 0100000 32 retlw b'00000000' ; 0100001 33 retlw b'00000000' ; FN 0100010 34 retlw b'00000000' ; alt 0100011 35 retlw b'00000000' ; 0100100 36 retlw b'00000000' ; 0100101 37 retlw b'00000000' ; 0100110 38 retlw b'00000000' ; 0100111 39 retlw b'00000000' ; 0101000 40 retlw b'00000000' ; 0101001 41 retlw b'00000000' ; 0101010 42 retlw b'00000000' ; 0101011 43 retlw b'00011010' ; C 0101100 44 retlw b'00010001' ; V 0101101 45 retlw b'00011000' ; B 0101110 46 retlw b'00000110' ; N 0101111 47 retlw b'00000000' ; - 0110000 48 retlw b'00110001' ; = 0110001 49 retlw b'00000000' ; backspace 0110010 50 retlw b'00000000' ; spec fn 1 0110011 51 retlw b'00111100' ; 8 0110100 52 retlw b'00111110' ; 9 0110101 53 retlw b'00111111' ; 0 0110110 54 retlw b'11111111' ; space2 0110111 55 retlw b'00000000' ; [ 0111000 56 retlw b'00000000' ; ] 0111001 57 retlw b'00110010' ; \ 0111010 58 retlw b'00000000' ; spec fn 2 0111011 59 retlw b'00001001' ; U 0111100 60 retlw b'00000100' ; I 0111101 61 retlw b'00001111' ; O 0111110 62 retlw b'00010110' ; P 0111111 63 retlw b'00000000' ; ' 1000000 64 retlw b'00000000' ; enter 1000001 65 retlw b'00000000' ; spec fn 3 1000010 66 retlw b'00000000' ; 1000011 67 retlw b'00010111' ; J 1000100 68 retlw b'00001101' ; K 1000101 69 retlw b'00010100' ; L 1000110 70 retlw b'00000000' ; ; 1000111 71 retlw b'01001100' ; ? and / 1001000 72 retlw b'00000000' ; scroll up 1001001 73 retlw b'00000000' ; spec fn 4 1001010 74 retlw b'00000000' ; 1001011 75 retlw b'00000111' ; M 1001100 76 retlw b'01110011' ; , 1001101 77 retlw b'01010101' ; . 1001110 78 retlw b'00000000' ; DONE 1001111 79 retlw b'00000000' ; DEL 1010000 80 retlw b'00000000' ; left arrow 1010001 81 retlw b'00000000' ; scroll down 1010010 82 retlw b'00000000' ; right arrow 1010011 83 retlw b'00000000' ; 1010100 84 retlw b'00000000' ; 1010101 85 retlw b'00000000' ; 1010110 86 retlw b'00000000' ; 1010111 87 retlw b'00000000' ; shift left 1011000 88 retlw b'00000000' ; shift right 1011001 89 retlw b'00000000' ; 1011010 90 retlw b'00000000' ; 1011011 91 retlw b'00000000' ; 1011100 92 retlw b'00000000' ; 1011101 93 retlw b'00000000' ; 1011110 94 retlw b'00000000' ; 1011111 95 ; rupt - the interrupt routine - go here when the hotsync is pulsed ; OR if the RCIF uart flag is set ; this interrupt service routine will: ; ; 1) get the key code ; 2) convert the key code to Morse ; a) if it's a ctrl key press, go and set the code speed ; 3) fill the circular buffer ; two pointers are used ; recv points to the next location to be filled with data ; xmit points to the next location to be played ; 4) increment a buffer fill counter byte ; ; per an email from Steve Weber, (KD1JV): ; ; at powerup, zero the buffer - both pointers to start ; ; when you get a char from the keyboard (via interrupt) ; look at byte pointed to by recv - if <> 0 then you've overflowed ; if = 0 then it's OK, put the key code into the byte ; ; when you send a key code, write a zero into it's location ; ; rupt ;save the status and w registers before proceeding to the int service movwf savew ;save the w register swapf STATUS,w ;load the status register into w movwf saves ;save the status register swapf FSR,w ;load the indirect address register movwf savef ;save the indirect address register ; ; explicitly set the ram pointers to the base banks bcf STATUS,RP0 ;be sure we are in bank 0 for direct bcf STATUS,RP1 ;be sure we are in bank 0 for direct bcf STATUS,IRP ;be sure we are using bank 0 & 1 for indirect ; ; get a byte from the keyboard bsf PORTA,RTS ;raise RTS since hotsync has gone high, call in232 ;get the byte from the uart ; ; next, store that key code temporarily movwf inbyte ;save the key code in inbyte btfsc inbyte,7 ;is bit 7 high ? goto cbreak ;yes, skip this char, it's a break ; ; is this a control key press ? sublw b'00011010' ;test for a btfsc STATUS,zero ; ctrl key press call spedset ;yes, do a speed set ; ; test the buffer, is it full ? bufit movf recv,w ;load up the recv pointer movwf FSR ;put recv pointer into the indirect address movf INDF,w ;get the contents of the buffer btfss STATUS,zero ;is this byte a zero? goto cbreak ; no, bail ; ; store the key code in the circular buffer and test the next buffer loc. storchr movlw d'96' ;highest possible address in numbers table + 1 subwf inbyte,w ;perform inbyte - 96 btfsc STATUS,carry ;test the carry bit goto cbreak ;inbyte is either 96 or more, skip this keycode movf inbyte,w ;load up the current key code call numbers ;convert it to Morse ; ; test the resulting Morse before putting it into the buffer movwf inbyte ;reuse inbyte to hold the Morse movf inbyte,w ;reload inbyte to perform a zero test btfsc STATUS,zero ;is this byte a zero? goto cbreak ; yes, bail movwf INDF ;save it in the buffer bsf flags,keypr ;set the key press flag incf recv,f ; no, increment the recv pointer movlw bufstop+1 ;load up the last possible buffer location + 1 subwf recv,w ; is the recv pointer (recv-(bufstop+1)) btfss STATUS,carry ; less than the last possible buff loc + 1? goto cbreak ; yes, just exit the interrupt routine movlw bufstrt ; no, reset the recv pointer movwf recv ; to the bufstrt ; cbreak ;restore the status and w registers ;so that the movwf & swapf instructions don't affect STATUS bits swapf savef,w ;unswap the saved FSR register into w movwf FSR ;put it back into FSR swapf saves,w ;unswap the saved status register into w movwf STATUS ;put it back into STATUS swapf savew,f ;swap the nibbles of the saved w register swapf savew,w ;swap them again into the w register bcf INTCON,RBIF ;clear the port b change interrupt flag retfie ;return from interrupt, end of rupt routine ; in232 - get the serial character (ascii), put it into w in232 btfsc RCSTA,OERR ;has a overrun error happened ? call ovrerr ;yes, handle the error btfss RCSTA,FERR ;has a framing error happened ? goto ovrferr ;no, skip the frame error handling movf RCREG,w ;reading the uart clears the FERR flag goto in232 ;try it again ovrferr btfss PIR1,RCIF ;is the buffer full ? goto in232 ;no, wait until it is full movf RCREG,w ;load w with the received character return ;end of in232 routine ; ovrerr - a way to clear the overflow error bit ovrerr bcf RCSTA,CREN ;turn off cren movf RCREG,w ;flush movf RCREG,w ; the movf RCREG,w ; fifo bsf RCSTA,CREN ;turn on cren again return ;end of ovrerr routine ; speed2 - a routine to save a few bytes by putting a 3 statement segment ; into a subroutine that is called 3 times. speed2 bcf STATUS,carry ;clear the carry bit rlf spd,w ;multiply speed table pointer by 2, put in w movwf sammo ;save it temporarily in sam call speed ;get the high byte movwf TMR1H ;save it in the timer 1 high byte incf sammo,w ;increment the pointer and load into w call speed ;get the low byte movwf TMR1L ;save it in the timer 1 low byte ovradj bcf PIR1,TMR1IF ;clear the timer 1 interrupt flag bsf T1CON,TMR1ON ;turn on timer 1 return ;end of speed2 routine ; outdah - a routine to send a dah outdah call toneon ;turn on the tone. bsf PORTA,owt ;actuate the keyed output ok2 movf spd,w ;load the current code speed call speed2 ;convert the spd pos to dit cycles loop4 btfss PIR1,TMR1IF ;poll the timer 1 overflow flag - is it set? goto loop4 ;no, keep polling movf spd,w ;load the current code speed call speed2 ;convert the spd pos to dit cycles loop4a btfss PIR1,TMR1IF ;poll the timer 1 overflow flag - is it set? goto loop4a ;no, keep polling goto dahent2 ;yes, enter the dit routine ; outdit - a routine to send a dit outdit bsf PORTA,owt ;actuate the keyed output call toneon ;turn on the tone. dahent2 movf spd,w ;load the current code speed call speed2 ;convert the spd pos to dit cycles loop5 btfss PIR1,TMR1IF ;poll the timer 1 overflow flag - is it set? goto loop5 ;no, keep polling ;yes, end the dit ditsp bcf PORTA,owt ;turn off the keyed output call toneoff ;turn off the sidetone movlw 0x01 ;setup the loop count for a dit space movwf third ; save it in third movf spd,w ;load the current code speed call speed2 ;convert the spd pos to dit cycles ovrsub2 goto ditsp3 ;enter the space routine ; wdsp - a routine to wait 6 dit spaces + 1 from last dit or dah = 7 total ; wdsp2 - a routine to wait 4 dit spaces + 3 from last char = 7 total ; chsp - a routine to wait 2 dit spaces + 1 from last dit or dah = 3 total ; mem loc used: third wdsp movlw 0x7 ;load up 6 dit spaces goto loadet ;branch in wdsp2 movlw 0x5 ;load up 4 dit space (an additional delay after chsp) goto loadet ;branch into the routine with an alterante chsp movlw 0x3 ;load up 2 dit spaces loadet movwf third ;save it in third - a temp reg decet decfsz third,f ;decrement the dit space counter - is it 0 ? goto ditsp2 ;no, start waiting goto dun2 ;yes, end the dit ditsp2 movf spd,w ;load the current code speed call speed2 ;convert the spd pos to dit cycles ditsp3 btfss PIR1,TMR1IF ;poll the timer 1 overflow flag - is it set? goto ditsp3 ;no, keep polling goto decet ;yes, check the dit space counter dun2 return ;end of wdsp routine ; toneoff - a routine to turn off the hardware PWM tone on pin 13 (RC2) toneoff bcf T2CON,TMR2ON ;turn off timer 2 (and tone?) clrf CCP1CON ;set the output to a zero bcf PORTB,CCP1 ;set the sidetone low ? bsf STATUS,RP0 ;select bank 1 bcf TRISB,CCP1 ;turn on the output pin bcf STATUS,RP0 ;select bank 0 return ;end of toneoff routine ; toneon - a routine to turn on the hardware PWM tone toneon bcf STATUS,RP0 ;set bank 0 bcf STATUS,RP1 ; for sure tone2 movlw d'103' ; 601 hz pr2 ; movlw d'82' ; 753 hz pr2 ovtone2 bsf STATUS,RP0 ;select bank 1 movwf PR2 ;save PR2 ; bank 0 bcf STATUS,RP0 ;select bank 0 tone2a movlw d'52' ; ccpr1l = 52; ccp1con = 0; 601 Hz ; movlw d'169' ; ccpr1l = 41; ccp1con = 2; 753 Hz movwf tonef2 ;save them temp in tonef2 for ccp1con setting andlw b'01111111' ;and out the ccp1con bit movwf CCPR1L ;set the duty cycle movlw b'00001100' ;5:4=0:0 for 50% duty, pwm mode btfsc tonef2,7 ;test the high order bit, if 0, skip the 11 setting movlw b'00111100' ;5:4=1:1 for 50% duty, pwm mode movwf CCP1CON ;set the duty cycle and other bits tone3 bsf STATUS,RP0 ;select bank 1 bcf TRISB,CCP1 ;turn on the output pin bcf STATUS,RP0 ;select bank 0 bsf T2CON,TMR2ON ;turn on timer 2 (and tone?) return ;enf of toneon routine ; deboun - wait for 20 ms ; delay = 2 (call) + 2 (return) + 2 setup + (2000 x 10 ) - 1 = 20,005 deboun movlw d'10' ;1 1 load up a counter movwf temp2 ;1 2 store it in temp2 dbounc call halfcy2 ;wait 1997 usec decfsz temp2,f ;1 3 decrement the loop count - skip at 0 goto dbounc ;2 5 count not zero, loop back return ;2 end of debounce routine ; halfcy2 - a routine to wait a specified period of time: 1997 us ; at PIC clock = 4 MHz, each PIC instruction takes 1 or 2 usec. ; delay = 2 (call) + 2 (return) + 5 (routine) + ((freq x 9) - 1) loop delay freq2 equ d'221' ; 8 + (221 x 9) = 1997 halfcy2 movlw freq2 ;1 1 load up the 1/2 cycle delay constant movwf temp ;1 2 store it in the temporary location goto waste3 ;2 4 waste 2 cycles waste3 nop ;1 5 waste a cycle loopy2 goto fake1 ;2 waste 2 cycles fake1 goto fake2 ;2 and another 2 cycles fake2 goto fake3 ;2 and another 2 cycles fake3 decfsz temp,f ;1 decrement the temp location - is it 0 ? goto loopy2 ;2 no, keep decrementing return ;2 500 yes, end of half cycle routine ; spedset - a routine to set the Morse code speed of the keyboard ; while this routine does work, it's kinda big and clumsy, feel ; free to streamline it as you'd like ;) spedset clrf newsped ;clear the new speed holder spedse1 call in232 ;get the next key press movwf inbyte ;save the key code in inbyte btfsc inbyte,7 ;is bit 7 high ? goto ctrlrls ; yes, go and see if it's a ctrl release ;no, it's not a ctrl release, but what is it ? movf inbyte,w ;load the key code btfss STATUS,zero ;is it equal to zero ? goto test2 ;no, skip to the next test movlw d'1' ;yes, the key code is zero savcalc movwf digit ; which means that the key pressed is a 1 movf newsped,w ;test newsped btfsc STATUS,zero ;is it zero ? goto justsav ;yes, just save digit as newsped addwf newsped,f ;no, multiply newsped by 10 and add digit addwf newsped,f ;3x addwf newsped,f ; 4x addwf newsped,f ; 5x addwf newsped,f ; 6x addwf newsped,f ; 7x addwf newsped,f ; 8x addwf newsped,f ; 9x addwf newsped,f ; 10x justsav movf digit,w ;load digit addwf newsped,f ;add digit to newsped goto spedse1 ;loop back for another key test2 sublw d'1' ;test for a 1 btfss STATUS,zero ; key code goto test3 ;no, it's not = 1, do the next test movlw d'2' ;yes, the key code is one goto savcalc ; so save the 2 and then calculate the speed test3 movlw d'2' ;test for a 2 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test4 ;no, it's not = 1, do the next test movlw d'3' ;yes, the key code is one goto savcalc ; so save the 3 and then calculate the speed test4 movlw d'4' ;test for a 4 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test5 ;no, it's not = 1, do the next test movlw d'4' ;yes, the key code is 4 goto savcalc ; so save the 4 and then calculate the speed test5 movlw d'5' ;test for a 5 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test6 ;no, it's not = 1, do the next test movlw d'5' ;yes, the key code is 5 goto savcalc ; so save the 5 and then calculate the speed test6 movlw d'6' ;test for a 6 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test7 ;no, it's not = 1, do the next test movlw d'6' ;yes, the key code is 6 goto savcalc ; so save the 6 and then calculate the speed test7 movlw d'7' ;test for a 7 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test8 ;no, it's not = 1, do the next test movlw d'7' ;yes, the key code is 7 goto savcalc ; so save the 7 and then calculate the speed test8 movlw d'52' ;test for a 52 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test9 ;no, it's not = 1, do the next test movlw d'8' ;yes, the key code is 52 goto savcalc ; so save the 8 and then calculate the speed test9 movlw d'53' ;test for a 53 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto test0 ;no, it's not = 1, do the next test movlw d'9' ;yes, the key code is 53 goto savcalc ; so save the 7 and then calculate the speed test0 movlw d'54' ;test for a 54 subwf inbyte,w ; key code btfss STATUS,zero ; perform the comparision goto spedse1 ;no, it's not any number, get another key movlw d'0' ;yes, the key code is 54 goto savcalc ; so save the 0 and then calculate the speed ctrlrls sublw b'10011010' ;test for btfss STATUS,zero ; ctrl key release goto spedse1 ;loop back for another key ;now test newsped to see if it makes sense, if 0 or > 51, bail out movf newsped,f ;test newsped btfsc STATUS,zero ;is newsped 0 ? goto finss ;yes, skip the spd update movlw d'52' ;ctrl has been released subwf newsped,w ; now test newsped against max speed + 1 btfsc STATUS,carry ;if the carry is set, newsped is too big goto finss ;exit without changing spd movf newsped,w ;newsped is less than 52 movwf spd ;save it as the keyboard Morse code speed clrf EEADDR ;clear the EEPROM address call wreep ;save the new speed in eeprom address 0 finss return ;end of spedset ; rdeep - a routine to read the data in the eeprom pointed to by EEADDR ; and put it into w & eedatb0 rdeepi incf EEADDR,1 ;increment the EEPROM address rdeep bcf STATUS,RP0 ;set bank 0 for sure - prob not needed bcf STATUS,RP1 ; movf EEADDR,0 ;load up the internal eeprom address rdeebri bsf STATUS,RP0 ;set bank 1 movwf EEADR ;store it in the EEPROM address buffer bsf EECON1,RD ;EEPROM read movf EEDATA,0 ;load up eedata into w bcf STATUS,RP0 ;bank 0 return ;end of read eeprom routine ; wreep - a routine to write the data in w into the address EEADDR ; write sequence from the 16F87x spec - for the internal 16f87x eeprom. ; This routine was modififed from the one used in the keybdf84 program. wreep movwf eedatb0 ;save the byte to be written temporarily movf EEADDR,0 ;load up the internal eeprom address bsf STATUS,RP0 ;set bank 1 movwf EEADR ;store is in the EEPROM address buffer bcf STATUS,RP0 ;set bank 0 movf eedatb0,0 ;load up the data to be written bsf STATUS,RP0 ;set bank 1 movwf EEDATA ;store in the eeprom data buffer bsf STATUS,RP0 ;set the bank bit - 1 bsf EECON1,WREN ;enable write movlw H'55' ;load up a 55h per the 16f84 spec movwf EECON2 ;write the 55h movlw H'AA' ;load up AAh per the 16f84 spec movwf EECON2 ;write the AAh bsf EECON1,WR ;set the WR bit and thus begin writing to eeprom bcf EECON1,WREN ;disable write bcf STATUS,RP0 ;reset the bank bit to 0 checkit btfss PIR1,EEIF ;see if the last write is complete goto checkit ;no, check it again bcf PIR1,EEIF ;clear the write complete interrupt flag bit return ;end of wreep routine ; init - initialize the keyer from a powerup or reset init ; bank 0 clrf EEADDR ;clear the EEPROM address call rdeep ;get eeprom byte 0 movwf spd ;save it in spd clrf downcnt ;init various clrf flags ; ram variables clrf EEADDR ;clear the EEPROM address clrf CCP1CON ;CCP Module is off clrf TMR2 ;Clear Timer 2 clrf PORTA ;clear all bits of PORTA bcf PORTA,RTS ;set the RTS low clrf PORTB ;clear all bits of PORTB movlw b'00001000' ;b3=1, port b int on chg movwf INTCON ;set the interrupts for wake on GP change movlw b'00110000' ;prescale = 8, int clock, timer off movwf T1CON ;setup timer 1 ;50 % duty cycle at PR2=10 => 1/2 * 416 = 208 = 11010000 b ; so CCPR1L = 52 (110100) and CCP1CON(5:4) = 0 (00) movlw b'00001100' ;5:4=0:0 for 50% duty, pwm mode movwf CCP1CON ;set the duty cycle and other bits clrf PIR1 ;clear peripheral interrupt flags ; bank 1 bsf STATUS,RP0 ;select bank 1 clrf PIE1 ;disable peripheral interrupts clrf TRISA ;all port a pins are outputs movlw b'10000010' ;RX pin (rb1), rb7 are inputs movwf TRISB ;replace the tris instruction movlw b'10000000' ;port b pullups disabled movwf OPTION_REG ;replace the option instruction makes assem hap ; bank 0 bcf STATUS,RP0 ;select bank 0 movlw b'00000010' ;timer 2 off, 1:1 postscale, 16 prescale movwf T2CON ; bank 1 bsf STATUS,RP0 ;select bank 1 ; ; setup the usart - asynch rate equ 0x19 ;constant for 9600 baud @ 4 MHz movlw rate ;load up the baud constant movwf SPBRG ;set the baud movlw b'00100100' ;TXSTA bit settings, meaning ; . : |------TX9D = 0 => 9th sent bit ; . : TRMT = 0 => tsr full ; . :....... BRGH = 1 => high speed ; . unused ; . SYNC = 0 => asynch mode ; . . . . . . TXEN = 1 => enable xfer ; TX9 = 0 => 8 bit xfer ;-------------CSRC = 0 => don't care movwf TXSTA ;set the transmit register bsf PIE1,RCIE ;enable the UART receive interrupt ; bank 0 bcf STATUS,RP0 ;select bank 0 movlw b'10010000' ;constant for receive and serial port enabled ; .| : |------RX9D = 0 => 9th received bit ; .| : OERR = 0 => overrun error bit ; .| :....... FERR = 0 => framing error bit ; .| ADEN = 0 => address detect enable bit, unused ; .+--------- CREN = 1 => enables continuous receive ; . . . . . . SREN = 0 => single receive enable bit, unused ; RX9 = 0 => 8 bit reception ;------------ SPEN = 1 => serial port enabled movwf RCSTA ;set the receive register movlw b'00000111' ;turn off the movwf CMCON ; analog comparator pins pollhs btfss PORTB,hs ;test the hotsync pin goto pollhs ;wait until it goes high bsf PORTA,RTS ;raise RTS for a while call deboun ;wait a little getinit call in232 ;get a key code from the Palm keyboard movwf keycod ;save key code temporarily (should it be tested ?) secbyte btfss PIR1,RCIF ;is the buffer full ? goto secbyte ;wait for the second byte call in232 ;get a key code from the Palm keyboard movwf keycod ;save key code temporarily (should it be tested ?) bcf PORTA,RTS ;lower RTS for a while ; are these deboun calls needed ? call deboun call deboun call deboun call deboun call deboun call initbuf ;initialize the buffer movlw b'00010010' ;load an F call pc2 ;play it movlw b'00011000' ;load a B call pc2 ;play it bsf INTCON,PEIE ;enable the peripheral interrupts ; mainlup - the main program loop mainlup movlw d'50' ;load up 1 second count movwf downcnt ; of 20 ms calls mainlu2 call deboun ;wait 20 ms btfsc flags,keypr ;test the key press flag goto ml3 ;a key was pressed, start emptying the buffer decfsz downcnt ;decrement the down counter goto mainlu2 ;loop until the count is 0 call ovrerr ;clear the RCIF flag bsf INTCON,GIE ;enable the interrupts bcf INTCON,RBIF ;clear the port b change interrupt flag bcf PORTA,RTS ;lower RTS to power down the keyboard sleep ;konk out until an hs pulse happens call deboun ;wait a while for xmit to get loaded the ml3 movf xmit,w ;load up the xmit pointer movwf FSR ;put xmit pointer into the indirect address movf INDF,w ;get the contents of the buffer btfsc STATUS,zero ;is this byte a zero? goto mainlup ;yes the buffer is empty, loop back and sleep ;no, it's a good character so drop through to play232 ; play232 - a routine to get a char from rs232 input and then play it play232 call pc2 ;play the Morse character clrf INDF ;zero the buffer incf xmit,f ;increment the xmit pointer movlw bufstop+1 ;load up the last possible buffer location + 1 subwf xmit,w ; is the xmit pointer btfss STATUS,zero ; equal to the last possible buffer loc? goto ml3 ;no, loop back for another character movlw bufstrt ;yes, reset the xmit pointer movwf xmit ; to the bufstrt goto ml3 ;no, loop back for another character ; playchr - a routine to play a character stored in w ; mem loc used: char, tens, size playchr call numbers ;lookup the value pc2 movwf char ;save it in char movwf tens ;save it in tens comf tens,w ;invert the character btfsc STATUS,zero ;skip playing the char if it is zero (FF before inv) goto spacey ;play a word space getdig3 call strtbit ;rotate char left until the start bit comes up shout3 rlf char,f ;rotate the msb into carry btfsc STATUS,carry ;test the carry - 0=dit, 1=dah goto dahsnd3 ;it's a one, send a dah call outdit ;send a dit goto sizdec ;go check the size variable dahsnd3 call outdah ;send the dah sizdec decfsz size,f ;decrement the char size counter, is it 0? goto shout3 ;no, go back for the next bit call chsp ;yes, send a character space pcend bcf flags,keypr ;clear the key press flag return ; end of playchr routine spacey call wdsp2 ;send a word space goto pcend ;exit the routine ; strtbit - a routine to rotate a char left until a 1 appears strtbit movlw 0x8 ;load up the maximum char size + 1 movwf size ;save it temp strbit2 decf size,f ;decrement the element counter xrotit rlf char,f ;rotate the msb into carry btfss STATUS,carry ;test the carry - 0=placeholder, 1=start bit goto strbit2 ;it's a just a placeholder (0), keep shifting return ;you're done - end of the startbit routine ; initbuf - clear the buffer to all zero and set the pointers initbuf movlw bufstrt ;load up the start of the buffer movwf FSR ;set the buffer pointer agn clrf INDF ;clear the current buffer location incf FSR,f ;increment the memory pointer movlw bufstop+1 ;lets look at the element counter subwf FSR,w ;is the element counter btfss STATUS,zero ; less than the message end address? goto agn ;yes, they are unequal - keep going movlw bufstrt ;load up the starting address of the buffer movwf xmit ;set the transmit buffer pointer movwf recv ;set the receive buffer pointer movwf FSR ;set the buffer pointer, too return ;end of initbuf routine ; preload the 16F628a internal eeprom org 0x2100 ;starting address for 16F628 internal eeprom de d'16' ;16 wpm code speed at first powerup end ;end of the palm3x.asm, Morse code Palm keyboard program