TITLE "XZBeacon.asm" ;====================================================================================================== ;* XZBeacon ;* CW Beacon for LU1XZ operation ;* ;* Pedro E. Colla (LU7HZ) Argentina 2009,2010 ;* code excerpts from F5HLA's "Beacon CW pour F1PDX apres keyer morse" ported to PIC 12F675 ;====================================================================================================== ;* Compiler directives ;====================================================================================================== List P=12F675, R=DEC INCLUDE "p12F675.inc" #DEFINE BEACON_TIME 06 ;*---> Uncomment when debugging .... ;#DEFINE DEBUG ;====================================================================================================== ;* EQUates ;====================================================================================================== ;*------------------------------------------------------------------------------------------------- ;* Morse Characters Table ;*------------------------------------------------------------------------------------------------- M_END EQU 0x00 ; Ending delimiter M_SKIP EQU 0x01 ; Skip this entry M_SP EQU 0x02 ; Word space M_USER EQU 0x03 ; Toggle to USER RAM M_TO EQU 0x80 ; Timeout return from GETCW M_0 EQU 0xFC ; 0 1111 1100 = DAH-DAH-DAH-DAH-DAH M_1 EQU 0x7C ; 1 0111 1100 = DI-DAH-DAH-DAH-DAH M_2 EQU 0x3C ; 2 0011 1100 = DI-DI-DAH-DAH-DAH M_3 EQU 0x1C ; 3 0001 1100 = DI-DI-DI-DAH-DAH M_4 EQU 0x0C ; 4 0000 1100 = DI-DI-DI-DI-DAH M_5 EQU 0x04 ; 5 0000 0100 = DI-DI-DI-DI-DIT M_6 EQU 0x84 ; 6 1000 0100 = DAH-DI-DI-DI-DIT M_7 EQU 0xC4 ; 7 1100 0100 = DAH-DAH-DI-DI-DIT M_8 EQU 0xE4 ; 8 1110 0100 = DAH-DAH-DAH-DI-DIT M_9 EQU 0xF4 ; 9 1111 0100 = DAH-DAH-DAH-DAH-DIT M_AR EQU 0x54 ; 0101 0100 = DI-DAH-DI-DAH-DIT M_SK EQU 0x16 ; 0001 0110 = DI-DI-DI-DAH-DI-DAH M_PER EQU 0x56 ; 0101 0110 = DI-DAH-DI-DAH-DI-DAH M_COM EQU 0xCE ; 1100 1110 = DAH-DAH-DI-DI-DAH-DAH M_BT EQU 0x8C ; 1000 1100 = DAH-DI-DI-DI-DAH M_QUE EQU 0x32 ; ? 0011 0010 = DI-DI-DAH-DAH-DI-DIT M_DN EQU 0x94 ; / 1001 0100 = DAH-DI-DI-DAH-DIT M_A EQU 0x60 ; A 0110 0000 = DI-DAH M_B EQU 0x88 ; B 1000 1000 = DAH-DI-DI-DIT M_C EQU 0xA8 ; C 1010 1000 = DAH-DI-DAH-DIT M_D EQU 0x90 ; D 1001 0000 = DAH-DI-DIT M_E EQU 0x40 ; E 0100 0000 = DIT M_F EQU 0x28 ; F 0010 1000 = DI-DI-DAH-DIT M_G EQU 0xD0 ; G 1101 0000 = DAH-DAH-DIT M_H EQU 0x08 ; H 0000 1000 = DI-DI-DI-DIT M_I EQU 0x20 ; I 0010 0000 = DI-DIT M_J EQU 0x78 ; J 0111 1000 = DI-DAH-DAH-DAH M_K EQU 0xB0 ; K 1011 0000 = DAH-DI-DAH M_L EQU 0x48 ; L 0100 1000 = DI-DAH-DI-DIT M_M EQU 0xE0 ; M 1110 0000 = DAH-DAH M_N EQU 0xA0 ; N 1010 0000 = DAH-DIT M_O EQU 0xF0 ; O 1111 0000 = DAH-DAH-DAH M_P EQU 0x68 ; P 0110 1000 = DI-DAH-DAH-DIT M_Q EQU 0xD8 ; Q 1101 1000 = DAH-DAH-DI-DAH M_R EQU 0x50 ; R 0101 0000 = DI-DAH-DIT M_S EQU 0x10 ; S 0001 0000 = DI-DI-DIT M_T EQU 0xC0 ; T 1100 0000 = DAH M_U EQU 0x30 ; U 0011 0000 = DI-DI-DAH M_V EQU 0x18 ; V 0001 1000 = DI-DI-DI-DAH M_W EQU 0x70 ; W 0111 0000 = DI-DAH-DAH M_X EQU 0x98 ; X 1001 1000 = DAH-DI-DI-DAH M_Y EQU 0xB8 ; Y 1101 1000 = DAH-DAH-DI-DAH M_Z EQU 0xC8 ; Z 1100 1000 = DAH-DAH-DI-DIT ;*------------------------------------------------------------------------------------------------- ;* Code Speed Index Table ;* Each index points to a delay value in the code speed index table. ;* Use the call IDX2SPD to convert index to speed delay ;*------------------------------------------------------------------------------------------------- WPM_5 EQU 00D WPM_6 EQU 01D WPM_7 EQU 02D WPM_8 EQU 03D WPM_9 EQU 04D WPM_10 EQU 05D WPM_11 EQU 06D WPM_12 EQU 07D WPM_13 EQU 08D WPM_14 EQU 09D WPM_15 EQU 10D WPM_16 EQU 11D WPM_17 EQU 12D WPM_18 EQU 13D WPM_19 EQU 14D WPM_20 EQU 15D WPM_21 EQU 16D WPM_22 EQU 17D WPM_23 EQU 18D WPM_24 EQU 19D WPM_25 EQU 20D WPM_26 EQU 21D WPM_27 EQU 22D WPM_28 EQU 23D WPM_29 EQU 24D WPM_30 EQU 25D WPM_31 EQU 26D WPM_32 EQU 27D WPM_33 EQU 28D WPM_34 EQU 29D WPM_35 EQU 30D WPM_36 EQU 31D WPM_37 EQU 32D WPM_38 EQU 33D WPM_39 EQU 34D WPM_40 EQU 35D WPM_41 EQU 36D WPM_42 EQU 37D WPM_43 EQU 38D WPM_44 EQU 39D WPM_45 EQU 40D WPM_46 EQU 41D WPM_47 EQU 42D WPM_48 EQU 43D WPM_49 EQU 44D ;*------------------------------------------------------------------------------------------------- ;* General EQUates ;*------------------------------------------------------------------------------------------------- MAXSPDIDX EQU WPM_49 SPD_CONST EQU 20D ; Orig 212D SVAL EQU (5D * SPD_CONST) ; Fudged By oscilloscope verification SPEED_DEFAULT EQU WPM_10 ; Speed set at 10 WPM CURMSG1 EQU MSG5-MSGBASE ; Select message: User defined MAXCARRAM EQU 56 ; nb max de caracteres at the message AGC_DELAY EQU 04 PERIOD EQU 12D ; Final delay is PERIOD*TIMEBASE*1 mSec USERON EQU 03H ; Use USERRAM as message source when set ;====================================================================================================== ;* MACROS ;====================================================================================================== ;*------------------------------------------------------------------------------------------------- ;* Macro to Read from EEPROM ;* Data to write assumed to be left at W (pls note his is not a call but inline code). ;*------------------------------------------------------------------------------------------------- READEE MACRO ; Data is at W bsf STATUS,RP0 movwf EEADR bsf EECON1,RD movf EEDATA,W bcf STATUS,RP0 ENDM ; End of macro ;*------------------------------------------------------------------------------------------------- ;* Macro to Write into from EEPROM ;* Data to write assumed to be at W ;*------------------------------------------------------------------------------------------------- WRITEE MACRO ; Data in W local wait bsf STATUS,RP0 ; set bank1 wait CLRWDT ; clear watchdog btfsc EECON1,WR ; Verify if there is an on-going write goto wait ; Yes, wait bcf STATUS,RP0 ; Move to Bank1 movwf EEDATA ; Load data into register movf BX,W ; Change writing address movwf EEADR ; Load into register bsf STATUS,RP0 ; Swap to Bank1 ;*--- This is black magic as recommended by the 12F675 data sheet bcf EECON1,EEIF ; Raise EoW flag bsf EECON1,WREN ; Allow access to write movlw 0x55 ; Load 0x55 movwf EECON2 ; send command movlw 0xAA ; Load 0xAA movwf EECON2 ; send commaand ;*--- End of black magic bsf EECON1,WR ; complete writing cycle bcf EECON1,WREN ; Continue with next bcf STATUS,RP0 ; return to Bank0 endm ;*------------------------------------------------------------------------------------------------- ;* DATA segment ;*------------------------------------------------------------------------------------------------- CBLOCK 0x20 PROCLAT ; Process Latch DEL ; Variable used for delays DELAYHI ; High delay counter register DELAYLO ; Low delay counter register TIMEBASE ; Morse Time Base WTEMP ; for loading PCLATH SPEEDIDX ; ;*--- Crudely ported from a Z80 heritage processor, still finding it's way into the XXI century... AX ; Z80 memorabilia zone (still miss you 'pal). AL ; General Purpose Registers AH ; BX ; CX ; CL ; CH ; DH ; ;*--- Buffer to held beacon message at run time... USERRAM: MAXCARRAM ; User Message ENDC ;*------------------------------------------------------------------------------------------------- ;* CODE segment ;*------------------------------------------------------------------------------------------------- PAGE ifdef DEBUG __CONFIG _BODEN_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT else __CONFIG _BODEN_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_ON & _INTRC_OSC_NOCLKOUT endif Org 0x00 ; Start program at the beginning of the CODE area GOTO INIT ORG 0x04 ; Better not enable GIE otherwise, He'll loop! INTVEC GOTO INTVEC ;*------------------------------------------------------------------------------------------------- ;* main performing loop ;*------------------------------------------------------------------------------------------------- main movf BX, W ; Verify if master cycle control already reach zero btfsc STATUS, Z ; goto transmit ; Yes, then go to transmit decf BX, F ; No, continue waiting goto waitloop ; It is listening time, so listen transmit bsf GPIO,1 ; Set TX=ON movlw CURMSG1 ; Store current (main) beacon message call sendmsg ; Send the message call beepout ; Beep it out bcf GPIO,1 ; Set TX=OFF movlw BEACON_TIME ; restore master cycle control movwf BX ; Store in pseudo Z80 register goto main ; go to continue the loop waitloop clrwdt ; clear the watchdog to ensure full length (2.18 secs) sleep ; sleep that much nop ; movwf INTCON ; clear interrupts goto main ; endless loop ;*------------------------------------------------------------------------------------------------- ;* This is the master delay routine, all delays are built using it ;* delay routine (5 uSecs * 200 = 1 mSec) for PIC12675 @ 4 MHz ;*------------------------------------------------------------------------------------------------- delay ; a delay for del milliseconds movlw 1 ; movwf DEL ; delayloop movlw 200 sublw 1 ; this loop takes 5us*200 = 1ms sublw 0 btfss STATUS, Z ; goto $-3 ; decfsz DEL, f ; goto delayloop ; return ; ;*------------------------------------------------------------------------------------------------- ;* BeepOut ;* Send a beep for abt 1 sec ;*------------------------------------------------------------------------------------------------- beepout bsf GPIO,1 ; movlw 12 ; delay for 1 sec movwf AX ; dlybeep clrwdt ; call ditdly ; decfsz AX,F ; goto dlybeep ; bcf GPIO,1 ; return ;*------------------------------------------------------------------------------------------------- ;* ditdly ;* dit delay ;*------------------------------------------------------------------------------------------------- ditdly movf TIMEBASE,W ; movwf DELAYHI ; dd_nt0 clrwdt ; movlw PERIOD ; movwf DELAYLO ; dd_nt1 call delay ; decfsz DELAYLO,F ; goto dd_nt1 ; decfsz DELAYHI,F ; goto dd_nt0 ; return ; ;*------------------------------------------------------------------------------------------------- ;* ditout ;* send a dit ;*------------------------------------------------------------------------------------------------- ditout clrwdt ; bsf GPIO,0 ; call ditdly ; bcf GPIO,0 ; call ditdly ; return ; ;*------------------------------------------------------------------------------------------------- ;* dahout ;* send a dah ;*------------------------------------------------------------------------------------------------- dahout clrwdt ; bsf GPIO,0 ; movlw 3 ; movwf CX ; dahdly call ditdly ; decfsz CX ; goto dahdly ; bcf GPIO,0 ; call ditdly ; return ; ;*------------------------------------------------------------------------------------------------- ;* wordspace and letterspace ;* space between letters and words ;*------------------------------------------------------------------------------------------------- wordspace movlw 6 ; goto wlspac0 ; letterspace movlw 3 ; wlspac0 movwf CX ; wlspac1 call ditdly ; decfsz CX,F ; goto wlspac1 ; return ;*------------------------------------------------------------------------------------------------- ;* OSCHAR ;* output single morse character ;* receives character to send (already morse coded) in W, returns W=0 ;*------------------------------------------------------------------------------------------------- oschar movwf AL ; copy character in W to AL osloop movf AL, W ; get coded morse addwf AL, F ; AL*2, if Z==0 DONE else C=DIT/DAH btfss STATUS, Z ; skip if zero goto oscont ; send character call letterspace ; space retlw 0 oscont btfsc STATUS, C ; C==1 then dah else dit goto osdah ; call ditout ; goto osloop ; osdah call dahout ; goto osloop ; ;*------------------------------------------------------------------------------------------------- ;* dec2cw ;* convert numbers to CW characters, receives number in W register, returns literal in W ;*------------------------------------------------------------------------------------------------- dec2cw movwf WTEMP ;move W to WTEMP movlw HIGH($) ;move high nibble of address into W movwf PCLATH ;move W to PCLATH movf WTEMP,W ;move WTEMP to W addwf PCL,F ;add W and PC ;*---- Depending on the number stored in W returns (also in W) the Morse coded number DECTBL RETLW M_0 RETLW M_1 RETLW M_2 RETLW M_3 RETLW M_4 RETLW M_5 RETLW M_6 RETLW M_7 RETLW M_8 RETLW M_9 ;*------------------------------------------------------------------------------------------------- ;* Message table ;* Call with message pointer in DH reg ;* Table entry *DH++ is returned in W. ;* We start at a new page to allow us to have ;* a long message table which we can be sure ;* will never cross a page boundary. ;*------------------------------------------------------------------------------------------------- getmsg movf DH, W ; incf DH, F ; movwf WTEMP ; movlw HIGH($) ; movwf PCLATH ; movf WTEMP,W ; addwf PCL, F ;Jump thru table ;*------------------------------------------------------------------------------------------------- ;*----- This is the base for the messages used in the program (except the main one located at the end MSGBASE ;*------------------------------------------------------------------------------------------------- ;*--- VVV {User Message} MSG5 RETLW M_V RETLW M_V RETLW M_V RETLW M_BT RETLW M_SP RETLW M_USER ; User Message RETLW M_SP RETLW M_END ;*------------------------------------------------------------------------------------------------- ;*--- UR RST MSG6 RETLW M_END ;*------------------------------------------------------------------------------------------------- MSG7 RETLW M_END ;*------------------------------------------------------------------------------------------------- ;* TIMEBASE Defaults ; Using ARRL Handbook as a reference, a 5 WPM dit ; is 240 msec, a dah is 720 ms. ; Formula: ; ; Count= (((5/WPM) * 240) / SIDETONE_PERIOD) ; For Sidetone Freq of 870 Hz (1.15 ms): ; Count = ((5/WPM) * (240/1.15)) ;*------------------------------------------------------------------------------------------------- idx2spd movwf WTEMP ; movlw HIGH($) ; movwf PCLATH ; movf WTEMP,W ; addwf PCL, F ; SPEEDTBL RETLW SVAL/5D ; WPM_5 RETLW SVAL/6D ; WPM_6 RETLW SVAL/7D ; WPM_7 RETLW SVAL/8D ; WPM_8 RETLW SVAL/9D ; WPM_9 RETLW SVAL/10D ; WPM_10 RETLW SVAL/11D ; WPM_11 RETLW SVAL/12D ; WPM_12 RETLW SVAL/13D ; WPM_13 RETLW SVAL/14D ; WPM_14 RETLW SVAL/15D ; WPM_15 ;*------------------------------------------------------------------------------------------------- ;* SENDMSG ;* ;* Sends message pointed to by W, be careful of case where message table grows so large that it pushes ;* this code above 0xff or a CALL instruction will not be able to reach it. ;* Uses AL, DH (DH = MSGIDX) ;*------------------------------------------------------------------------------------------------- sendmsg movwf DH ;Store index to message to send s0loop clrwdt btfsc PROCLAT, USERON ;Is in USER buffer mode? goto s0user ;YES, user mode call getmsg ;NOT, go fetch the message needed get *DH++ in W goto s0cont ;and continue s0user movf INDF, W ;get *USERRAM++ incf FSR, F ; s0cont movwf AL ;actual token to send movlw M_END ; delimiter subwf AL, W ; btfss STATUS, Z ;Skip if not delimiter goto s1cont ; ;*--- Process a token when found btfss PROCLAT, USERON ;End of the user buffer or end of the whole buffer? goto s1done ;It is regarded as the end of the buffer bcf PROCLAT, USERON ;Clear the mark we're processing the buffer goto s0loop ; s1done bcf PROCLAT, USERON ; Clear retlw 0 ; s1cont movlw M_USER ; Token signaling the start of a user message subwf AL, W ; btfsc STATUS, Z ; skip if not compare goto s1user ; go to handle the user buffer movlw M_SKIP ; Token? subwf AL, W ; btfsc STATUS, Z ; skip if not compare goto s0loop movlw M_SP ; word space? subwf AL, W ; btfss STATUS, Z ; skip if compare goto s2loop ; call wordspace ; send a word space goto s0loop ; s1user movlw USERRAM ; point to the start of the user buffer s100 movwf FSR ; indirect addressing bsf PROCLAT, USERON ; flag the usage of the user buffer goto s0loop ; s2loop clrwdt ; Reset watchdog timer movf AL, W ; get codified morse token addwf AL, F ; AL*2, if (Z==0) DONE else C=DIT/DAH btfss STATUS, Z ; skip if zero goto s2cont ; call letterspace ; inter letter space goto s0loop ; s2cont clrwdt ; btfsc STATUS, C ; C==1 then DAH else DIT goto s2dah ; call ditout ; goto s2loop ; s2dah clrwdt ; call dahout ; goto s2loop ; ;*------------------------------------------------------------------------------------------------- ;* Initialization code ;* External inputs has been mapped as ;* bit 0 KEY (o) Digital ;* bit 1 AGC (i) Analógico ;* bit 2 TX (o) Digital ;* bit 3 SQL (i) Digital ;*------------------------------------------------------------------------------------------------- INIT bcf STATUS,RP0 ; select BANK0 clrf GPIO ; clear GPIO clrf INTCON ; clear INTCON movlw 0x07 ; movwf CMCON ; comparator OFF movlw B'10000000' ; init A/D ensure shift to left movwf ADCON0 ; bsf STATUS,RP0 ; select BANK1 movlw B'01110010' ; select GP0 (KEY),AN1(AGC),GP2(TX),GP3(SQL) movwf ANSEL ; clrf PIE1 ; reset peripherical clrf IOC ; clrf WPU ; movlw B'00001111' ; Assign pre-scaler to WDT and 1:128 value (2.18 Sec) movwf OPTION_REG ; movlw B'00001010' ; Assign pin 1 and 3 as input, 0 and 2 as output movwf TRISIO ; ;*---- Initialize master sending cycle bcf STATUS,RP0 ; select BANK0 again movlw BEACON_TIME ; init master cycle control (trigger at first cycle) movwf BX ; Store in pseudo Z80 register ;*---- Set the default sending speed movlw SPEED_DEFAULT ; movwf SPEEDIDX ; call idx2spd ; movwf TIMEBASE ; ;*---- Initialize beacon message from EEPROM in RAM movlw MAXCARRAM ;store max buffer at W movwf BX ;store max buffer at BX initmsg decf BX,F ;decrease BX movf BX, W ;store BX at W READEE ;read address at W movwf AL ;recover byte at that address, store in AL movf BX,W ;store pointer to buffer in W addlw USERRAM ;compute USERRAM+BX movwf WTEMP movwf FSR ;store at indirect addr register movf AL,W ;now character is in W movwf INDF ;move w to indirect addr register pointed movf BX,F ;recover BX on itself btfss STATUS,Z ;is it zero? goto initmsg ;no continue waitmsg btfsc EECON1, RD ;still reading? goto waitmsg ;wait goto main ;*---- End of code ;*=================================================================================================== ;* EEPROM ;* Message to Transmit (customize and assure it is ended with M_END) ;*=================================================================================================== org 0x2100 ; Starting EEPROM Address DE M_B DE M_C DE M_N DE M_SP DE M_L DE M_U DE M_1 DE M_X DE M_Z DE M_DN DE M_H DE M_SP DE M_L DE M_U DE M_1 DE M_X DE M_Z DE M_DN DE M_H DE M_SP DE M_R DE M_C DE M_SP DE M_M DE M_A DE M_L DE M_V DE M_I DE M_N DE M_A DE M_S DE M_SP DE M_F DE M_F DE M_7 DE M_8 DE M_U DE M_P DE M_SP DE M_P DE M_S DE M_E DE M_SP DE M_Q DE M_S DE M_L DE M_SP DE M_7 DE M_3 DE M_END DE 0x00 DE 0x00 end