'**************************************************************** '* Name : 500_TX.BAS * '* Author : Wim de Vree * '* Notice : Copyright (c) 2010 PE1GRL * '* : All Rights Reserved * '* Date : 2010/03/08 * '* Version : 0.1 * '* Notes : * '* : * '* Revision History * '* 2010/03/08 Created program to controll the 500kHz Smart * '* Beacon Transmitter. As all frequencies are configured on the * '* PC we don't need Freq->Phase conversions freeing code for * '* HELL and other funny modes. * '* 2010/03/20 Completed program * '* todo: check if we can also generate the WSPR * '* symbols using the phase registers (now just a * '* frequency is loaded) * '* 2010/03/27 I/O remapped to suit PA0CHN DDS module layout * '**************************************************************** Device 16F628A ; We gebruiken een 16F628A type Config HS_OSC, WDT_OFF, PWRTE_ON, LVP_OFF All_Digital TRUE ; Alle ingangen digitaal Xtal 20 ; PIC freq for UART All_Digital TRUE ;Optimiser_Level 3 Symbol SW_GO PORTB.7 ' "GO" switch Symbol SW_SELECT PORTB.6 ' "Select" switch Symbol CW_KEY PORTB.1 ' Morse key Symbol RS232_IN PORTA.0 ' RS232 input (GPS or host) Symbol RS232_OUT PORTA.1 ' RS232 output (host) Symbol RS232_SPD 16572 ' 4800 Bd, 188 = MAX, 16572 = Direct Symbol RS232_SPD_INV 188 Symbol DDS_FSYNC PORTB.2 ' AD9832 FSYNC pin Symbol DDS_SDATA PORTB.4 ' AD9832 SDATA pin Symbol DDS_SCLK PORTB.5 ' AD9832 SCLCK pin Symbol LED_STROBE PORTB.3 ' HEF4094 strobe pin Symbol LED_SDATA DDS_SDATA ' HEF4094 data pin Symbol LED_SCLK DDS_SCLK ' HEF4094 clock pin Symbol TRUE 1 Symbol FALSE 0 ' Configuration settings stored in EE memory EE_DDS_CLOCK EData As Dword 20000000 ' Xtal for PIC & DDS EE_MODE EData As Byte 3 ' Selected transmission mode (keyed) EE_WSPR_PHINC EData As Dword 108211701 ' WSPR Incr (503900 Hz) EE_WSPR_OFFSET EData As Word 0,315,629,944 ' Offsets for symbols EE_WSPR_T1TIME EData As Word 12204 ' Symbol timing EE_WSPR_OPTION EData As Byte 3 ' Option bits (duty cycle 50%) ' PE1GRL JO21 10mWatt (sample) EE_WSPR_MSG EData $8F, $2A, $AB, $37, $18, $66, $B5, $8A EData $B2, $C4, $A2, $1A, $AD, $C7, $E8, $19 EData $E8, $39, $13, $69, $30, $07, $36, $B9 EData $98, $88, $49, $F0, $51, $F8, $06, $5C EData $0A, $CE, $7A, $8A, $4A, $3B, $85, $B6 EData $02 EE_CWB_PHINC EData As Dword 108125801 ' Base Phase increment for CW mode EE_CWB_DITLEN EData As Byte 100 ' Duration of a "." in ms EE_CWB_PAUSE EData As Byte 30 ' Pause between transmissions in sec ' 24 bytes beacon text EE_CWB_BTEXT EData "12345678901234567890123",0 EE_QRSS_PHINC EData As Dword 108125801 ' WRSS Incr (503500 Hz) EE_QRSS_DITLEN EData As Byte 1 ' Duration of a "." in sec EE_QRSS_PAUSE EData As Byte 30 ' Pause between transmissions in sec EE_QRSS_BTEXT EData "123456789012345",0 ' 16 bytes beacon text EE_CW_PHINC EData As Dword 107696304 ' CW increment (501500 Hz) Dim DDSIncr As Dword ; DDS Phase increment Dim DDSFreg As Bit ; DDS Freq. Reg. to use Dim T1ticks As Byte ; nr. of T1 ints/tick Dim T1start As Word ; Timer 1 start value Dim T1busy As Bit ; Flag for T1 delays Dim wTemp As Word ; Temp Word variable Dim bTemp As Byte ; Temp Byte variable Dim i As Byte, j As Byte Dim SerData As Byte Dim Mins As Byte, Secs As Byte ; Time keeping Dim LedPattern As Byte ' 8 status leds accessed via shiftreg Dim Mode As Byte ' Mode used when transmitting Dim Transmitting As Bit Dim msgIndex As Byte Dim WSPR_Data As Byte ; Current Byte (contains 4 symbols) Dim WSPR_Symbol As Byte ; Current Symbol (0..3) Dim WSPR_FirstSymbol As Bit Dim BaseIncr As Dword ' Base increment for DDS (Main frequency) Dim CW_char As WSPR_Data ; Actual char to transmit Dim DitDelay As Word ; Dit-delay in milliseconds Dim TextBase As Byte ; Offset to start of text in EE Dim IdleTime As Byte ; Time between transmissions in seconds Dim GPS_Lock As Bit Dim NMEA_String[14] As Byte Dim TX_Pattern As Dword Symbol LED_GO LedPattern.4 ' ON if beacon running Symbol LED_TX LedPattern.5 ' ON if transmitting Symbol LED_GPS_NOLOCK LedPattern.6 ' Blinks if receving unlocked data Symbol LED_GPS_LOCK LedPattern.7 ' Blinks if receving locked data Symbol EXT_TX PORTA.2 ' Signal line to ext. PA High on TX GoTo Main_Prog ' Goto Main program On_Hardware_Interrupt GoTo TimerISR ' Used for WSPR symbol timing Declare fsr_context_save OFF Dim TIMER1 As TMR1L.Word Symbol GIE = INTCON.7 ' Global Interrupt Enable bit Symbol PEIE = INTCON.6 Symbol TMR1IF = PIR1.0 ' TIMER1 Overflow Interrupt Flag bit Symbol TMR1IE = PIE1.0 ' TIMER1 Overflow Interrupt Enable bit Symbol TMR1ON T1CON. 0 ' TIMER1 On bit ; Timer1 ISR, only interrupt source so no testing is required ;------------------------------------------------------------------------------- ; Each WSPR symbol should last 682666.7 microseconds. ; The max delay on a 20Mhz PIC is +/- 13 milliseconds ; (if the prescaler is not used). We therefore use an ISR to count 64 ; timer ticks. The resolution therefore is 12.8 usec and the maximum ; delay 838860 usec. The symbol time is 682667 usecs so the maximum ; delay is long enough. ;------------------------------------------------------------------------------- TimerISR: Context Save TIMER1 = TIMER1 + T1start ' reload timer Clear TMR1IF Inc T1ticks If T1ticks.6 <> 0 Then ' 64 ticks passed T1ticks = 0 T1busy = 0 ' Flag time completed EndIf Context Restore '----------------------------------------------------------- Main_Prog: PORTA = %00000000 PORTB = %00000100 TRISA = %11111001 ' Definieer I/O lijnen TRISB = %11000011 Clear Clear EXT_TX ' Not transmitting Clear DDS_SCLK ' Clock Idle Clear DDS_SDATA ' Data low Clear LED_STROBE Set DDS_FSYNC ' idle = high for AD9832 LedPattern = 0 : GoSub ShowLeds ' Status leds are off SerOut RS232_OUT, RS232_SPD, [" Here we go", 13,10] While 1 == 1 ' After startup listen for remote PC LedPattern = LedPattern ^ 0x0F : GoSub ShowLeds If SW_GO == 0 Then ' Go Switch pressed DelayMS 20 ' Simple debounce While SW_GO == 0 : : Wend ' Wait for key release DelayMS 20 ' Simple debounce Break EndIf SerIn RS232_IN, RS232_SPD, 250, NoHostCommand, [SerData] If SerData == "!" Then GoSub DoHostCommand NoHostCommand: Wend LedPattern = 0 : GoSub ShowLeds Mode = (ERead EE_MODE) & 0x03 ; 4 available modes GoSub ShowMode While 1 == 1 ; Select mode or GO If SW_GO == 0 Then ' Go Switch pressed DelayMS 20 ' Simple debounce While SW_GO == 0 : : Wend ' Wait for key release DelayMS 20 ' Simple debounce Break EndIf If SW_SELECT == 0 Then DelayMS 20 ' Simple debounce While SW_SELECT == 0 : : Wend ' Wait for key release DelayMS 20 ' Simple debounce Inc Mode ' Step to next mode Mode = Mode & 3 ' Wrap around after all 4 modes GoSub ShowMode EndIf Wend GoSub DDS_Init DelayMS 10 GoSub StopTransmit ' Make sure it's off ;------------------------------------------------------------------------------- ; Transmit using the selected mode. ; This enters an infinite loop, you have to power down to stop the transmission ;------------------------------------------------------------------------------- LED_GO = 1 : GoSub ShowLeds On Mode GoTo DoWSPR,DoQRSS,DoCWB,DoCWK Stop ;------------------------------------------------------------------------------- ; Handling of HOST commands: read or write the EE config ;------------------------------------------------------------------------------- DoHostCommand: LedPattern = 0 : GoSub ShowLeds ' Leds off SerOut RS232_OUT, RS232_SPD, [">"] ' Now in HOST mode SerIn RS232_IN, RS232_SPD, [SerData] ' Await command SerOut RS232_OUT, RS232_SPD, [SerData] ' Echo command If SerData == "R" Then ' "Read", send EE to PC For i=0 To 127 SerData = ERead i ' Read Setting SerOut RS232_OUT, RS232_SPD, [SerData] Next Else If SerData == "W" Then ' "Write", accept EE from PC For i=0 To 127 SerIn RS232_IN, RS232_SPD, [SerData] EWrite i, [SerData] ; Write setting SerOut RS232_OUT, RS232_SPD, [SerData] Next EndIf HostTimeOut: Return ;------------------------------------------------------------------------------- ; WSRP Beacon code ; Timing is done by using a NMEA GPS unit. Transmissions are disabled it there ; is no lock on the GPS signal. Uses the $GPRMC sentence ;------------------------------------------------------------------------------- DoWSPR: While 1 == 1 j = 0 TX_Pattern = LRead WSPR_TXTable ' Get the 32bit tx sequence to use While j<32 ' Wait for GPS time data SerIn RS232_IN, RS232_SPD_INV, [Wait("$GPRMC,"),Str NMEA_String\12] GPS_Lock = 0 For i=0 To 11 ' Check if GPS is locked If NMEA_String[i] == "," Then If NMEA_String[i+1] == "A" Then GPS_Lock = 1 EndIf Next If GPS_Lock == 1 Then ' GPS has lock, get mm:ss info Mins = (NMEA_String[2] - "0") * 10 + (NMEA_String[3] - "0") Secs = (NMEA_String[4] - "0") * 10 + (NMEA_String[5] - "0") If Secs == 0 And Mins.0 == 0 Then bTemp = TX_Pattern.Byte3 If bTemp.7 <> 0 Then DelayMS 1000 ; Start at even minute + 1s GoSub WSPR_SendMessage GoSub StopTransmit EndIf TX_Pattern = TX_Pattern << 1 Inc j Else LED_GPS_LOCK = 1 : GoSub ShowLeds DelayMS 100 LED_GPS_LOCK = 0 : GoSub ShowLeds EndIf Else LED_GPS_NOLOCK = 1 : GoSub ShowLeds DelayMS 100 LED_GPS_NOLOCK = 0 : GoSub ShowLeds EndIf Wend Wend ;------------------------------------------------------------------------------- ; CW Beacon code ;------------------------------------------------------------------------------- DoCWB: SerOut RS232_OUT, RS232_SPD, [" CW BEACON", 13,10] While 1==1 BaseIncr = ERead EE_CWB_PHINC ' Read base frequency DitDelay.HighByte = 0 ' Read CW speed (0..255 msec) DitDelay.LowByte = ERead EE_CWB_DITLEN SerOut RS232_OUT, RS232_SPD, [" Dit delay ", Dec DitDelay , 13,10] TextBase = EE_CWB_BTEXT ' Addres of text in EE IdleTime = ERead EE_CWB_PAUSE ' Delay between transmissions GoSub CW_SendMessage SerOut RS232_OUT, RS232_SPD, [" Delaying ", Dec IdleTime, " sec" , 13,10] For i=0 To IdleTime DelayMS 1000 Next Wend ;------------------------------------------------------------------------------- ; QRSS Beacon code, almost identical to CW Beacon ; But the dot-delay is given in seconds, not milliseconds ;------------------------------------------------------------------------------- DoQRSS: While 1==1 BaseIncr = ERead EE_QRSS_PHINC ' Read base frequency DitDelay.HighByte = 0 ' Read QRSS speed (0..30 sec) DitDelay.LowByte = ERead EE_QRSS_DITLEN DitDelay = DitDelay * 1000 ' Convert to seconds TextBase = EE_QRSS_BTEXT ' Addres of text in EE IdleTime = ERead EE_QRSS_PAUSE ' Delay between transmissions GoSub CW_SendMessage For i=0 To IdleTime DelayMS 1000 Next Wend ;------------------------------------------------------------------------------- ; CW Keyed: if the KEY down transmit, otherwise stay IDLE ;------------------------------------------------------------------------------- DoCWK: Transmitting = 0 BaseIncr = ERead EE_CW_PHINC ; Get frequency While 1 == 1 If CW_KEY == 0 Then If Transmitting == 0 Then ; SerOut RS232_OUT, RS232_SPD, ["Key down",13,10] GoSub StartTransmit DelayMS 20 ; Debounce EndIf Else If Transmitting <> 0 Then ; SerOut RS232_OUT, RS232_SPD, ["Key up",13,10] GoSub StopTransmit DelayMS 10 ; Debounce EndIf EndIf Wend ;------------------------------------------------------------------------------- StartTransmit: DDSIncr = BaseIncr ; Set base frequency GoSub DDS_LoadFreq Transmitting = 1 EXT_TX = 1 LED_TX = 1 : GoSub ShowLeds Return ;------------------------------------------------------------------------------- StopTransmit: DDSIncr = 1 GoSub DDS_LoadFreq Transmitting = 0 EXT_TX = 0 LED_TX = 0 : GoSub ShowLeds Return ;------------------------------------------------------------------------------- DDS_Init: Set DDS_FSYNC ' AD9832 not selected wTemp = $F800 : GoSub DDS_SendWord ' SLEEP = RESET = CLR = 1 wTemp = $B000 : GoSub DDS_SendWord ' SYNC = SELSRC = 1 wTemp = $C000 : GoSub DDS_SendWord ' SLEEP = RESET = 0 Return ;------------------------------------------------------------------------------- DDS_Stop: DDSIncr = 0 ' Loading a freq. of 0 Hz GoSub DDS_LoadFreq ' will silence the output Return ;------------------------------------------------------------------------------- DDS_SendWord: Clear DDS_FSYNC ' pull AD9832 FSYNC pin low (active) SHOut DDS_SDATA, DDS_SCLK, msbfirst, [wTemp.HighByte, wTemp.LowByte] Set DDS_FSYNC ' pull AD9832 FSYNC pin high (inactive) Return ;------------------------------------------------------------------------------- DDS_LoadFreq: If DDSFreg == 0 Then wTemp.Byte0 = DDSIncr.Byte0 wTemp.Byte1 = $30 ' write to defer register, desti = 0 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte1 wTemp.Byte1 = $21 ' write 16 freq. bits, dest = 1 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte2 wTemp.Byte1 = $32 ' write to defer register, destination = 2 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte3 wTemp.Byte1 = $23 ' write 16 freq. bits, destination = 3 GoSub DDS_SendWord wTemp.Byte1 = $50 ' switch to frequency register 0 GoSub DDS_SendWord Else wTemp.Byte0 = DDSIncr.Byte0 wTemp.Byte1 = $34 ' write to defer register, dest = 4 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte1 wTemp.Byte1 = $25 ' write 16 freq. bits, dest = 5 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte2 wTemp.Byte1 = $36 ' write to defer register, dest = 6 GoSub DDS_SendWord wTemp.Byte0 = DDSIncr.Byte3 wTemp.Byte1 = $27 ' write 16 freq. bits, destination = 7 GoSub DDS_SendWord wTemp.Byte1 = $58 ' switch to frequency register 1 GoSub DDS_SendWord EndIf DDSFreg = ~DDSFreg Return ;------------------------------------------------------------------------------- ; Get current mode into LedPattern and display it ;------------------------------------------------------------------------------- ShowMode: LedPattern = LedPattern & $F0 ' Mode is 4 lsb leds bTemp = LookUp Mode,[$08,$04,$02,$01] LedPattern = LedPattern | bTemp ;------------------------------------------------------------------------------- ; Up to 8 leds are kept in LedPattern, shift the contents ; to the HEF4094 latch ;------------------------------------------------------------------------------- ShowLeds: SHOut LED_SDATA, LED_SCLK, msbfirst, [LedPattern] Set LED_STROBE DelayUS 1 Clear LED_STROBE Return ;------------------------------------------------------------------------------- ; Send a single WSPR message. ; Various settings are retreived from EE ; Symbol timing is done using TMR1 to ensure a correct bittime ;------------------------------------------------------------------------------- WSPR_SendMessage: Set WSPR_FirstSymbol ; Flag we're at the start T1start = ERead EE_WSPR_T1TIME ; Get Symbol time from EE BaseIncr = ERead EE_WSPR_PHINC ; Read base frequency GoSub StartTransmit ; Start transmitting msgIndex = 0 ; Index in message Array For i=0 To 162 If (i & $03) == 0 Then ; New message byte WSPR_Data = ERead EE_WSPR_MSG + msgIndex Inc msgIndex ; Point to next byte in array EndIf WSPR_Symbol = WSPR_Data & $03 ; Get low 2 bits WSPR_Data = WSPR_Data >> 2 ; Shift next symbol in B0,B1 GoSub WSPR_SendSymbol Next While T1busy == 1 : Wend ' Wait for last symbol to complete TMR1ON = 0 ; Stop Symbol Timer Return WSPR_SendSymbol: ; Send a single WSPR symbol wTemp = ERead EE_WSPR_OFFSET + WSPR_Symbol ; Get offset DDSIncr = BaseIncr + wTemp ; Add offset to Base Frequency If WSPR_FirstSymbol == 1 Then T1CON = %00000000 ; 1:1 prescaler, int. clock TIMER1 = T1start ; Set timer count T1ticks = 0 ; Reset Ticks TMR1IE = 1 ; Timer1 int on PEIE = 1 ; Pheripheral ints on GIE = 1 ; Global ints on TMR1IF = 0 ; TMR1ON = 1 ; Start Symbol Timer WSPR_FirstSymbol = 0 ; First symbol done Else While T1busy == 1 : Wend ' Wait for previous symbol to complete EndIf ' Cleared in ISR GoSub DDS_LoadFreq ' Now load the new symbol T1busy = 1 ' Symbol in progress Return ;------------------------------------------------------------------------------- CW_SendMessage: SerOut RS232_OUT, RS232_SPD, ["CW sending :"] msgIndex = 0 ; Index in message Array While 1 == 1 CW_char = ERead (TextBase + msgIndex) If CW_char == 0 Then Break ; End of message reached SerOut RS232_OUT, RS232_SPD, [CW_char] GoSub CW_SendChar Inc msgIndex ; Point to next char in text Wend SerOut RS232_OUT, RS232_SPD, [13,10] Return ;------------------------------------------------------------------------------- ; Send a singe CW character kept in CW_char ; Currently uses DelayMS calls so requires a 20MHz XTAL. ; A Better implementation would use Timer1 and the actual ; oscillator frequency (KNOWN EXACTLY because DDS requires this) ;------------------------------------------------------------------------------- CW_SendChar: CW_char = CW_char - $20 ' Control chars not in table CW_char = LRead CW_Table + CW_char ' ASCII -> Morse translation If CW_char == 0 Then GoTo CW_WaitChar While (CW_char & %11111110) <> 0 If CW_char.0 <> 0 Then GoSub CW_SendDah Else GoSub CW_SendDit EndIf GoSub StopTransmit GoSub CW_WaitDit ' Pauze between dah/dits CW_char = CW_char >> 1 Wend GoSub CW_WaitDit ' end of char: 2 extra dits GoSub CW_WaitDit Return CW_SendDah: GoSub StartTransmit GoTo CW_WaitDah CW_SendDit: GoSub StartTransmit GoTo CW_WaitDit CW_WaitChar: GoSub CW_WaitDit ' Wordpause: 3 already done, add 4 CW_WaitDah: GoSub CW_WaitDit ' Dah = 3 * Dit GoSub CW_WaitDit CW_WaitDit: DelayMS DitDelay Return ;---------------------------------------------------------------------------- ; Pseudo random transmission table, taken from G4JNT ; remark: looks "to nice" to be a random table ;---------------------------------------------------------------------------- WSPR_TXTable: LData %10000100000000100100010000010000 ' 19% LData %10000100001000100000110000100010 ' 25% LData %10010101001011000100010010001000 ' 34% LData %10100101101010001101011010101010 ' 50% ;---------------------------------------------------------------------------- ; CW lookuptable, replaces an ASCII char between 0x20 and 0x5F with its ; equivalent in Morse. The bits are processed right to left, a 0 indicates ; a "dot", a 1 a "dash" and are filled with an extra 1 bit to indicate the ; end of the character. ;---------------------------------------------------------------------------- CW_Table: LData %00000000 ' 0x20 ' ' LData %00000010 ; 0x21 '!' Used for MOE (1 dot) LData %00000100 ; 0x22 '"' Used for MOI (2 dots) LData %00001000 ; 0x23 '#' Used for MOS (3 dots) LData %00010000 ; 0x24 '$' Used for MOH (4 dots) LData %00100000 ; 0x25 '%' Used for MO5 (5 dots) LData %01000000 ; 0x26 '&' Used for MO? (6 dots) LData %10000000 ; 0x27 ''' Used for MO? (7 dots) LData %00011111 ; 0x28 '(' Special ---- LData %00111111 ; 0x29 ')' Special ----- LData %01111111 ; 0x2A '*' Special ------ LData %11111111 ; 0x2B '+' Special ------- LData %01110011 ; 0x2C ',' --..-- LData %00000000 ; 0x2D '-' LData %01101010 ; 0x2E '.' .-.-.- LData %00101001 ; 0x2F '/' -..-. LData %00111111, %00111110, %00111100, %00111000 ' 0 1 2 3 LData %00110000, %00100000, %00100001, %00100011 ' 4 5 6 7 LData %00100111, %00101111, %01000111, %01010101 ' 8 9 : ; LData %00000000, %00000000, %00000000, %01001100 ' < = > ? LData %01010110, %00000110, %00010001, %00010101 ' @ A B C LData %00001001, %00000010, %00010100, %00001011 ' D E F G LData %00010000, %00000100, %00011110, %00001101 ' H I J K LData %00010010, %00000111, %00000101, %00001111 ' L M N O LData %00010110, %00011011, %00001010, %00001000 ' P Q R S LData %00000011, %00001100, %00011000, %00001110 ' T U V W LData %00011001, %00011101, %00010011 ' X Y Z LData %10101010 ' 0x5B '[' Special .-.-.-. LData %00000000 ' 0x5C '\' LData %11010101 ' 0x5D ']' Special -.-.-.- LData %00000000 ' 0x5E '^' LData %00000000 ' 0x5F unused End