;
; DDS_WSPR - Uses AD9851 to tranmit WSPR code on 20% transmit cycle
; - Multi-band 160 thru 6 meters
; - Transmit band scanning
;
;
; Gene Marcus W3PM GM4YRE
; 16 February, 2009
;
; Clock Frequency: 4 MHz
;
;
;
; PIC16F628A
; __________
; Not used ----RA2 |1 18| RA1---------Scan LED
; Not used ----RA3 |2 17| RA0---------Transmit
; PB_1-Bandswitch-RA4 |3 16| OSC1--------XTAL
; +5V-----------!MCLR |4 15| OSC2--------XTAL
; Ground----------Vss |5 14| VDD---------+5 V
; 1 PPS-----------RB0 |6 13| RB7---------DDS_LOAD
; Band BCD0-------RB1 |7 12| RB6---------Band BCD3
; DDS_CLK---------RB2 |8 11| RB5---------Band BCD2
; DDS_DATA--------RB3 |9 10| RB4---------Band BCD1
; ----------
;
;=====================================================================
processor pic16f628a
include "p16f628a.inc"
__config _CP_OFF & _LVP_OFF & _BODEN_OFF & _MCLRE_ON & _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON
movlw 0x07
movwf CMCON ; Turn off comparator
list b=4,n=70
;=====================================================================
; Manifest Constants
;=====================================================================
DDS_clk equ 0x02 ; AD9850/AD9851 write clock
DDS_dat equ 0x03 ; AD9850/AD9851 serial data input
DDS_load equ 0x07 ; Update pin on AD9850/AD9851
pb_1 equ 0x04 ; Change band/band scan
TX equ 0x00 ; Transmit on/off
BCD_0 equ 0x01 ; BCD outputs used to control
BCD_1 equ 0x04 ; CD4028 BCD to decimal switch
BCD_2 equ 0x05 ; or directly for amplifier/filter
BCD_3 equ 0x06 ; selection and/or LED band indicator
scan_LED equ 0x01 ; Used to indicate scan enabled
;=====================================================================
; File register use
;=====================================================================
cblock H'20'
basefreq_0 ; Base frequency MSB
basefreq_1 ;
basefreq_2 ;
basefreq_3 ; Base frequency LSB
offset ; Offset to add to base frequency
offset_0 ; WSPR offset for symbol o
offset_1 ; WSPR offset for symbol 1
offset_2 ; WSPR offset for symbol 2
offset_3 ; WSPR offset for symbol 3
DDSword_0 ; (base freq + offset) MSB
DDSword_1 ;
DDSword_2 ;
DDSword_3 ; (base freq + offset) LSB
DDSword_4 ; x6 multiply for AD9851
temp
temp1
temp2
temp3
bit_count
byte2send
timer1
timer2
Inputs
counter
w_temp
status_temp
sec_count
min_count
symbolcount
symboltimer
sendcode
pad
two_sec
band
led_stat
scan_flag
endc
goto start
;=====================================================================
; Subroutines
;=====================================================================
; Interrupt service routine - triggered by 1 PPS GPS on pin 6 (RB0)
IRQSVC
org H'04' ; Interrupt 1PPS starts here
movwf w_temp ; Save off the W register
swapf STATUS,W ; And the STATUS (use swapf
movwf status_temp ; so as not to change STATUS)
decfsz sec_count ; End of even 2 minute iterval?
goto main3 ; No, end interrupt service routine
movlw D'120' ; Yes, reset 2 minute counter
movwf sec_count ;
clrf scan_flag ; start scan transmit
decfsz min_count ; End of 10 minute interval?
goto main3 ; No, end interrupt service routine
movlw D'5' ; Yes, reset 10 minute counter
movwf min_count ;
clrf sendcode ; Flag to transmit
main3
bcf INTCON,T0IF ; Clear the old interrupt
bcf INTCON,INTF
swapf status_temp,W ; Restore the status
movwf STATUS ; register
swapf w_temp,F ; Restore W without disturbing
swapf w_temp,W ; the STATUS register
retfie
;
;
;_________________________________________________________________________________
;
; STATION CALLSIGN, POWER LEVEL, AND GRIDSQURE IS CONTAINED IN THE FOLLOWING TABLE
;
; Symbol table contains the station's callsign, powerlevel, and gridsquare
;
; The follwing data is for W3PM, 10 dBm, EM64
;
; Note: End of table flag is D'4'
;
;_________________________________________________________________________________
;
; org H'A0'
Table ; WSPR symbol data starts here
addwf PCL,F
retlw D'3'
retlw D'1'
retlw D'0'
retlw D'0'
retlw D'2'
retlw D'0'
retlw D'0'
retlw D'2'
retlw D'1'
retlw D'0'
retlw D'0'
retlw D'0'
retlw D'3'
retlw D'1'
retlw D'1'
retlw D'0'
retlw D'2'
retlw D'2'
retlw D'3'
retlw D'0'
retlw D'0'
retlw D'3'
retlw D'0'
retlw D'3'
retlw D'3'
retlw D'3'
retlw D'1'
retlw D'2'
retlw D'0'
retlw D'0'
retlw D'0'
retlw D'2'
retlw D'0'
retlw D'2'
retlw D'3'
retlw D'0'
retlw D'0'
retlw D'1'
retlw D'2'
retlw D'1'
retlw D'2'
retlw D'0'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'0'
retlw D'3'
retlw D'0'
retlw D'3'
retlw D'3'
retlw D'2'
retlw D'2'
retlw D'3'
retlw D'1'
retlw D'0'
retlw D'3'
retlw D'2'
retlw D'2'
retlw D'0'
retlw D'3'
retlw D'1'
retlw D'2'
retlw D'1'
retlw D'0'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'1'
retlw D'1'
retlw D'0'
retlw D'1'
retlw D'2'
retlw D'1'
retlw D'2'
retlw D'1'
retlw D'0'
retlw D'1'
retlw D'2'
retlw D'2'
retlw D'1'
retlw D'0'
retlw D'0'
retlw D'3'
retlw D'2'
retlw D'1'
retlw D'1'
retlw D'2'
retlw D'0'
retlw D'0'
retlw D'1'
retlw D'3'
retlw D'0'
retlw D'3'
retlw D'0'
retlw D'1'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'1'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'0'
retlw D'1'
retlw D'0'
retlw D'0'
retlw D'3'
retlw D'2'
retlw D'0'
retlw D'1'
retlw D'3'
retlw D'3'
retlw D'0'
retlw D'1'
retlw D'3'
retlw D'0'
retlw D'2'
retlw D'1'
retlw D'3'
retlw D'2'
retlw D'1'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'3'
retlw D'3'
retlw D'1'
retlw D'2'
retlw D'0'
retlw D'0'
retlw D'0'
retlw D'0'
retlw D'3'
retlw D'2'
retlw D'1'
retlw D'0'
retlw D'0'
retlw D'1'
retlw D'1'
retlw D'0'
retlw D'2'
retlw D'0'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'3'
retlw D'3'
retlw D'0'
retlw D'3'
retlw D'0'
retlw D'1'
retlw D'3'
retlw D'2'
retlw D'2'
retlw D'0'
retlw D'3'
retlw D'3'
retlw D'2'
retlw D'2'
retlw D'2'
retlw D'4' ; end_of_table flag
;
; ****************************************************************************
; * band table. *
; * *
; * Example: Fout = 14.0971 MHz *
; * Fclock = 180 MHz *
; * basefreq = (14.0971*10^6) * (2^32) / (180*10^6) = 336369908 *
; * 336369908 = 14 0c 98 f4 hex *
; * *
; * Each entry is four instructions long, with each group of four literals *
; * representing the frequency as a 32 bit integer. *
; * *
; ****************************************************************************
;
band_table
addwf PCL,f;_________________
retlw 0x47 ; 6 meters MSB 0
retlw 0x87 ;
retlw 0xab ; band 10
retlw 0x2a ;____________LSB__
retlw 0x28 ; 10 meters MSB 4
retlw 0x00 ;
retlw 0x66 ; band 9
retlw 0x87 ;_________________
retlw 0x23 ; 12 meters MSB 8
retlw 0x73 ;
retlw 0x50 ; band 8
retlw 0xe8 ;_________________
retlw 0x1e ; 15 meters MSB 12
retlw 0x00 ;
retlw 0xdb ; band 7
retlw 0x09 ;_________________
retlw 0x19 ; 17 meters MSB 16
retlw 0xc0 ;
retlw 0x3a ; band 6
retlw 0xd6 ;_________________
retlw 0x14 ; 20 meters MSB 20
retlw 0x0c ;
retlw 0x98 ; band 5
retlw 0xf4 ;_________________
retlw 0x0e ; 30 meters MSB 24
retlw 0x6b ;
retlw 0xef ; band 4
retlw 0x24 ;_________________
retlw 0x0a ; 40 meters MSB 28
retlw 0x03 ;
retlw 0x38 ; band 3
retlw 0xe1 ;_________________
retlw 0x05 ; 80 meters MSB 32
retlw 0x1c ;
retlw 0x92 ; band 2
retlw 0x66 ;_________________
retlw 0x02 ; 160 meters MSB 40
retlw 0x9d ;
retlw 0x3b ; band 1
retlw 0x56 ;_________________
;
; *****************************************************************************
; * main *
; * *
; * Purpose: This routine retrives WSPR symbols, determines symbol frequency *
; * offset and symbol transmit delay timing. *
; * *
; * Input: Symbol data from symbol table *
; * *
; * Output: Subroutine calls to calc_DDSword and send_DDS-word *
; * *
; *****************************************************************************
;
main ; Symbol send code starts here
movlw D'0'
movwf symbolcount
bsf PORTA,TX ; Turn on transmitter
call get_band
movlw D'30' ; 2 sec delay before data start
movwf two_sec ;
tdelay ;
btfss INTCON,T0IF ; Did timer overflow?
goto tdelay ; No, hang around some more
bcf INTCON,T0IF ; reset overflow flag
decfsz two_sec,F ; Count down
goto tdelay ; Not time yet
symbol_loop
movlw D'21'
movwf symboltimer
movfw symbolcount
call Table
movwf temp
incf symbolcount
sublw D'4' ; Test for end_of_table flag
bz stop ; Yes, stop
movfw temp ; No, reload w from temp and continue
sublw D'0' ; Test for symbol 0
bz zero ; Yes, goto zero
movfw temp ; No, reload w from temp and continue
sublw D'1' ; Test for symbol 1
bz one ; Yes, goto one
movfw temp ; No, reload w from temp and continue
sublw D'2' ; Test for symbol 2
bz two ; Yes, goto two
; No, it must be symbol 3
movfw offset_3 ; Load the offset for symbol 3
movwf offset ; into offset
call calc_DDSword ; Calculate the word to send to the DDS
call send_dds_word ; Transmit symbol
goto delay ; Wait for 680 mSec
zero
movfw offset_0 ; Load the offset for symbol 0
movwf offset ; into offset
call calc_DDSword ; Calculate the word to send to the DDS
call send_dds_word ; Transmit symbol
goto delay ; Wait for 680 mSec
one
movfw offset_1 ; Load the offset for symbol 1
movwf offset ; into offset
call calc_DDSword ; Calculate the word to send to the DDS
call send_dds_word ; Transmit symbol
goto delay ; Wait for 680 mSec
two
movfw offset_2 ; Load the offset for symbol 2
movwf offset ; into offset
call calc_DDSword ; Calculate the word to send to the DDS
call send_dds_word ; Transmit symbol
delay
btfss INTCON,T0IF ; Did timer overflow?
goto delay ; No, hang around some more
movlw D'39' ;
movwf pad ; Calibrate symbol timer
timepad decfsz pad,F ; to 680 mSec
goto timepad ;
bcf INTCON,T0IF ; Reset overflow flag
movlw D'130' ; Timer will count
movwf TMR0 ; 127 (256-129) counts
decfsz symboltimer,F ; Count down
goto delay ; Not time yet
goto symbol_loop
stop
movlw D'1'
movwf sendcode ; End of transmit
movwf scan_flag ; End of transmit for scan function
bcf PORTA,TX ; Turn off tranmitter
return
;
; *****************************************************************************
; * calc_DDSword *
; * *
; * Purpose: This routine calculates the DDSword control word to be sent *
; * to the DDS. *
; * *
; * Input: basefreq_3 ... basefreq_0, offset *
; * *
; * Output: DDSword_3 ... DDSword_0 *
; * *
; *****************************************************************************
;
calc_DDSword
movf basefreq_0,w ; LSD freq byte operand
addwf offset,w ; Add offset to LSDfreq byte operand
movwf DDSword_0 ; Store result
movf basefreq_1,w ; Pick up next operand
movwf temp3 ; Temporarily store
btfss STATUS,C ; Was there a carry?
goto a01 ; No, skip to a01
movlw 0x01 ; Yes, add in carry
addwf temp3,w ; Add to temp
a01 movwf DDSword_1 ; Store result
movf basefreq_2,w ; Pick up next operand
movwf temp3 ; Temporarily store
btfss STATUS,C ; Was there a carry?
goto a02 ; No, skip to a02
movlw 0x01 ; Yes, add in carry
addwf temp3,W ; Add to temp
a02 movwf DDSword_2 ; Store result
movf basefreq_3,w ; Pick MSB freq byte operand
movwf temp3 ; Temporarily store
btfss STATUS,C ; Was there a carry?
goto a03 ; No, skip to a03
movlw 0x01 ; Yes, add in carry
addwf temp3,w ; Add to temp
a03 movwf DDSword_3 ; Store result
return
;
; *****************************************************************************
; * send_dds-word *
; * *
; * Purpose: This routine sends the DDSword control word to the DDS *
; * using a serial data transfer. *
; * *
; * Input: DDSword_4 ... DDSword_0 *
; * *
; * Output: The DDS chip register is updated. *
; * *
; *****************************************************************************
;
send_dds_word
movlw DDSword_0 ; Point FSR at Least Significant Byte
movwf FSR ;
next_byte
movf INDF,w ;
movwf byte2send ;
movlw 0x08 ; Set counter to 8
movwf bit_count ;
next_bit
rrf byte2send,f ; Test if next bit is 1 or 0
btfss STATUS,C ; Was it zero?
goto send0 ; Yes, send zero
bsf PORTB,DDS_dat ; No, send one
bsf PORTB,DDS_clk ; Toggle write clock
bcf PORTB,DDS_clk ;
goto break ;
send0
bcf PORTB,DDS_dat ; Send zero
bsf PORTB,DDS_clk ; Toggle write clock
bcf PORTB,DDS_clk ;
break
decfsz bit_count,f ; Has the whole byte been sent?
goto next_bit ; No, keep going.
incf FSR,f ; Start the next byte unless finished
movlw DDSword_4+1 ; Next byte (past the end)
subwf FSR,w ;
btfss STATUS,C ;
goto next_byte ;
bsf PORTB,DDS_load ; Send load signal to the AD9850/DDSword
bcf PORTB,DDS_load ;
return
;
;
; *****************************************************************************
; * get_band *
; * *
; * Purpose: This routine reads the frequency value of a band table entry *
; * pointed to by band and returns it in freq_3...freq_0. *
; * *
; * Input: band must contain the index of the desired band entry * 4 *
; * (with the entries numbered from zero). *
; * *
; * Output: The band frequency in freq. *
; * *
; *****************************************************************************
;
get_band
movf band,w ; Get the index of the high byte
call band_table ; Get the value into W
movwf basefreq_3 ; Save it in basefreq_3
incf band,f ; Increment index to next byte
movf band,w ; Get the index of the next byte
call band_table ; Get the value into W
movwf basefreq_2 ; Save it in basefreq_2
incf band,f ; Increment index to the next byte
movf band,w ; Get the index to the next byte
call band_table ; Get the value into W
movwf basefreq_1 ; Save it in basefreq_1
incf band,f ; Increment index to the low byte
movf band,w ; Get the index to the low byte
call band_table ; Get the value into W
movwf basefreq_0 ; Save it in basefreq_0
movlw 0x03 ; Get a constant three
subwf band,f ; Restore original value of band
return ; Return to the caller
;
; *****************************************************************************
; * LED_status *
; * *
; * Purpose: Reads the variable led_stat and outputs BCD word to ports. *
; * BCD ports used to indicate band number selected. *
; * *
; * Input: led_stat *
; * *
; * *
; * Output: Ports RB1,RB4,RB5, and RB6. *
; * *
; *****************************************************************************
;
LED_status
btfss led_stat,0
goto $+3
bsf PORTB,BCD_0
goto $+2
bcf PORTB,BCD_0
btfss led_stat,1
goto $+3
bsf PORTB,BCD_1
goto $+2
bcf PORTB,BCD_1
btfss led_stat,2
goto $+3
bsf PORTB,BCD_2
goto $+2
bcf PORTB,BCD_2
btfss led_stat,3
goto $+3
bsf PORTB,BCD_3
goto $+2
bcf PORTB,BCD_3
return
;______________________________________________________________________
start
;---------------------------------------------------------------------
; Set up timer
;---------------------------------------------------------------------
errorlevel -302
banksel INTCON
bsf INTCON,GIE
bcf INTCON,T0IE ; Mask timer interrupt
bsf INTCON,INTE
banksel OPTION_REG
bsf OPTION_REG,INTEDG
bcf OPTION_REG,T0CS ; Select timer
bcf OPTION_REG,PSA ; Prescaler to timer
bsf OPTION_REG,PS2 ; \
bsf OPTION_REG,PS1 ; >- 1:256 prescale
bsf OPTION_REG,PS0 ; /
;---------------------------------------------------------------------
; Set up I/O
;---------------------------------------------------------------------
banksel TRISA
movlw B'11011100' ; Tristate PORTA (all Inputs except RA0,RA1)
movwf TRISA ;
movlw B'00000001'
movwf TRISB ; Set port B to all outputs except RB0
banksel PORTA
clrf PORTA
clrf PORTB
;---------------------------------------------------------------------
; Initialize memory
;---------------------------------------------------------------------
; Set default basefreq to 10.1042 MHz
movlw D'24' ; MSB for band (refer to band table)
movwf band
movlw D'4' ; band number from band table
movwf led_stat ; set up LED (BCD) status
call LED_status
; Set DDSword_4 to turn on AD9851 6x clock multiplier
movlw 0x01 ; Turn on 6x clock multiplier (AD9851)
movwf DDSword_4 ; Last byte to be sent
; Mult answer is in bytes _3 .. _0
;_________________________________________________________________________________
;
; offset - N*12000*2^32 / 8192*Fclock
; Example: (for symbol 2)
; Fclock = 180 MHz
; offset = 2*12000*2^32 / 8192*(180*10^6) = 69.905
; 69.905 ~ 70 = 46 Hex
;__________________________________________________________________________________
; Load WSPR offsets
movlw 0x00 ; 0.00 Hz
movwf offset_0
movlw 0x23 ; 1.46 Hz
movwf offset_1
movlw 0x46 ; 2.93 Hz
movwf offset_2
movlw 0x69 ; 4.39 Hz
movwf offset_3
movlw D'120' ; for 2 minute intervals
movwf sec_count
movlw D'5'
movwf min_count ; for 10 minute intervals
clrf sendcode
clrf scan_flag
btfsc PORTA,pb_1 ; PB1 down?
goto WaitForInt ; No, skip to WaitForInt
TestPB1up
btfss PORTA,pb_1 ; PB1 up?
goto TestPB1up ; No, wait for release
goto start_scan
WaitForInt
movfw sendcode ; Will go to transmit on the
addlw D'0' ; default frequency upon reset then
bz transmit ; transmit on a 10 min. interval.
; Note: pushbutton is only active when not transmitting
btfsc PORTA,pb_1 ; PB1 down?
goto WaitForInt ; No, skip to WaitForInt
TestPB1u
btfss PORTA,pb_1 ; PB1 up?
goto TestPB1u ; No, wait for release
PB_yes1
bcf STATUS,C
movlw 0x04 ; get 4 bytes to subtract
subwf band,f ; Move down to MSB in band list
incf led_stat ; Increment band LED
btfss STATUS,C ; Off the bottom?
goto reset_var ; Yes, reset variables
call LED_status ; Set up BCD outputs
movlw D'1'
movwf sendcode ; Ensure timing integity
goto WaitForInt
start_scan
movlw D'1'
movwf scan_flag ; Set scan flag
bsf PORTA,scan_LED ; Turn on scan LED
loop
movfw scan_flag ; Interrupt routine will
addlw D'0' ; clear scan_flag at 120 sec interval
bz transmit2 ; Time to transmit?
goto loop ; No, wait for even minute
transmit
call main ; Start transmit routine
goto WaitForInt ; Start over and wait for interrupts
reset_var
movlw D'40' ; Set counter to end of band table
movwf band
movlw D'0' ; Initialize LED (BCD) status
movwf led_stat
goto PB_yes1
transmit2
bcf STATUS,C
movlw 0x04 ; Get 4 bytes to subtract
subwf band,f ; Move down to MSB in band list
incf led_stat ; Increment band LED
btfss STATUS,C ; Off the bottom?
goto reset_var2 ; Yes, reset variables
call LED_status ; Set up BCD outputs
call main ; Start transmit routine
goto loop ; Start over and wait for interrupt
reset_var2
movlw D'40' ; Set counter to end of band table
movwf band
movlw D'0' ; Initialize LED (BCD) status
movwf led_stat
goto transmit2
end