; 10MINF84.ASM - Morse Code Speedometer using the 16F84 PIC ; ; 2/7/2000 Chuck Olson, WB9KZY ; Jackson Harbor Press ; http://jacksonharbor.home.att.net ; jacksonharbor@att.net ; ; What does it do? ; ; This program uses a 16f84 to wait for 10 minutes after the switch ; input is pressed - it will wink an LED once a second - then when 10 ; minutes have elapsed, the PIC will send a Morse code message through ; both the piezo speaker and an output transistor. The Morse message ; can be up to 60 characters long. When the timer is not in use the ; PIC is put to sleep to save power and reduce interference. There is ; also an LED output - the LED is "winked" once per second while the ; delay proceeds to let the user know that the PIC is active. ; ; There is another output pin used to control a relay - this might be ; useful for non-ham apps of the timer such as an EPROM eraser timer? ; ; The timer can be automatically retriggered after the initial delay ; by connecting the retrig pin to +5V. The timer delay OR the message ; send can be interrupted by a press and release of the switch input. ; The timer can output a once a second click to the piezo if the ; click input is connected to +5V ; ; The message, the delay time and the code speed are stored in ; eeprom memory and can be changed using a PIC programmer or using ; a nasty little recording routine stored in the PIC. This routine ; uses the click and retrig inputs to set the various parameters. ; The eeprom change routine (MENU) is entered with a press and hold ; of the switch input for 2 seconds - after this initial hold, the ; PIC will send: ; ; MIN? x (where x is the current minute delay) ; ; The user can then use the retrig input to decrease the delay by ; connecting it to +5V OR use the click input to increase the delay by ; connecting it to +5V. The PIC will send the changed minute delay ; and then check the inputs again. To exit the routine and save the ; changed minute value, press and release the switch input. The PIC ; will then send: ; ; SEC? y (where y is the current second delay) ; ; Again, the retrig switch decreases the delay when connected to +5v, ; the click switch increases the delay when connected to +5V. Pressing ; and releasing the switch input will save the changed value and ; then the PIC will send: ; ; DL? z (where z is the current dit length in ms) ; ; Connecting the retrig switch to +5V will decrease the dit length, ; connecting the click switch to +5V will increase the dit length. ; The maximum dit length is 80 ms (15 WPM). The minimum dit length ; is 30 ms (40 WPM). A press and release of the switch input will ; save the dit length in eeprom and then the PIC will send: ; ; MSG? ; ; If the switch input is pressed and released, the message record ; routine will be skipped. If either the retrig or click switches ; are set to +5V, the first character in the message will be played. ; The user can then changed this character (retrig to +5v goes up the ; character table, click to +5V goes down the character table). ; Proceed to the next character with a press and release of the ; switch input. Exit the message record routine with a 2 second ; press of the switch input. The PIC will then send: ; ; DONE ; ; Note that there are two special characters ; used in the recording process: a word space is denoted with a ; run together SP (....--.). Then end of the message is denoted with ; a run together END (.-.-..). ; ; The size of the message is limited to 60 characters - the recording ; routine should automatically bail out when the 60th character is ; entered. The PIC 16F84 has 64 bytes of eeprom - 1 is used for ; the minutes, another for the seconds, a third for the dit length ; and the last for the end of message byte. ; ; Note that all of the routines will "wraparound" ; either from high to low OR low to high when the user reaches a ; limit (ex, increase the delay from 99 minutes and the delay will ; wrap around to 0). ; ; The easier way to change any of these parameters is to simply edit ; the EEPROM data at the end of this file and then use the programmer ; to burn it into the 16F84 EEPROM. The message can be entered in ; ASCII (upper case letters only). Some of the special characters ; should be entered in numerical format. ; ; An easy way to reset the timer is to power off, then press and hold ; the switch input while powering on the timer. This will reset the ; timer to a 10 minute, 0 second delay at 15 WPM with a single period ; (.-.-.-) as the only message. ; ; See the file: 10MINREA.DME for more information on the program. ; ; ; Possible Enhancements: ; ; 1) use a 32.768 KHz crystal instead of the 4 MHz crystal to save power ; ; 2) get rid of the end of message byte usage at the full limit of the ; eeprom - this would allow another character in the message ; or 61 total characters. ; ; 3) some new or different way to enter the timing and message parameters ; which is less ponderous and more intuitive. ; ; ; PIC pinout ; (also, see the file: 10MINF84.GIF for the project schematic) ; ;PIC ;pin name function/connection ;--- ---- ----------------------------------------------- ; 1 ra2 wink LED output ; 2 ra3 auto timer retrigger, 1=retrigger 0=terminate ; 3 ra4 second click off, 1=click on, 0=click off ; 4 MCLR pulled up to +5V with a 10K resistor ; 5 VSS Ground ; 6 rb0 retrig input, 1=auto retrigger of delay, 0 = single delay ; 7 rb1 no connect ; 8 rb2 no connect ; 9 rb3 no connect ; 10 rb4 no connect ; 11 rb5 no connect ; 12 rb6 no connect ; 13 rb7 timer output to the relay driver transistor ; 14 VDD +5V ; 15 osc2 connected to 4 MHz xtal & 22? pf cap ; 16 osc1 connected to 4 MHz xtal & 22? pf cap ; 17 ra0 keyed output to the keying transistor ; 18 ra1 piezo speaker output for sending the speed in Morse & click LIST P=16F84, R=DEC INCLUDE "p16F84.inc" __FUSES _PWRTE_OFF & _CP_OFF & _WDT_OFF & _XT_OSC ; powerup timer off - not sure why. ; copy protect is off ; watchdog timer is off ; the oscillator uses an external 4 Mhx crystal or resonator ; memory locations temp equ H'0E' ;all purpose temporary byte minute equ H'0F' ;minutes counter byte second equ H'10' ;seconds counter byte char equ H'11' ;holder for character data - used in playchr, strtbit size equ H'12' ;char size counter - used in playchr, strtbit third equ H'13' ;holder for number of dit length spaces => ditsp temp2 equ H'14' ;holder for element length => outdit, outdah, ditsp lowcb equ H'15' ;low order counter byte highcb equ H'16' ;high order counter byte temp3 equ H'17' ;counter byte for 1 second delay flags equ H'18' ;flag bits such as command temp4 equ H'19' ;counter byte for 245 us delay swdown equ H'1A' ;down counter for 2 second menu delay tens equ H'1B' ;tens holder for bin2dec ones equ H'1C' ;ones holder for bin2dec eeaddr equ H'1D' ;internal eeprom address holder ditlen equ H'1E' ;dit length in 1 ms increments - chareep equ H'1F' ;character to be read/written from/to eeprom ; constants - also see individual routines ; bit definitions in equ 2 ;bit 2 of port a is the input pin carry equ 0 ;bit 0 of STATUS is the carry bit select equ 3 ;select is pin 12 of the hc595 - bit 3 of PORTA strobe equ 4 ;strobe is pin 11 of the hc595 - bit 4 of PORTA datao equ 0 ;datao is pin 14 of the hc595 - bit 0 of PORTA zero equ 2 ;bit 2 of STATUS is the zero bit switch equ 0 ;bit 0 of PORTB is the switch input pin keyout equ 0 ;bit 0 of PORTA is the keyed output pin piezo equ 1 ;bit 1 of PORTA is the piezo tone output pin led equ 2 ;bit 2 of PORTA is the LED wink retrig equ 3 ;bit 3 of PORTA is the retrigger, 1=on click equ 4 ;bit 4 of PORTA is the click input, 1=on command equ 0 ;bit 0 of flags is the command bit, 1=no output kill equ 1 ;bit 1 of flags is the kill bit, 1=kill message play timeout equ 7 ;bit 7 of PORTB is the timer output to relay transistor ORG 0x00 start goto init ;startup routine ORG 0x04 rupt goto init ;interrupt routine ; 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 memory ; numbers addwf PCL,1 ;compute the jump value ; character hex table position retlw b'00111111' ;0 ascii 30 hex 00 retlw b'00101111' ;1 31 01 retlw b'00100111' ;2 32 02 retlw b'00100011' ;3 33 03 retlw b'00100001' ;4 34 04 retlw b'00100000' ;5 35 05 retlw b'00110000' ;6 36 06 retlw b'00111000' ;7 37 07 retlw b'00111100' ;8 38 08 retlw b'00111110' ;9 39 09 ;following filler statements make ascii code conversion easier - just ; subtract a hex 30 from an ascii character to get the equivalent ; Morse code table position - for upper case letters only. retlw b'01010101' ; . 3A 0A retlw b'01110011' ; , 3B 0B ;note, the next char is a run together END - used for end of msg retlw b'01010100' ; END 3C 0C retlw b'00110001' ; BT 3D 0D retlw b'00110010' ; / 3E 0E retlw b'01001100' ; ? 3F 0F qmark is an ascii char ;note, the next char is a run together SP - used to edit in a space retlw b'10000110' ; SP 40 10 lets retlw b'00000101' ;A 41 hex 11 retlw b'00011000' ;B 42 12 retlw b'00011010' ;C 43 13 retlw b'00001100' ;D 44 14 retlw b'00000010' ;E 45 15 retlw b'00010010' ;F 46 16 retlw b'00001110' ;G 47 17 retlw b'00010000' ;H 48 18 retlw b'00000100' ;I 49 19 retlw b'00010111' ;J 4A 1A retlw b'00001101' ;K 4B 1B retlw b'00010100' ;L 4C 1C retlw b'00000111' ;M 4D 1D retlw b'00000110' ;N 4E 1E retlw b'00001111' ;O 4F 1F retlw b'00010110' ;P 50 20 retlw b'00011101' ;Q 51 21 retlw b'00001010' ;R 52 22 retlw b'00001000' ;S 53 23 retlw b'00000011' ;T 54 24 retlw b'00001001' ;U 55 25 retlw b'00010001' ;V 56 26 retlw b'00001011' ;W 57 27 retlw b'00011001' ;X 58 28 retlw b'00011011' ;Y 59 29 retlw b'00011100' ;Z 5A 2A ; outdit - a routine to send a dit outdit movf ditlen,0 ;load up the dit length movwf temp2 ;store it in the second temp location goto outst ;enter the dah routine ; outdah - a routine to send a dah outdah bcf STATUS,carry ;prepare for a shift rlf ditlen,0 ;multply the dit length by 2 addwf ditlen,0 ;add another dit length for a total of 3 movwf temp2 ;store it in the second temp location outst btfss flags,command ;is the command bit set? ;no, key the output transistor bsf PORTA,keyout ;turn on the output transistor ;yes, skip the output transistor keying loop4 decfsz temp2,1 ;decrement the dit length counter - is it 0 ? goto beep4 ;no, start beeping ;yes, end the dit goto ditsp ;with a dit space beep4 bsf PORTA,piezo ; set piezo high call halfcyc ;wait a half audio cycle bcf PORTA,piezo ; set piezo low call halfcyc ;wait a half audio cycle goto loop4 ;loop back until this dit length is complete ; ditsp - a routine to wait 1 dit space ; 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 ditsp bcf PORTA,keyout ;turn off the output transistor movlw 0x2 ;load up 1 dit space (add one due to loop contstruct) goto loadet ;branch in 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,1 ;decrement the dit space counter - is it 0 ? goto ditsp2 ;no, start waiting goto dun2 ;yes, end the dit ditsp2 ;movlw d'80' ;load a 15 wpm dit length movf ditlen,0 ;load up the dit length movwf temp2 ;store it in the second temp location loop6 decfsz temp2,1 ;decrement the dit length counter - is it 0 ? goto nobeep2 ;no, start waiting goto decet ;yes, check the dit space counter nobeep2 call halfcyc ;wait a half audio cycle call halfcyc ;wait a half audio cycle goto loop6 ;go back for another cycle? dun2 return ;end of a bunch of routines ; halfcyc - a routine to wait 1/2 of a sidetone cycle - in this case ; that is 500 usec or 1/2 ms => 1000 Hz tone ; at PIC clock = 4 MHz, 500 usec = 500 PIC instruction cycles ; delay = 2 (call) + 2 (return) + 5 (routine) + ((freq x 3) - 1) loop delay freq equ d'164' ; 9 + (164 x 3) - 1 = 500 halfcyc movlw freq ;1 1 load up the 1/2 cycle delay constant movwf temp ;1 2 store it in the temporary location btfss PORTB,switch ;2 4 check the switch, is it pressed? bsf flags,kill ; yes, set the kill bit nop ;1 5 waste a cycle loopy decfsz temp,1 ;1 decrement the temp location - is it 0 ? goto loopy ;2 no, keep decrementing ;yes, return to the calling routine return ;2 500 end of half cycle 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,1 ;decrement the element counter rotit rlf char,1 ;rotate the msb into carry btfss STATUS,0 ;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 ; playchr - a routine to play a character stored in w with Morse code playchr call numbers ;lookup the value movwf char ;save it in char getdig3 call strtbit ;rotate char left until the start bit comes up shout3 rlf char,1 ;rotate the msb into carry btfsc STATUS,0 ;test the carry - 0=dit, 1=dah goto dahsnd3 ;it's a one, send a dah ; send a dit call outdit ;send a dit goto sizdec ;go check the size variable dahsnd3 call outdah ;send the dah sizdec decfsz size,1 ;decrement the char size counter, is it 0? goto shout3 ;no, go back for the next bit ;yes, send a char space and then kill this thing call chsp ;send a character space rpcend return ;end of playchr routine ; eeinit - initialize the eeprom at power up if switch is pressed ; set the minute byte to 10 minutes delay ; set the second byte to 0 seconds delay ; set the dit length byte to 80 milliseconds (15 wpm) ; set the message to the single character: . (period) eeinit bcf EECON1,EEIF ;clear the write complete interrupt flag bit ;first, write the minute byte into eeprom clrf eeaddr ;clear the eeprom address movlw d'10' ;load up a 10 movwf EEDATA ;save it in the EEDATA byte call wreep ;write it into the eeprom ;call deboun ;wait a while ;second, write the second byte into eeprom. incf eeaddr,1 ;increment the eeprom address pointer clrf EEDATA ;clear the second byte call wreep ;write it into the eeprom ;call deboun ;wait a while ;third, write the dit length byte into eeprom. incf eeaddr,1 ;increment the eeprom address movlw d'80' ;load up a 80 ms dit length (15 wpm) movwf EEDATA ;save it in the EEDATA byte call wreep ;write it into the eeprom ;call deboun ;wait a while ;fourth, write the message into eeprom. incf eeaddr,1 ;increment the eeprom address movlw 0x3A ;load up a period movwf EEDATA ;save it in the EEDATA byte call wreep ;write it into the eeprom ;call deboun ;wait a while incf eeaddr,1 ;increment the eeprom address clrf EEDATA ;load and save an end of msg byte in EEDATA call wreep ;write it into the eeprom ;call deboun ;wait a while return ;end of the eeinit routine ; init - the initialization power up entry point of the program init bsf STATUS,5 ;set the bank bit to 1 movlw b'00000001' ; all outs on PORTB xcept bit 0 (switch) movwf TRISB ; set the tri-state register movlw b'00011000' ;PORTA all outs xcept bits 3,4 (retrig, click) movwf TRISA ; set the tri-state register movlw b'00000000' ; no option bits are relevant except PORTB p/u movwf OPTION_REG ; set the option register bcf STATUS,5 ;set the bank bit to 0 bcf INTCON,GIE ;disable interrupts bsf PORTA,led ;turn off the LED bcf PORTA,keyout ;turn off the transistor bcf PORTB,timeout ;turn off the relay transistor btfss PORTB,switch ;has the switch been pressed? call eeinit ;yes, initialize the eeprom call setdl ;get the dit length from eeprom ;play an FB through the piezo to signify a "good" powerup bsf flags,command ;turn off the output for the fb send movlw 0x16 ;load up an F call playchr ; send the F movlw 0x12 ;load up a B call playchr ; send the B bcf flags,command ;turn on the output mainlup bcf flags,kill ;kill the kill bit ;check for input activity and branch if something happens bsf INTCON,INTE ;enable port b 0 interrupt sleep ;power down until port b 0 changes bcf INTCON,INTE ;disable port b 0 interrupt bcf INTCON,INTF ;clear the port b 0 interrupt flag btfss PORTB,switch ;has the switch been pressed? goto cntdown ;yes, begin the 10 minute count down goto mainlup ;no activity, keep looping ; cntdown - a routine to count down the 10 minute interval - this includes ; winking an LED at the user for a short time each second. cntdown bsf PORTB,timeout ;turn on the relay transistor ;add a check for release of the switch ;also, keep track of the number of deboun calls, over 100 (2 seconds) ;means that you've entered the menu routines. movlw d'100' ;load up a 2 second delay movwf swdown ;save the 2 second count in swdown cntdwn1 call deboun ;wait 20 ms decfsz swdown,1 ;count down the 2 seconds for menu entry goto tstsw ;less than 2 seconds, test the switch goto menu ;enter the eeprom set menu tstsw btfss PORTB,switch ;has the switch been released? goto cntdwn1 ;no, loop until it is bcf flags,kill ;kill the kill bit before entering the loops ; Note that the first second delay is long by at least 14 usec, but the ; delay may be off by as much as 20 ms already due to the debounce ; delay above - the delay is needed to prevent a "bounce bailout" ; during the first pass. The rest of the seconds should be 1,000,000 ; cycles, so a long delay shouldn't be compromised either way if the ; crystal is accurate. ;yes, start the delay ;fetch the minute/second counts from eeprom cdstart clrf EEADR ;1 1 minute is located in loc 0 of int. eeprom bsf STATUS,5 ;1 2 set the bank bit to 1 for EECON1 set bsf EECON1,RD ;1 3 setup internal EEPROM for read mode bcf STATUS,5 ;1 4 reset the bank bit to 0 movf EEDATA,0 ;1 5 load up w with the EEPROM data byte movwf minute ;1 6 store the minutes incf EEADR ;1 7 second is located in loc 0 of int. eeprom bsf STATUS,5 ;1 8 set the bank bit to 1 for EECON1 set bsf EECON1,RD ;1 9 setup internal EEPROM for read mode bcf STATUS,5 ;1 10 reset the bank bit to 0 movf EEDATA,0 ;1 11 load up w with the EEPROM data byte movwf second ;1 12 store the second incf second,1 ;1 13 increment the second and incf minute,1 ;1 14 minute counters to prepare for decre ;start a new count for the 1 second loop lstart decfsz second,1 ;1 1 decrement the second counter goto even1 ;2 3 second still > 0, go and even out the del movlw d'60' ;1 3 load up a full 60 sec movwf second ;1 4 reload the full minute decfsz minute,1 ;1 5 decrement the minute counter goto even2 ;2 7 minute still > 0, go and even out the del goto playcal ; you are done, go & send the callsign even1 nop ;1 4 add 4 cycles to even out the delay nop ;1 5 goto even2 ;2 7 even2 ;wait for about 1 second, include a check of the switch, and a wink ; of the LED movlw d'249' ;1 8 load up a loop count movwf temp3 ;1 9 save it ;wink the LED and/or the piezo bcf PORTA,led ;1 10 turn on the led btfsc PORTA,click ;2 12 is the click on or off? bsf PORTA,piezo ;it's on, turn on the piezo call halfcyc ;500 x 3 = 1500 call halfcyc call halfcyc bsf PORTA,led ;1 1512 turn off the LED bcf PORTA,piezo ;1 1513 turn off the piezo, even if it's already off cdown call wait4ms ;4004 x 249 = 996996 wait about 4 ms btfss PORTB,switch ;2 x 249 = 498 has the switch been pressed? goto bailo ; yes, bail out of the loop decfsz temp3,1 ;1 (2) 3 x 248 + 2 = 746 decrement the loop count - skip at 0 goto cdown ; loop back ;lets wait another 245 usec to even the delay movlw d'80' ;1 load up a loop delay counter movf temp4 ;1 save it in temp4 nop evendel decfsz temp4,1 ;1 (2) (3 x 81) - 1 + 3 = 245 decrement the loop count - skip at 0 goto evendel ;2 loop back until done goto lstart ;2 1000000 loop back for another 1 second bailo ;you've bailed from the loop - now debounce the switch and release call deboun ;wait 20 ms btfss PORTB,switch ;is the switch still pressed? goto bailo ;yes, keep looping bcf PORTB,timeout ;turn off the relay transistor keyup call deboun ;wait another 20 ms btfsc PORTB,switch ;is the switch still pressed? goto mainlup ;no, go back and sleep goto keyup ;yes, wait for a release ; wait4ms - a routine to call half cyc 8 times and thus wait 4004 cycles ; (500 x 8) + 2 call, 2 return = 4004 wait4ms call halfcyc ;wait 500 usec wait3p5 call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec call halfcyc ;wait 500 usec return ;end of wait4ms ; deboun - wait for 20 ms deboun movlw d'40' ;load up a counter movwf temp2 ;store it in temp2 dbounc call halfcyc ;wait 500 usec decfsz temp2,1 ;decrement the loop count - skip at 0 goto dbounc ;count not zero, loop back return ;end of debounce routine ; playcal - a routine to play the EEPROM message (call) contents playcal bcf PORTB,timeout ;turn off the relay transistor movlw d'3' ;start of the callsign stored in eeprom is 3 movwf EEADR ;store the start address of the callsign ldele2 bsf STATUS,5 ;set the bank bit to 1 for EECON1 set bsf EECON1,RD ;setup internal EEPROM for read mode bcf STATUS,5 ;reset the bank bit to 0 movf EEDATA,0 ;load up the eeprom data byte btfsc STATUS,zero ;is this the end of the message (0)?? goto endply2 ;yes, end the play ;no, test for a word space sublw 0x20 ;a 20 hex is an ascii space btfsc STATUS,zero ;is this an ascii space?? goto wspace ;yes, go and send a wdsp2, then loop back ;no, keep sending mark2 movlw 0x30 ;get ready to convert the ascii code to table pointer subwf EEDATA,0 ;by subtracting 30 hex from the eedata call playchr ;play the character btfsc flags,kill ;check the kill bit goto switup ;the kill bit is set, go check the switch branchi incf EEADR,1 ;increment the eeprom address goto ldele2 ;get the next element endply2 btfsc PORTA,retrig ;should we retrigger the delay? goto cntdown ;yes, go back to cntdown goto mainlup ;no, head back to the main loop for sleep wspace call wdsp2 ;send a word space goto branchi ;branch back into the routine switup call deboun ;wait another 20 ms btfsc PORTB,switch ;is the switch still pressed? goto mainlup ;no, go back and sleep goto switup ;yes, keep looping until released ; menu - a routine to provide an audible menu for setting the eeprom contents menu bsf flags,command ;set the command flag so output transis off ;send the minutes menu item: MIN? movlw 0x1D ;load an M call playchr ;play it movlw 0x19 ;load an I call playchr ;play it movlw 0x1E ;load an N call playchr ;play it movlw 0x0F ;load an ? call playchr ;play it call wdsp2 ;stick in a word space clrf eeaddr ;clear the eeprom address shadow call skipinc ;branch into the eepread routine to get the ;eeprom data without incrementing the address movwf minute ;store the minutes btfss PORTB,switch ;is the switch still held? goto menu ;yes, repeat the MIN? item playmin movf minute,0 ;load up the minute byte call bin2dec ;convert and play it recheck call deboun ;wait 20 ms btfsc PORTA,click ;has click set to +5? goto minup ;yes, go and increase the minutes byte btfsc PORTA,retrig ;has retrig set to +5? goto mindown ;yes, go and decrease the minutes byte btfss PORTB,switch ;has the switch been pressed? goto menu2 ;yes, proceed to the next item goto recheck ;no, recheck the 3 switch inputs minup incf minute,1 ;increment the minute byte movlw d'100' ;load up a full scale minutes value + 1 subwf minute,0 ;subtract it from the changed minutes value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap1 ;yes, the changed minute interval is greater than 99 ;no, the changed minute interval is not 100 goto playmin ;proceed with the routine wrap1 clrf minute ;wrap the minute byte around to 0 goto playmin ;proceed with the routine mindown decf minute,1 ;decrement the second byte movlw d'255' ;load up a full scale minutes value subwf minute,0 ;subtract it from the changed minutes value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap2 ;yes, the changed minute interval is 255 (wraparound) ;no, the changed minute interval is 0 or greater goto playmin ;proceed with the routine wrap2 movlw d'99' ;wrap around to the full scale minute value movwf minute ;save it in minute goto playmin ;proceed with the routine menu2 ;first, write the minute byte back into eeprom movf minute,0 ;load up the changed minute value movwf EEDATA ;save it in the EEDATA byte call wreep ;write the changed value into the eeprom ;send the seconds menu item: SEC? movlw 0x23 ;load an S call playchr ;play it movlw 0x15 ;load an E call playchr ;play it movlw 0x13 ;load a C call playchr ;play it movlw 0x0F ;load an ? call playchr ;play it call wdsp2 ;stick in a word space call eepread ;get the second byte from eeprom movwf second ;store the second btfss PORTB,switch ;is the switch still held? goto menu2 ;yes, repeat the MIN? item playsec movf second,0 ;load up the second byte call bin2dec ;convert and play it rechk2 call deboun ;wait 20 ms btfsc PORTA,click ;has click set to +5? goto secup ;yes, go and increase the second byte btfsc PORTA,retrig ;has retrig set to +5? goto secdown ;yes, go and decrease the second byte btfss PORTB,switch ;has the switch been pressed? goto menu3 ;yes, proceed to the next item goto rechk2 ;no, recheck the 3 switch inputs secup incf second,1 ;increment the second byte movlw d'60' ;load up a full scale second value + 1 subwf second,0 ;subtract it from the changed second value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap3 ;yes, the changed second interval is greater than 59 ;no, the changed second interval is not 60 goto playsec ;proceed with the routine wrap3 clrf second ;wrap the second byte around to 0 goto playsec ;proceed with the routine secdown decf second,1 ;decrement the second byte movlw d'255' ;load up a full scale minutes value subwf second,0 ;subtract it from the changed second value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap4 ;yes, the changed second interval is 255 (wraparound) ;no, the changed second interval is 0 or greater goto playsec ;proceed with the routine wrap4 movlw d'59' ;wrap around to the full scale second value movwf second ;save it in second goto playsec ;proceed with the routine menu3 movf second,0 ;load up the changed second value movwf EEDATA ;save it in the EEDATA byte call wreep ;write the changed value into the eeprom ;send the speed menu item: SPD? movlw 0x14 ;load a D call playchr ;play it movlw 0x1C ;load an L call playchr ;play it movlw 0x0F ;load an ? call playchr ;play it call wdsp2 ;stick in a word space call eepread ;increment the address and read the eeprom movwf ditlen ;store the dit length btfss PORTB,switch ;is the switch still held? goto menu3 ;yes, repeat the DL? item playdl movf ditlen,0 ;load up the dit length byte call bin2dec ;convert and play it rechk3 call deboun ;wait 20 ms btfsc PORTA,click ;has click set to +5? goto dlup ;yes, go and increase the dit length byte btfsc PORTA,retrig ;has retrig set to +5? goto dldown ;yes, go and decrease the dit length byte btfss PORTB,switch ;has the switch been pressed? goto menu4 ;yes, proceed to the next item goto rechk3 ;no, recheck the 3 switch inputs dlup incf ditlen,1 ;increment the ditlen byte movlw d'81' ;load up a full scale dit length value + 1 subwf ditlen,0 ;subtract it from the changed dit length value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap5 ;yes, the changed dit length int is greater than 80 ;no, the changed second interval is not 80 goto playdl ;proceed with the routine wrap5 movlw d'30' ;load up a 40 wpm dit length movwf ditlen ;wrap the dit length byte around to 40 wpm goto playdl ;proceed with the routine dldown decf ditlen,1 ;decrement the dit length byte movlw d'255' ;load up a full scale 8 bit value subwf ditlen,0 ;subtract it from the changed dit length value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap6 ;yes, the changed dit length int is 255 (wraparound) ;no, the changed dit length interval is 0 or greater goto playdl ;proceed with the routine wrap6 movlw d'80' ;wrap around to the full scale dit length value movwf ditlen ;save it in ditlen goto playdl ;proceed with the routine menu4 ;first write the previous byte back into eeprom movf ditlen,0 ;load up the changed dit length value movwf EEDATA ;save it in the EEDATA byte call wreep ;write the changed value into the eeprom ;send the msg menu item: MSG? movlw 0x1D ;load an M call playchr ;play it movlw 0x23 ;load an S call playchr ;play it movlw 0x17 ;load a G call playchr ;play it movlw 0x0F ;load an ? call playchr ;play it call wdsp2 ;stick in a word space btfss PORTB,switch ;is the switch still held? goto menu4 ;yes, repeat the DL? item ;include a way to bail out of a msg record without changing anything. bail? call deboun ;wait a while btfsc PORTA,click ;has click set to +5? goto release ;yes, start the routine btfsc PORTA,retrig ;has retrig set to +5? goto release ;yes, start the routine btfss PORTB,switch ;has the switch been pressed? goto dun ;yes, bailout of the routine goto bail? ;keep looping until something happens release call deboun ;wait a while btfsc PORTA,click ;is click at 0? goto release ;yes, keep looping until click is reset to 0 btfsc PORTA,retrig ;is retrig at 0? goto release ;yes, keep looping until retrig is reset to 0 nextchr call eepread ;increment the address and read the eeprom movwf chareep ;store the character plychar call wdsp2 ;stick in a word space to seperate the chars movf chareep,0 ;load up the character btfss STATUS,zero ;is the character a zero? (end of msg) goto tstspac ;no, test for a space ;send a special string for the end of message byte movlw 0x3C ;load up the special END char (.-.-..) movwf chareep ;save the END char into the routine holder goto ascnvrt ;go and convert, then play the char tstspac sublw 0x20 ;a 20 hex is an ascii space btfss STATUS,zero ;is this an ascii space?? goto lastchk ;no, it's not a space ;yes, so change chareep to 40 hex movlw 0x40 ;load up the sp table address movwf chareep ;save the changed character lastchk ;see if the char is outside the normal 30 to 5A char range movlw 0x30 ;check the lower limit subwf chareep,0 ; against the char btfss STATUS,carry ;check the carry bit, if a 0 then chareep ;is less than 30 goto loadane ;it's less than 30, go load an e movlw 0x5B ;check the upper limit + 1 subwf chareep,0 ; against the char btfss STATUS,carry ; check the carry, if a 1 then chareep ; is greater than 5A goto ascnvrt ;it's within the specified range, convert it loadane ;it's more than 5A, go load an e movlw 0x45 ;load up an E movwf chareep ;save it in chareep ascnvrt movlw 0x30 ;load up for ascii conversion subwf chareep,0 ;convert the character ovrasc call playchr ;play it rechk4 call deboun ;wait 20 ms btfsc PORTA,click ;has click set to +5? goto chrup ;yes, go and increase the char byte btfsc PORTA,retrig ;has retrig set to +5? goto chrdown ;yes, go and decrease the char byte btfss PORTB,switch ;has the switch been pressed? goto menu5 ;yes, proceed to the next item goto rechk4 ;no, recheck the 3 switch inputs chrup incf chareep,1 ;increment the chareep byte movlw 0x5B ;load up a full scale char value + 1 subwf chareep,0 ;subtract it from the changed char value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap7 ;yes, the changed char int is greater than 2A ;no, the changed char interval is not 2A goto plychar ;proceed with the routine wrap7 movlw 0x30 ;load up an ascii 0 movwf chareep ;wrap the char byte around to a 0 goto plychar ;proceed with the routine chrdown decf chareep,1 ;decrement the char byte movlw d'255' ;load up a full scale 8 bit value subwf chareep,0 ;subtract it from the changed char value btfsc STATUS,2 ; are the values equal? (Z=1) goto wrap8 ;yes, the changed char int is 255 (wraparound) ;no, the changed char interval is 0 or greater goto plychar ;proceed with the routine wrap8 movlw 0x5A ;wrap around to the full scale char value movwf chareep ;save it in chareep goto plychar ;proceed with the routine menu5 ;first, check to see if it's the special space character, SP movf chareep,0 ;load up the character sublw 0x40 ;a 40 hex is the special character btfss STATUS,zero ;is this the special space character?? goto tstend ;no, skip the convertion bcf STATUS,carry ;yes,clear the carry, we need to shift rrf chareep,1 ;yes, convert it back to 20 hex with a shift goto overx ;skip the END test tstend ;test for the end character movf chareep,0 ;load up the character sublw 0x3C ;a 3C hex is the special END character btfss STATUS,zero ;is this the special END character?? goto overx ;no, skip the convertion clrf chareep ;yes, convert it back to 0 hex with a clear ;write the present char to eeprom overx movlw d'63' ;load up for a top address check subwf eeaddr,0 ;compare the current address with 62 btfsc STATUS,zero ;is this the last char? goto menu6a ;yes, record an end of msg & bail out ;no, just record the byte in eeprom overy movf chareep,0 ;load up the changed char value movwf EEDATA ;save it in the EEDATA byte call wreep ;write the changed value into the eeprom ;see if the switch is held more or less than 2 seconds movlw d'100' ;load up a 2 second delay movwf swdown ;save the 2 second count in swdown cntdwn2 call deboun ;wait 20 ms decfsz swdown,1 ;count down the 2 seconds for menu entry goto tstsw1 ;less than 2 seconds, test the switch goto menu6 ;more than 2 seconds, bail out tstsw1 btfss PORTB,switch ;has the switch been released? goto cntdwn2 ;no, loop until it is goto nextchr ;yes, re-enter the msg routine menu6 ;write the end of message byte (0) incf eeaddr,1 ;increment the eeprom address pointer menu6a clrf EEDATA ;save it in the EEDATA byte call wreep ;write the changed value into the eeprom ;tell the user that 2 seconds are over by sending DONE dun movlw 0x14 ;load a D call playchr ;play it movlw 0x1F ;load an O call playchr ;play it movlw 0x1E ;load an N call playchr ;play it movlw 0x15 ;load an E call playchr ;play it btfss PORTB,switch ;is the switch still held? goto dun ;yes, repeat the DONE item call setdl ;set the dit length with the new eeprom value bcf flags,command ;reset command flag so output trans enabled goto mainlup ;bail back to the main loop ; eepread - a routine to increment the eeprom address pointer and then ; read the eeprom data at that new address into w eepread incf eeaddr,1 ;increment the eeprom address pointer, eeaddr skipinc movf eeaddr,0 ;load eeaddr into w movwf EEADR ;save it in EEADR bsf STATUS,5 ;set the bank bit to 1 for EECON1 set bsf EECON1,RD ;setup internal EEPROM for read mode bcf STATUS,5 ;reset the bank bit to 0 movf EEDATA,0 ;load up w with the EEPROM data byte return ;end of the eepread routine ; bin2dec - a routine to convert a single binary byte into a 2 decimal digit ; equivalent. The routine subtracts 10 from the binary byte until ; a negative result occurs, keeping a count of the number of times ; that 10 is subtracted - then the last 10 is added back - the ; counter for the 10s subtraction contains the tens decimal digit ; equivalent of the binary number, the holder for the binary number ; contains the ones decimal digit equivalent. ; memory used (destroyed): tens, ones ; bin2dec is entered with the binary number in W. bin2dec clrf tens ;clear the tens holder movwf ones ;store the binary byte into ones incre incf tens,1 ;bump the tens holder movlw 0x0A ;load up a 10 into w subwf ones,1 ;try subtracting 10 from the binary value btfsc STATUS,0 ;keep going until result is negative (carry=0) goto incre ;it's positive, keep looping decf tens,1 ;too far, give back the last increment movlw 0x0A ;load up a 10 into w addwf ones,1 ;" " add back the last subtraction ;so now the 10's digit is in tens ; and the 1's digit is in ones ;so play the digits, doing a leading zero check movf tens,0 ;load up tens btfss STATUS,zero ;is the leading digit a zero? call playchr ;no, play the tens digit ;yes, skip the tens digit play movf ones,0 ;load up ones call playchr ;play the ones digit even if it's a zero ;) call wdsp2 ;stick in a word space return ;end of bin2dec rouitne ; wreep - a routine to write the data in EEDATA into the address EEADR ; write sequence from the 16F84 spec - for the internal 16f84 eeprom. ; Why use the eeaddr memory location to hold the internal eeprom address ; rather than using EEADR? ; Because EEADR appears to be reset to 0 after each write to the internal ; eeprom, so it can't be used as an address pointer for the record2 ; routine - this doesn't happen with the read from the internal eeprom. wreep movf eeaddr,0 ;load up the internal eeprom address movwf EEADR ;store is in the EEPROM address buffer bsf STATUS,5 ;set the bank bit to 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 checkit btfss EECON1,EEIF ;see if the last write is complete goto checkit ;no, check it again bcf EECON1,EEIF ;clear the write complete interrupt flag bit bcf STATUS,5 ;reset the bank bit to 0 return ;end of wreep routine ; setdl - set the dit length - read eeprom data, put into ditlen setdl movlw d'2' ;load up the address of the dit len in eeprom movwf EEADR ;minute is located in loc 0 of int. eeprom bsf STATUS,5 ;set the bank bit to 1 for EECON1 set bsf EECON1,RD ;setup internal EEPROM for read mode bcf STATUS,5 ;reset the bank bit to 0 movf EEDATA,0 ;load up w with the EEPROM data byte movwf ditlen ;store the minutes return ;end of setdl routine ;setup the 16F84 eeprom for the minutes, seconds and callsign / message org 0x2100 ;starting address for 16F84 internal eeprom ;de 2 ;2 minute delay de 0 ;0 minute delay de 2 ; plus a 2 second delay de d'60' ; 60 ms dit length is a 20 wpm rate ;note, use caps for the callsign/message - must be 60 chars (or less) long ;de "DE WB9KZY" ;callsign / message de "ABCDEFGHIJKLMNOPQRSTUVWXYZ" de "ABCDEFGHIJKLMNOPQRSTUVWXYZ" de "34567890" de 0 ;end of callsign / message byte END ;end of the 10minf84.asm program