;HFBCN02.ASM G4JNT 2003-05-19 ;Controls keyer, PA and power steps for RAL 5MHz beacon project ;Based on GPS locked timer, uses PPS input, and 4MHz xtal (1us clock period) ;NMEA data from GPS in $GPRMC message used for beacon sequence timing ;Based on 5MHZBCN02.ASM ;All sequence data and EEPROM contents stored in separate include file ;Checks A flag bit in NMEA data for validity ;Ver 2 adds in NMEA polarity option. Changed stack operations. ; Customised read RMC time for Oncore format of hhmmss.ss ; Adds test mode for full RF output (PA Off) when no GPS signal LIST P=16F84 INCLUDE "P16F84.INC" __config 0x3FF1 errorlevel 2 INCLUDE "HFBCN.INC" ;Sequence definition constants and EEPROM data cblock 0x0C Wreg ;Save W and STATUS during interrupt SaveSts Temp Flags DelCount DelCount2 Counter AttenCount ;Count through attenuator settings Seconds ;Counts 1 PPS interrupts to define sequence MinCount ;Counts minutes modulo repeat time Hours Minutes CWChar MsgCount StackA Stack9 ;Earliest character Stack8 ;Stack received NMEA characters Stack7 ;6 characters needed plus comma Stack6 ;eg for $GPRMC , UTC , Data Stack5 Stack4 Stack3 Stack2 Stack1 Stack0 ;Latest character CommaCount BCDHi BCDMe BCDLo AHi ALo endc #define KEY PORTA, 0 #define PA PORTA, 1 #define LED PORTA, 2 #define TXD PORTA, 3 ;RS232 out for setting GPS module. #define Disarm PORTA, 4 ;Low to disable PA and Reset timer sequence ;GPS 1PPS Interrupt input to PORTB, 0 #define PPS PORTB, 0 #define NMEA PORTB, 1 #define TestPort PORTB, 7 ;PORTB 2-7 are attenuator outputs #define GieFlag Flags, 0 #define IntFlag Flags, 1;Set while interrupt is being serviced ; inhibits repeat action #define DoSequence Flags, 2;Allows Tx sequence 1st time mins roll over #define FirstInt Flags, 3;Set at 1st interrupt, for controlled startup #define GPSValid Flags, 4;Set when RMC message indicates data is valid org 0 goto Startup ;jump to main code ;------------------------------ org 4 ;Called every second by GPS, during sequence only movwf Wreg ;Save W swapf STATUS , W ; nb. movwf does not affect any STATUS bits movwf SaveSts ;Stave STATUS, without affecting it bcf INTCON , INTF ;Clear interrupt flag incf Seconds movlw d'60' subwf Seconds , W ;W = Seconds - 60 btfsc STATUS , Z ;if not zero, no further action required clrf Seconds swapf SaveSts , W ;Restore STATUS movwf STATUS swapf Wreg ;Restore W swapf Wreg , W ;Can't use movf as it affects Z bit retfie ;======================== Startup bsf STATUS,RP0 ;ram page 1 movlw b'00010000' ;A0/1 Tx control, A2 LED, A3 TXD, A4 disarm input movwf PORTA ; movlw b'00000011' ;B0-1 inputs for Int and NMEA, B2-7 atten O/P movwf PORTB movlw b'01000000' ;Pull ups enabled, rising edge int, movwf OPTION_REG bcf STATUS,RP0 ;ram page 0 movlw b'00010000' ;INTF Interrupt. movwf INTCON ;Enabled immediately clrf MinCount clrf Minutes clrf Hours clrf Flags bcf PA bcf KEY bsf LED movlw 0 call GetEE call SetAtten WaitForPPS ;Can't do anything until PPS appears btfss PPS ; when GPS has locked up goto WaitForPPS DontStartYet call GetRMC ;Prevent jumping into sequence immediately call TestMinutes ; movf MinCount ; btfsc STATUS, Z goto DontStartYet bcf LED ;............... MainLoop btfss Disarm goto Armed bcf PA bsf KEY bsf LED movlw 0 ;Disarm mode gives constant CW output call GetEE ; with Tx disabled call SetAtten goto MainLoop Armed bcf KEY call GetRMC call TestMinutes btfss GPSValid ;Set on 'A' in $GPRMC field 3, reset on 'V' bsf LED ;Illuminate LED as error message movlw RptTime-1 ;Test for the minute BEFORE the sequence start subwf MinCount, W ; MinCount counts 0 - [RptTime-1] from btfss STATUS, Z goto MainLoop movlw d'59' ;Test for second just before sequence start subwf Seconds, W btfss STATUS, Z goto MainLoop WaitRefPPS ;Seconds has not yet incremented for this interval btfss PPS ; as there was no RMC read. This increment will goto WaitRefPPS ; happen immediately the interrupt is enabled .............. bsf INTCON, GIE ;Start PPS interrupt to keep Seconds count going. nop ;The immediate pending interrupt compensates ; for the lost count above bcf KEY movlw 0 call GetEE ;Attenuator code for full power call SetAtten bsf PA ;Transmitter on call CWDelay ;A few 10's of ms to allow PA to switch on call CWDelay call SendCallsign call CWDelay bsf KEY ;Full carrier after callsign clrf AttenCount WaitForSteps ;Wait in this loop at full carrier for Seconds movlw PowerSteps ; counter to reach start of power step time subwf Seconds, W btfss STATUS, Z goto WaitForSteps ;......... StepSeqLoop movf AttenCount, W ;Counter steps through atten code addresses call GetEE ;Step values start at address 0 call SetAtten ;Set power level for this interval bcf KEY ;Each step, break for StepDur ms bcf LED movlw StepDur movwf DelCount2 StepDelLoop call Delay1ms decfsz DelCount2 goto StepDelLoop bsf KEY bsf LED incf AttenCount ;Increment ready for next pass movlw Nlevels ;Get number of power steps subwf AttenCount, W ;W = AttenCount - Nlevels btfsc STATUS, Z clrf AttenCount ;Count modulo Nlevels sleep ;Make sure we pass through this loop once nop ; per PPS interrupt only movlw EndSteps ;Test for end of power step sequence subwf Seconds, W btfss STATUS, Z goto StepSeqLoop ;......... movlw 0 call GetEE call SetAtten bsf KEY ;Back to full carrier WaitForSounder ;Wait in this loop at full carrier for Seconds movlw SounderCnt ; counter to reach start of sounder time subwf Seconds, W btfss STATUS, Z goto WaitForSounder bcf INTCON, GIE ;Kill interrupt, lose Seconds count call SendSounder ;......... bcf KEY ;Timekeeping lost, could possibly even bcf LED ; be beyond minute marker bcf PA ;Use ReadRMC to restore ;Exit with key up goto MainLoop ;========================= TestMinutes ;Returns Minutes - SeqOffset) MOD 60, ie a count of movlw SeqOffset ; 0 - RptTime-1, starting at sequence ref point. subwf Minutes, W ;W = Minutes - SeqOffset btfss STATUS, C ;If result -ve (C=0), add 60 to normalise addlw d'60' movwf MinCount ;MinCount = (Minutes - SeqOffset) MOD 60 MinModLoop ;Repeatedly subtract RptTime until result is -ve movlw RptTime ; subwf MinCount, W ;W = MinCount - RptCount, if -ve(C=0), MOD is done btfss STATUS, C goto ModDone movwf MinCount ; otherwise save back and continue goto MinModLoop ModDone return ;---------------------------- SetAtten ;Enter with attenuator code in W movwf Temp rlf Temp rlf Temp, W ;Align to correct position movwf PORTB ;Can ignore B0/1 as they are inputs return ;---------------------------- SendSounder ; ;Timing check---------------------- count is that AFTER the operation ; V - Individual ; V - Inner loop total ; V - Inner loop during outer loop movlw SounderDur movwf Counter ; SndrLoop1 movlw d'40' ; 1 24998 Number of pulses per second movwf DelCount ; 1 24999 SndrLoop2 ; bsf KEY ; 1* 0 On period 500us bsf LED ; 1 call SndrDel ; 495 496 nop ; 1 nop ; 1 nop ; 1 bcf KEY ; 1* 500 bcf LED ; 1 501 ;,,,,,,,,,,,,,,,,,,,, movlw d'49' ; 1 Ý This loop 49*(495+4)+1= 24452 movwf AttenCount ; 1 Ý SndOffLoop ; Ý call SndrDel ; 495 Ý nop ; 1 Ý decfsz AttenCount ; 1 Ý goto SndOffLoop ; 2 -- ; 24953 (at completion of loop) movlw d'12' ; 1 Ý This loop 12*3+1= 37 movwf AttenCount ; 1 Ý SndOffLoop2 ; Ý decfsz AttenCount ; 1 Ý goto SndOffLoop2 ; 2 -- ; 24990 (at completion of loop) decfsz DelCount ; 1/2 goto ExtraDelay ; 2/- ;,,,,,,,,,,,,,,,,, nop ; 1 /24993 incf Seconds ; 1 Add on the second due to decfsz Counter ; 1 missed PPS interrupt goto SndrLoop1 ; 2 /24997 during outer operation (1 sec) decf Seconds ; Take off the count that will now be btfsc GieFlag ; generated by the waiting PPS interrupt. bsf INTCON , GIE ;Re-enable Timer interrupt if appropriate return ;Exit with key up at this stage ;..... ExtraDelay ; Adds in 6 clocks when inner loop nop ; 1 operates normally, making total of 25000 nop ; 1 nop ; 1 nop ; 1 goto SndrLoop2 ; 2 24999 during inner operation ;---------------------------- SndrDel ;Total delay including call and return = 5N + 5 = 495 movlw d'98' movwf DelCount2 SndrDelLoop nop nop decfsz DelCount2 goto SndrDelLoop return ;---------------------------- ;---------------------------- SendCallsign movlw 0x10 ;Start of Callsign in EEPROM movwf MsgCount EELoop movf MsgCount, W call GetEE iorlw 0 ;Test for null character (EOL) btfsc STATUS , Z goto GetOutCS call SendCW incf MsgCount goto EELoop GetOutCS return ;------------------------------ SendCW ;Sends a single CW character stored in W bcf GieFlag btfsc INTCON , GIE ;Inhibit int to avoid corrupting bsf GieFlag ; character, but save status for bcf INTCON , GIE ; return addlw 0xE0 ;Subtract 32 (add 256 - 32) to point to table call CWTable movwf CWChar ;Doesn't affect STATUS so... movf CWChar ; check for invalid returned code here btfsc STATUS , Z goto NoChar movlw 0x07 andwf CWChar , W ;Extract element count into W movwf Counter movlw 6 subwf Counter , W ;W = Elcount - 6. If +ve/0 then C=1, > 5 els btfss STATUS , C goto Send5 rrf Counter ;Puts LSB into C as 110 = . 111 = - call SendEle ;First . or - for 6 element characters movlw 5 movwf Counter ;Force count to 5 for rest of the elements Send5 rlf CWChar ;Element to be sent into C. call SendEle ; count already in Counter decfsz Counter goto Send5 NoChar ;Inter word gap, and invalid codes. bcf KEY call CWDelay call CWDelay btfsc GieFlag bsf INTCON , GIE ;Re-enable Timer interrupt if appropriate return ;------------------- SendEle ;Data in STATUS , C; 0 = dit 1 = dah bsf KEY bsf LED btfss STATUS , C ;test for dot or dash goto SendDot call CWDelay call CWDelay SendDot call CWDelay bcf KEY bcf LED call CWDelay ;Inter element gap return ;--------------------------- CWDelay movlw DotLength movwf DelCount2 CWDelLoop call Delay1ms decfsz DelCount2 goto CWDelLoop return ;--------------------------- Delay1ms ;1ms delay including call and return movlw d'199' ;Delay = 5.N + 5 = 1000us movwf DelCount BasicDLoop nop nop decfsz DelCount Goto BasicDLoop return ;---------------------------- GetEE movwf EEADR bsf STATUS,RP0 ;ram page 1 bsf EECON1 , RD bcf STATUS , RP0 ;ram page 0 movf EEDATA , W return ;---------------------------- GetRMC ;Monitors NMEA line for $GPRMC, then reads UTC and Date info call PushStack call GetByte movwf Stack0 movf Stack5 , W ;Check for $GPRMC just arrived sublw "$" ; Stack 543210 btfss STATUS , Z goto GetRMC movf Stack4 , W sublw "G" btfss STATUS , Z goto GetRMC movf Stack3 , W sublw "P" btfss STATUS , Z goto GetRMC movf Stack2 , W sublw "R" btfss STATUS , Z goto GetRMC movf Stack1 , W sublw "M" btfss STATUS , Z goto GetRMC movf Stack0 , W sublw "C" btfss STATUS , Z goto GetRMC ; ;Having reached here means RMC Message just starting clrf CommaCount ;and no commas to date bsf LED DecodeRMC ;Rest of RMC message passes through here call PushStack call GetByte ;Stack hold last 7 items of data, including comma movwf Stack0 ; in Stack0 sublw "," ;For general NMEA btfsc STATUS , Z ; goto DeLimit movf Stack0 , W sublw "*" ;Last item terminator btfsc STATUS , Z ; goto OkRMC ;If * teminator, then new RMC again or GO. goto DecodeRMC ;Continue decoding RMC message ;............................. DeLimit ;comma detected, act on info if appropriate incf CommaCount ;Needed to find position in string (MS stack item) movf CommaCount , W sublw 2 ;UTC Sent as first item, detect comma afterwards btfsc STATUS , Z call DoTime movf CommaCount , W sublw 3 ;UTC Sent as first item, detect comma afterwards btfsc STATUS , Z call DoStatus goto DecodeRMC OkRMC return ;After complete RMC message decoded ;-------------------------------------- GetByte if NMEAPol == 1 btfss NMEA ;Data inverted for RS232 polarity else btfsc NMEA endif goto GetByte ;Loop waiting for start bit call HalfBitDelay ;Centre of start bit if NMEAPol == 1 btfss NMEA ;Data inverted for RS232 polarity else btfsc NMEA endif goto GetByte ;Glitch movlw 8 movwf Counter clrf Temp ;probably not needed, but good practice InitByteLoop call HalfBitDelay call HalfBitDelay ;200us here nop bcf STATUS , C ;start in known state if NMEAPol == 1 btfss NMEA ;data polarity is inverted for RS232 polarity else btfsc NMEA endif bsf STATUS , C ;Data into Carry rrf Temp ;and then into Temp, LSB first decfsz Counter goto InitByteLoop ;loop is 8 + 2 * HalfBitDelay long call HalfBitDelay call HalfBitDelay ;Now have approx 100us of stop bit left movf Temp , W ;to use this data return ;Exit with received byte in W and Temp ;----------------------------------------- DoTime ; movf Stack9 , W movwf BCDHi movf Stack8 , W movwf BCDLo call BCDtoBIN movwf Hours movf Stack7 , W movwf BCDHi movf Stack6 , W movwf BCDLo call BCDtoBIN movwf Minutes movf Stack5 , W movwf BCDHi movf Stack4 , W movwf BCDLo call BCDtoBIN movwf Seconds return ;----------------------------------------- DoTimeOncore ; Oncore uses format HHMMSS.SS, movf Stack9 , W ; 9876543210 movwf BCDHi movf Stack8 , W movwf BCDLo call BCDtoBIN movwf Hours movf Stack7 , W movwf BCDHi movf Stack6 , W movwf BCDLo call BCDtoBIN movwf Minutes movf Stack5 , W movwf BCDHi movf Stack4 , W movwf BCDLo call BCDtoBIN movwf Seconds return ;----------------------------------------- DoStatus movlw "A" subwf Stack1, W btfsc STATUS, Z bsf GPSValid movlw "V" subwf Stack1, W btfsc STATUS, Z bcf GPSValid bcf LED ;End of useful data from NMEA sentence return ;----------------------------------------- HalfBitDelay ;4800 baud needs 2 x 104us, so delay 100us here movlw d'19' ;Also doubles as full baud delay for 9600 movwf DelCount BaudDelLoop nop nop decfsz DelCount goto BaudDelLoop return ;------------------------------- PushStack movf Stack9, W movwf StackA movf Stack8, W movwf Stack9 movf Stack7, W movwf Stack8 movf Stack6, W movwf Stack7 movf Stack5, W movwf Stack6 movf Stack4, W movwf Stack5 movf Stack3, W movwf Stack4 movf Stack2, W movwf Stack3 movf Stack1, W movwf Stack2 movf Stack0, W movwf Stack1 return ;------------------------------- BCDCwByte ;Sends BCD equivalent of a byte (from 0-99 only) movwf ALo ; with leading zero suppression clrf AHi call BinToBCD swapf BCDLo, W andlw 0x0F btfsc STATUS, Z goto SupprZero addlw 0x30 call SendCW SupprZero movf BCDLo, W andlw 0x0F addlw 0x30 call SendCW call CWDelay call CWDelay return ;---------------------------- BCDtoBIN ;Converts pairs of ASCII BCD characters to movf BCDHi , W ;binary in W andlw b'00001111' ;mask from ASCII to digit = [BCDHi] movwf Temp bcf STATUS , C rlf Temp rlf Temp ;Temp = 4 * [BCDHi] movlw b'00001111' andwf BCDHi , W ;W = [BCDHi] addwf Temp ;Temp = 5 * [BCDHi] bcf STATUS , C rlf Temp movf BCDLo , W andlw b'00001111' addwf Temp ;Temp = 10 * BCDHi + BCDLo movf Temp , W return ;------------------------------- BinToBCD ;Takes in AHi/ALo, calculates packed BCD equivalent clrf BCDHi ;Uses Counter, Temp + specifics. Destroys AHi/lo clrf BCDMe clrf BCDLo movlw d'16' movwf Counter BcdLoop rlf ALo rlf AHi rlf BCDLo rlf BCDMe rlf BCDHi decfsz Counter ;Need a final shift only, so loop count here goto BCDgo goto BCDone ;.......... BCDgo movf BCDHi , W addlw 3 movwf Temp btfsc Temp , 3 movwf BCDHi ;If BCDHI >= 5, add 3 and store back movf BCDHi , W addlw 0x30 movwf Temp btfsc Temp , 7 movwf BCDHi ;Adjust BCDHI if > 80 movf BCDMe , W ;Repeating for middle then low digits addlw 3 movwf Temp btfsc Temp , 3 movwf BCDMe movf BCDMe , W addlw 0x30 movwf Temp btfsc Temp , 7 movwf BCDMe movf BCDLo , W ; addlw 3 movwf Temp btfsc Temp , 3 movwf BCDLo movf BCDLo , W addlw 0x30 movwf Temp btfsc Temp , 7 movwf BCDLo goto BcdLoop ;........... BCDone return ;-------------------------- ;---------------------------- Send232 ;Enter with data in W, send it to PC for debugging movwf Temp ;Uses Temp, counter bsf TestPort ;start bit call HalfBitDelay call HalfBitDelay movlw 8 movwf Counter SendDatLoop rrf Temp ;bit into C btfsc STATUS , C bcf TestPort btfss STATUS , C bsf TestPort ;could have 2us jitter depending on 1/0 call HalfBitDelay call HalfBitDelay decfsz Counter goto SendDatLoop ;loop 8 + BitDelay long bcf TestPort ;stop bit call HalfBitDelay call HalfBitDelay return ;------------------------- ;=============================== org 0x3A0 ;Force tables to end to avoid page boundary problems. CWTable ; CW Characters in compressed form. movwf Temp movlw 3 movwf PCLATH movf Temp , W addwf PCL retlw b'00000000' ; [sp] 00 retlw b'00000000' ; ! 00 retlw b'10010110' ; " 96 retlw b'00010101' ; # 15 retlw b'00000000' ; $ 00 retlw b'00000000' ; % 00 retlw b'00000000' ; & 00 retlw b'11110110' ; ' F6 retlw b'10110101' ; ( B5 retlw b'01101111' ; ) 6F retlw b'01000101' ; * 45 retlw b'00000000' ; + 00 retlw b'10011111' ; , 9F Coding for CW characters of up to retlw b'00001111' ; - 0F 6 elements. Stored as : retlw b'10101110' ; . AE Bits 7 to 3 make first five CW elements retlw b'10010101' ; / 95 Reading Left to Right retlw b'11111101' ; 0 FD Coded as 0 = dit 1 = dah retlw b'01111101' ; 1 7D Bts 2-0 are number of elements except for retlw b'00111101' ; 2 3D 110 > 1st element is a dit retlw b'00011101' ; 3 1D 111 > 1st element is a dah retlw b'00001101' ; 4 0D retlw b'00000101' ; 5 05 retlw b'10000101' ; 6 85 retlw b'11000101' ; 7 C5 retlw b'11100101' ; 8 E5 retlw b'11110101' ; 9 F5 retlw b'00000000' ; : 00 retlw b'00000000' ; ; 00 retlw b'00000000' ; < 00 retlw b'10001101' ; = 8D retlw b'00000000' ; > 00 retlw b'01100110' ; ? 66 retlw b'00000000' ; @ 00 retlw b'01000010' ; A 42 retlw b'10000100' ; B 84 retlw b'10100100' ; C A4 retlw b'10000011' ; D 83 retlw b'00000001' ; E 01 retlw b'00100100' ; F 24 retlw b'11000011' ; G C3 retlw b'00000100' ; H 04 retlw b'00000010' ; I 02 retlw b'01110100' ; J 74 retlw b'10100011' ; K A3 retlw b'01000100' ; L 44 retlw b'11000010' ; M C2 retlw b'10000010' ; N 82 retlw b'11100011' ; O E3 retlw b'01100100' ; P 64 retlw b'11010100' ; Q D4 retlw b'01000011' ; R 43 retlw b'00000011' ; S 03 retlw b'10000001' ; T 81 retlw b'00100011' ; U 23 retlw b'00010100' ; V 14 retlw b'01100011' ; W 63 retlw b'10010100' ; X 94 retlw b'10110100' ; Y B4 retlw b'11000100' ; Z C4 ;============================ end