;****************************************************************************** ; Source code used for Direct Digital Synthesis VFO ; ; Target Controller - PIC16F84 ; __________ ; ENCODER SWITCH--RA2 |1 18| RA1---------ENCODER A ; PB SWITCH-------RA3 |2 17| RA0---------ENCODER B ; +5V-------------RA4 |3 16| OSC1--------XTAL ; Ground--------!MCLR |4 15| OSC2--------XTAL ; Ground----------Vss |5 14| VDD---------+5 V ; DDS LOAD--------RB0 |6 13| RB7---------DDS DATA/LCD 14 ; LCD_rs----------RB1 |7 12| RB6---------LCD 13 ; LCD_rw----------RB2 |8 11| RB5---------DDS CLOCK/LCD 12 ; LCD_e-----------RB3 |9 10| RB4---------LCD 11 ; ---------- ; ; Assembler - Parallax SPASM v4.7 ; Author - Curtis W. Preuss - WB2V ; ; Modification History ; Initial Version 8/19/98 ; ; Converted to MPASM by Bruce Stough, AA0ED inf 12/98 ; ;****************************************************************************** ; Description: ; This is the control program for a DDS VFO built with an AD9850 DDS chip, a ; shaft encoder, a push button switch and an Liquid crystal display. ; ; Features: ; VARIABLE RATE TUNING based on the speed at which the encoder is turned. The ; encoder also has a built in switch which will change the step size from 1Hz ; to 1kHz if the encoder shaft is pressed down while turning. ; ; BAND MEMORIES an external push button switch allows the frequency to be ; cycled around the HF ham bands. ; ; CALIBRATE MODE is entered if the external push button is pressed during ; power on. The display is set to 10,000.000 CAL. If the push button is held ; pressed then turning the shaft encoder will increase or decrease the value ; "osc" used to calculated the DDS control word. A fast adjustment speed is ; available by pressing the encoder shaft down while turning. ; To exit calibrate mode, release the external push button and turn the shaft ; encoder one more time. The calibrated value of "osc" will then be stored in ; EEprom memory. ; ;****************************************************************************** ; Device type and options ; processor PIC16F84 radix dec ; ;******************************************************************************* ; Configuration fuse information: ; __config 0x3FFD ; Code protect OFF, Power-up timer ON, WDT ENABLED, XT Oscillator ; ;******************************************************************************* ; PortA equ 0x05 PortB equ 0x06 TRISA equ 0x05 TRISB equ 0x06 EEdata equ 0x08 EEadr equ 0x09 WREN equ 0x02 WR equ 0x01 RD equ 0x00 ; ;******************************************************************************* ; ID location information: ; (MPASM warns about DW here, don't worry) ; ORG 0x2000 DATA 0x007F DATA 0x007F DATA 0x007F DATA 0x007F ; ;******************************************************************************* ; User EEPROM data: ; ; The fourth byte "23h" is the integer part of ; (2**32 / oscillator_freq_in_Hertz). ; ; Bytes 3,2,1 "CAh, 91h, 70h" are the fractional_part_of ; (2**32 / oscillator_freq_in_Hertz) times 2**24. ; ORG 0x2100 DATA 0x70,0x91,0xCA,0x23 ; Default oscillator frequency DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; ;******************************************************************************* ; RAM page independent file registers: ; INDF EQU 0x00 PCL EQU 0x02 STATUS EQU 0x03 FSR EQU 0x04 PCLATH EQU 0x0A INTCON EQU 0x0B ; ;******************************************************************************* ; ; Bit numbers for the STATUS file register: ; B_RP0 EQU 5 B_NTO EQU 4 B_NPD EQU 3 B_Z EQU 2 B_DC EQU 1 B_C EQU 0 ; ;******************************************************************************* ; Bit numbers for the INTCON file register: ; B_GIE EQU 7 B_EEIE EQU 6 B_T0IE EQU 5 B_INTE EQU 4 B_RBIE EQU 3 B_T0IF EQU 2 B_INTF EQU 1 B_RBIF EQU 0 ; ;******************************************************************************* ; Assign names to IO pins ; ; B register bits: ; DDS_load equ 0x00 ; Update pin on AD9850 LCD_rs equ 0x01 ; 0=instruction, 1=data LCD_rw equ 0x02 ; 0=write, 1=read LCD_e equ 0x03 ; 0=disable, 1=enable DDS_clk equ 0x05 ; AD9850 write clock DDS_dat equ 0x07 ; AD9850 serial data input ; ; A register bits: ; PbSwitch equ 0x03 ; Calibrate Push Button, (active low) ; ;****************************************************************************** ;Allocate variables in general purpose register space ; CBLOCK 0x0c freq ; Display frequency in hex (4 bytes) freq_2, freq_3, freq_4 bcd ; Display frequency in BCD (5 bytes) bcd_2, bcd_3, bcd_4, bcd_5 AD9850 ; AD9850 control word (5 bytes) AD9850_2, AD9850_3, AD9850_4, AD9850_5 fstep ; Frequency inc/dec (4 bytes) fstep_2, fset_3, fstep_4 bcdCount ; Used in BIN2BCD routine bcdTemp ; " multCount ; Used in Calc_DDS_Word routine bitCount ; " byte2send ; osc ; (4 bytes) osc_2, osc_3, osc_4 osc_temp ; Oscillator frequency (4 bytes) osc_temp_2, osc_temp_3, osc_temp_4 LCD_char ; Character being sent to the LCD LCD_read ; Character read from the LCD timer1 ; Used in delay routines timer2 ; " ren_timer ; For variable rate tuning (2 bytes) ren_timer_2 ren_new ; New value of encoder pins A and B ren_old ; Old value of encoder pins A and B ren_read ; Encoder pins A and B and switch pin Last_dir ; Indicates last direction of encoder Next_dir ; Indicates expected direction W_copy ; Used by interrupt handler S_copy ; " count ; loop counter (gets reused) band ; Used to index a table of frequencies ENDC ; ;***************************************************************************** ;The 16F84 resets to 000h, Interrupt vector is at 004h ; ORG 0x0000 reset_entry goto start ; Jump to the beginning of the program ; ORG 0x0004 ;****************************************************************************** ; Interrupt handler saves copies of the W and STATUS registers on entry ; and restores the original register values on exit. ; isr_entry movwf W_copy ; Make a copy of w. movf STATUS,w ; Make a copy of status. movwf S_copy ; Location = 0x0006 ; ;interupt stuff goes here . ; movf S_copy,w ; Restore STATUS movwf STATUS ; swapf W_copy,f ; Restore w without changing STATUS swapf W_copy,w ; retfie ; ;******************************************************************************* start clrf INTCON ; No interrupts for now bsf STATUS,B_RP0 ; Switch to bank 1 bsf 0x01,7 ; Disable weak pullups movlw 0xFF ; Tristate port A movwf TRISA ; clrf TRISB ; Set port B to all outputs bcf STATUS,B_RP0 ; Switch back to bank 0 ;****************************************************************************** ; Power on initialization of Liquid Crystal Display. ; The LCD controller chip must be equivalent to an Hitachi 44780. ; The LCD is assumed to be a 16 X 1 display. ; call Wait128 ; Wait for LCD to power up movlw b'00110000' ; LCD init instruction movwf PortB ; Send to LCD bsf PortB,LCD_e ; Pulse the LCD E line call Wait128 ; Wait a bit bcf PortB,LCD_e ; Clear E movlw b'00110000' ; LCD init instruction movwf PortB ; Send to LCD bsf PortB,LCD_e ; Pulse E call Wait64 ; bcf PortB,LCD_e ; movlw b'00110000' ; LCD init instruction movwf PortB ; Send to LCD bsf PortB,LCD_e ; Pulse E call Wait64 ; bcf PortB,LCD_e ; movlw b'00100000' ; 4-bit mode instruction movwf PortB ; Send to LCD bsf PortB,LCD_e ; Pulse E call Wait32 ; bcf PortB,LCD_e ; movlw 0x28 ; 2 line display movwf LCD_char ; call Cmnd2LCD ; movlw 0x08 ; Display off movwf LCD_char ; call Cmnd2LCD ; movlw 0x01 ; Clear and reset cursor movwf LCD_char ; call Cmnd2LCD ; movlw 0x06 ; Set cursor move direction movwf LCD_char ; call Cmnd2LCD ; movlw 0x0C ; Cursor blink off, display on movwf LCD_char ; call Cmnd2LCD ; ; ;Enter Calibrate Mode if push button is pressed while turning the power on. ; btfsc PortA,PbSwitch ; Is the switch pressed? goto readEEocs ; No, get clock freq from EEprom call Calibrate ; Yes, calibrate readEEocs clrf EEadr ; Reset the EEprom read address call ReadEE ; Read EEprom movf EEdata,w ; Get the first osc byte movwf osc ; Save osc frequency call ReadEE ; Get next byte movf EEdata,w ; movwf osc+1 ; Save it call ReadEE ; Get the third byte movf EEdata,w ; movwf osc+2 ; Save it call ReadEE ; Get the fourth byte movf EEdata,w ; movwf osc+3 ; Save it ; ; Set the power on frequency to 14.025. ; movlw 0x28 ; First byte movwf freq ; Save it movlw 0x01 ; Second byte movwf freq+1 ; Save it movlw 0xD6 ; Third byte movwf freq+2 ; Save it movlw 0x00 ; Fourth byte movwf freq+3 ; Save it ; ; Display the power on frequency. ; call BIN2BCD ; Convert it to BCD call ShowFreq ; Display it ; ; Send power on frequency to the DDS chip. ; call Calc_DDS_Word ; Convert to delta value call Send_DDS_Word ; Put 9850 in serial mode call Send_DDS_Word ; Sends the power on frequency ; ; Get the power on encoder value. ; movf PortA,w ; Read port A movwf ren_read ; Save it in ren_read movlw 0x03 ; Get encoder mask andwf ren_read,w ; Get encoder bits movwf ren_old ; Save in ren_old ; ; Initialize variables. ; clrf ren_timer+1 ; movlw 0x40 ; movwf ren_timer ; clrf Last_dir ; clrf band ; ; ;****************************************************************************** ; This is the Main Program Loop ; The program's main loop "MAIN" calls PollEncoder which continuously polls the ; rotary shaft encoder. When the shaft encoder has changed then the direction ; it moved is determined and stored in "Last_dir" the subroutine then returns to ; to MAIN. ; ; If the push button swith was not pressed then the variable "fstep" is ; calculated based on the delay between shaft encoder changes, ("ren_timer" ; contains the delay value determined by the PollEncoder subroutine). ; The variable "fstep" is added or subtracted from the current VFO frequency ; stored in "freq". The contents of "freq" are then converted to a BCD ; number in subroutine "BIN2BCD". The subroutine "ShowFreq" is then called to ; display the result on the Liquid Crystal Display. Next, the subroutine ; "Calc_DDS_Word" is used to calculate the DDS frequency control word from the ; values in "freq" and "osc". The result is stored in "AD9850". This data ; is transferred to the AD9850 DDS chip by calling the subroutine "Send_DDS_Word". ; ; If the push button is pressed while turning the encoder then "freq" is loaded ; with a constant stored in "BandTable". The variable "band" is used as an index ; into the table. "Band" is incremented or decremented based on the ; encoder direction. ; Main call PollEncoder ; Check for knob movement btfss ren_read,3 ; Change band? goto changeBand ; Yes, change bank ; ; Determine step size to use (1 Hz or 1 kHz). ; clrf fstep+3 ; clrf fstep+2 ; clrf fstep+1 ; movlw 0x01 ; 1 Hz step if encoder switch is false movwf fstep ; btfsc ren_read,2 ; goto goStep ; Apply movlw 0xE8 ; 1 kHz step if encoder switch is true movwf fstep ; movlw 0x03 ; movwf fstep+1 ; goto goStep ; Apply ; ; Adjust the tuning step based on ren_timer. ; bumpstep bcf STATUS,B_C ; rlf fstep,f ; rlf fstep+1,f ; rlf fstep+2,f ; rlf fstep+3,f ; goStep rlf ren_timer,f ; rlf ren_timer+1,f ; btfss STATUS,B_C ; goto bumpstep ; btfsc Last_dir,1 ; goto up ; down call sub_step ; goto write ; up call add_step ; goto write ; changeBand btfsc Last_dir,1 ; goto bandUp ; movlw 0x04 ; subwf band,f ; movlw 0xDF ; addwf band,w ; btfss STATUS,B_C ; goto valid ; movlw 0x20 ; movwf band ; valid call GetBand ; goto write ; bandUp movlw 0x04 ; Table entries are 4 bytes apart addwf 0x39,f ; movlw 0xDF ; addwf band,w ; btfsc STATUS,B_C ; clrf band ; call GetBand ; write call BIN2BCD ; call ShowFreq ; call Calc_DDS_Word ; call Send_DDS_Word ; goto Main ; continue polling the encoder ; ;******************************************************************************* ; BandTable addwf PCL,f ; retlw 0x00 ; 160 meters retlw 0x1B ; retlw 0x77 ; retlw 0x40 ; retlw 0x00 ; 80 meters retlw 0x35 ; retlw 0x67 ; retlw 0xE0 ; retlw 0x00 ; 40 meters retlw 0x6A ; retlw 0xCF ; retlw 0xC0 ; retlw 0x00 ; 30 meters retlw 0x9A ; retlw 0x1D ; retlw 0x20 ; retlw 0x00 ; 20 meters retlw 0xD5 ; retlw 0x9F ; retlw 0x80 ; retlw 0x01 ; 17 meters retlw 0x13 ; retlw 0xB2 ; retlw 0x20 ; retlw 0x01 ; 15 meters retlw 0x40 ; retlw 0x6F ; retlw 0x40 ; retlw 0x01 ; 12 meters retlw 0x7B ; retlw 0xCA ; retlw 0x90 ; retlw 0x01 ; 10 meters retlw 0xAB ; retlw 0x3F ; retlw 0x00 ; ; ;******************************************************************************* ; GetBand movf band,w ; call BandTable ; movwf freq+3 ; incf band,f ; movf band,w ; call BandTable ; movwf freq+2 ; incf band,f ; movf band,w ; call BandTable ; movwf freq+1 ; incf band,f ; movf band,w ; call BandTable ; movwf freq ; movlw 0x03 ; subwf band,f ; Restore original value of band return ; ; ;******************************************************************************* ; add_step movf fstep,w ; addwf freq,f ; btfss STATUS,B_C ; goto add1 ; incfsz freq+1,f ; goto add1 ; incfsz freq+2,f ; goto add1 ; incf freq+3,f ; add1 movf fstep+1,w ; addwf freq+1,f ; btfss STATUS,B_C ; goto add2 ; incfsz freq+2,f ; goto add2 ; incf freq+3,f ; add2 movf fstep+2,w ; addwf freq+2,f ; btfss STATUS,B_C ; goto add3 ; incf freq+3,f ; add3 movf fstep+3,w ; addwf freq+3,f ; ; ; Peg the maximum VFO output frequency to ; a predetermined value, (1C9C380 is 30 MHz) ; movlw 0xFE ; addwf freq+3,w ; btfsc STATUS,B_C ; goto setMax ; movlw 0x01 ; subwf freq+3,w ; btfss STATUS,B_C ; goto exit1 ; movlw 0x36 ; addwf freq+2,w ; btfsc STATUS,B_C ; goto setMax ; movlw 0xC9 ; subwf freq+2,w ; btfss STATUS,B_C ; goto exit1 ; movlw 0x3C ; addwf freq+1,w ; btfsc STATUS,B_C ; goto setMax ; movlw 0xC3 ; subwf freq+1,w ; btfss STATUS,B_C ; goto exit1 ; movlw 0x80 ; subwf freq,w ; btfss STATUS,B_C ; goto exit1 ; setMax movlw 0x80 ; movwf freq ; movlw 0xC3 ; movwf freq+1 ; movlw 0xC9 ; movwf freq+2 ; movlw 0x01 ; movwf freq+3 ; exit1 return ; ; ;******************************************************************************* ; sub_step comf fstep,f ; Subtraction of fstep from comf fstep+1,f ; freq is down by adding the comf fstep+2,f ; twos compliment of fstepto comf fstep+3,f ; freq. incfsz fstep,f ; goto compDone ; incfsz fstep+1,f ; goto compDone ; incfsz fstep+2,f ; goto compDone ; incf fstep+3,f ; compDone call add_step ; ; ; Peg the minimum VFO output frequency to ; a predetermined value, (3E8 is 1 kHz). ; btfsc 0x0F,7 ; Is number negative? goto setMin ; Yes, set minimum movf freq+3,f ; btfss STATUS,B_Z ; goto exit2 ; movf freq+2,f ; btfss STATUS,B_Z ; goto exit2 ; movlw 0x03 ; subwf freq+1,w ; btfss STATUS,B_C ; goto setMin ; movlw 0xFC ; addwf freq+1,w ; btfsc STATUS,B_C ; goto exit2 ; movlw 0x17 ; addwf freq,w ; btfsc STATUS,B_C ; goto exit2 ; setMin movlw 0xE8 ; movwf freq ; movlw 0x03 ; movwf freq+1 ; clrf freq+2 ; clrf freq+3 ; exit2 return ; ;******************************************************************************* ; PollEncoder clrf ren_timer+1 ; Put starting values in ren_timer movlw 0x40 ; movwf ren_timer ; read_encoder clrwdt ; Reset the watchdog timer btfsc ren_timer+1,7 ; goto noInc ; movlw 0x08 ; addwf ren_timer,f ; btfsc STATUS,B_C ; incf ren_timer+1,f ; noInc movf PortA,w ; Get the current encoder value movwf ren_read ; Save it movlw 0x03 ; Get encoder mask andwf ren_read,w ; Isolate encoder bits movwf ren_new ; Save new value xorwf ren_old,w ; Has it changed? btfsc STATUS,B_Z ; goto read_encoder ; No, keep looking until it changes ; ; Determine which direction the encoder turned. ; bcf STATUS,B_C ; Clear the carry bit rlf ren_old,f ; movf ren_new,w ; xorwf ren_old,f ; movf ren_old,w ; andlw 0x02 ; movwf Next_dir ; xorwf Last_dir,w ; ; ; Prevent encoder slip from giving a false change in direction. ; btfsc STATUS,B_Z ; Zero? goto continue ; No slip; keep going movf Next_dir,w ; Yes, update direction movwf Last_dir ; movf ren_new,w ; movwf ren_old ; goto read_encoder ; continue btfsc ren_old,1 ; goto up2 ; clrf Last_dir ; goto exit3 ; up2 movlw 0x02 ; movwf Last_dir ; exit3 movf ren_new,w ; movwf ren_old ; return ; ; ;******************************************************************************* ; Calibrate movlw 0x80 ; Set frequency to 10MHz movwf freq ; 0159 movlw 0x96 ; Location = 0x015A movwf freq+1 ; movlw 0x98 ; movwf freq+2 ; movlw 0x00 ; movwf freq+3 ; movlw 0x06 ; Load the default oscillator freq movwf osc ; movlw 0xF0 ; movwf osc+1 ; movlw 0xCA ; movwf osc+2 ; movlw 0x23 ; movwf osc+3 ; call BIN2BCD ; call ShowFreq ; movlw 0xC4 ; Point LCD at digit 14 movwf LCD_char ; call Cmnd2LCD ; movlw 0x43 ; Send a C movwf LCD_char ; call Data2LCD ; movlw 0x41 ; Send an A movwf LCD_char ; call Data2LCD ; movlw 0x4C ; Send an L movwf LCD_char ; call Data2LCD ; Cal_Loop call Calc_DDS_Word ; call Send_DDS_Word ; call PollEncoder ; clrf fstep+3 ; clrf fstep+2 ; clrf fstep+1 ; movlw 0x10 ; Slow adjust movwf fstep ; btfsc ren_read,2 ; goto updateOSC ; movlw 0x80 ; Fast adjust movwf fstep ; updateOSC nop ; btfsc Last_dir,1 ; goto faster ; ; ; slower ; comf fstep,f ; Subtraction of fstep is done by comf fstep+1,f ; adding the twos compliment of fsetp comf fstep+2,f ; to osc comf fstep+3,f ; incfsz fstep,f ; goto faster ; incfsz fstep+1,f ; goto faster ; incfsz fstep+2,f ; goto faster ; incf fstep+3,f ; faster movf fstep,w ; addwf osc,f ; btfss STATUS,B_C ; goto add4 ; incfsz osc+1,f ; goto add4 ; incfsz osc+2,f ; goto add4 ; incf osc+3,f ; add4 movf fstep+1,w ; addwf osc+1,f ; btfss STATUS,B_C ; goto add5 ; incfsz osc+2,f ; goto add5 ; incf osc+3,f ; add5 movf fstep+2,w ; addwf osc+2,f ; btfss STATUS,B_C ; goto add6 ; incf osc+3,f ; add6 movf fstep+3,w ; addwf osc+3,f ; btfss ren_read,3 ; goto Cal_Loop ; clrf EEadr ; Write final value to EEprom movf osc,w ; movwf EEdata ; call WriteEE ; movf osc+1,w ; movwf EEdata ; call WriteEE ; movf osc+2,w ; movwf EEdata ; call WriteEE ; movf osc+3,w ; movwf EEdata ; call WriteEE ; return ; ; ;******************************************************************************* ; ; Multiply the 32 bit number for oscillator frequency times the 32 bit number ; for the displayed frequency. ; Calc_DDS_Word clrf AD9850 ; Clear AD9850 retisters clrf AD9850+1 ; clrf AD9850+2 ; clrf AD9850+3 ; clrf AD9850+4 ; movlw 0x20 ; Set move count movwf multCount ; movf osc,w ; movwf osc_temp ; movf osc+1,w ; movwf osc_temp+1 ; movf osc+2,w ; movwf osc_temp+2 ; movf osc+3,w ; movwf osc_temp+3 ; mult_loop bcf STATUS,B_C ; btfss osc_temp,0 ; goto noAdd ; movf freq,w ; addwf AD9850+1,f ; btfss STATUS,B_C ; goto add7 ; incfsz AD9850+2,f ; goto add7 ; incfsz AD9850+3,f ; goto add7 ; incf AD9850+4,f ; add7 movf freq+1,w ; addwf AD9850+2,f ; btfss STATUS,B_C ; goto add8 ; incfsz AD9850+3,f ; goto add8 ; incf AD9850+4,f ; add8 movf freq+2,w ; addwf AD9850+3,f ; btfss STATUS,B_C ; goto add9 ; incf AD9850+4,f ; add9 movf freq+3,w ; addwf AD9850+4,f ; noAdd rrf AD9850+4,f ; rrf AD9850+3,f ; rrf AD9850+2,f ; rrf AD9850+1,f ; rrf AD9850,f ; rrf osc_temp+3,f ; rrf osc_temp+2,f ; rrf osc_temp+1,f ; rrf osc_temp,f ; decfsz multCount,f ; goto mult_loop ; clrf AD9850+4 ; return ; ; ;******************************************************************************* ; ; Send Control Word to AD9850 DDS chip. ; This routine does a serial data transfer to the AD9850. ; Send_DDS_Word movlw AD9850 ; Point FSR at AD9850 movwf FSR ; next_byte movf INDF,w ; movwf byte2send ; movlw 0x08 ; Set counter to 8 movwf bitCount ; next_bit rrf byte2send,f ; Test if next bit is 1 or 0 btfss STATUS,B_C ; Was it zero? goto send0 ; Yes, send zero bsf PortB,7 ; No, send one bsf PortB,5 ; Toggle write clock bcf PortB,5 ; goto break ; send0 bcf PortB,7 ; Send zero bsf PortB,5 ; Toggle write clock bcf PortB,5 ; break decfsz bitCount,f ; Has the whole byte been sent? goto next_bit ; No, keep going. incf FSR,f ; Start the next byte unless finished movlw AD9850+5 ; Next byte subwf FSR,w ; btfss STATUS,B_C ; goto next_byte ; bsf PortB,0 ; Send load signal to the AD9850 bcf PortB,0 ; return ; ; ;******************************************************************************* ; ; This subroutine converts a 32 bit binary number to a 10 digit BCD number. ; The input value taken from freq(0 to 3) is preserved. ; The output is in bcd(0 to 4), each byte holds => (hi_digit,lo_digit), ; most significant digits are in bcd+4. This routine is a modified version of one ; described in MicroChip application note AN526. ; BIN2BCD movlw 0x20 ; movwf bcdCount ; clrf bcd ; clrf bcd+1 ; clrf bcd+2 ; clrf bcd+3 ; clrf bcd+4 ; loop bcf STATUS,B_C ; rlf freq,f ; rlf freq+1,f ; rlf freq+2,f ; rlf freq+3,f ; btfsc STATUS,B_C ; bsf 0x0C,0 ; rlf bcd,f ; rlf bcd+1,f ; rlf bcd+2,f ; rlf bcd+3,f ; rlf bcd+4,f ; decf bcdCount,f ; btfss STATUS,B_Z ; goto adjust ; return ; adjust movlw 0x10 ; movwf FSR ; call adjBCD ; incf FSR,f ; call adjBCD ; incf FSR,f ; call adjBCD ; incf FSR,f ; call adjBCD ; incf FSR,f ; call adjBCD ; goto loop ; adjBCD movf INDF,w ; movwf bcdTemp ; movlw 0x33 ; addwf bcdTemp,f ; clrw ; btfss 0x1F,3 ; iorlw 0x03 ; btfss 0x1F,7 ; iorlw 0x30 ; subwf bcdTemp,f ; movf bcdTemp,w ; movwf INDF ; return ; ; ;******************************************************************************* ; ; Display the frequency setting on LCD. ; ShowFreq movlw 0x81 ; Point the LCD to digit number one. movwf LCD_char ; call Cmnd2LCD ; movf bcd+3,w ; Send the 10MHz digit movwf LCD_char ; movlw 0xF0 ; andwf LCD_char,f ; swapf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movf bcd+3,w ; Send the 1MHz digit movwf LCD_char ; movlw 0x0F ; andwf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movlw ',' ; Send a comma movwf LCD_char ; call Data2LCD ; ; movf bcd+2,w ; Send the 100 kHz digit movwf LCD_char ; movlw 0xF0 ; andwf LCD_char,f ; swapf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movf bcd+2,w ; Send the 10 kHz digit movwf LCD_char ; movlw 0x0F ; andwf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movf bcd+1,w ; Send the 1 kHz digit movwf LCD_char ; movlw 0xF0 ; andwf LCD_char,f ; swapf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movlw '.' ; Send a period movwf LCD_char ; call Data2LCD ; ; movlw 0xC0 ; Point to LCD digit number nine movwf LCD_char ; call Cmnd2LCD ; ; movf bcd+1,w ; Send 100 Hz digit movwf LCD_char ; movlw 0x0F ; andwf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movf bcd,w ; Send 10 Hz digit movwf LCD_char ; movlw 0xF0 ; andwf LCD_char,f ; swapf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movf bcd,w ; Send 1 Hz digit movwf LCD_char ; movlw 0x0F ; andwf LCD_char,f ; movlw 0x30 ; iorwf LCD_char,f ; call Data2LCD ; ; movlw ' ' ; Send a space movwf LCD_char ; call Data2LCD ; ; movlw 'k' ; Send a 'k' movwf LCD_char ; call Data2LCD ; ; movlw 'H' ; Send an "H" movwf LCD_char ; call Data2LCD ; ; movlw 'z' ; Send a 'z' movwf LCD_char ; call Data2LCD ; ; return ; ; ;******************************************************************************* ; ; Check if LCD is done with the last operation. ; Busy_Check clrf PortB ; Clear outputs on PortB bsf STATUS,B_RP0 ; Switch to bank 1 movlw b'11110000' ; Set inputs movwf PortB ; bcf STATUS,B_RP0 ; Switch back to bank 0 bcf PortB,LCD_rs ; Set up LCD for commands (RS) bsf PortB,LCD_rw ; Set up LCD for read movlw 0xFF ; movwf timer1 ; lcd_is_busy bsf PortB,LCD_e ; movf PortB,w ; movwf LCD_read ; bcf PortB,LCD_e ; nop ; nop ; bsf PortB,LCD_e ; nop ; bcf PortB,LCD_e ; decf timer1,f ; Try it 255 times, then return anyway btfsc STATUS,B_Z ; goto not_busy ; btfsc LCD_read,7 ; goto lcd_is_busy ; not_busy return ; ; ;******************************************************************************* ; ; Send Command to the LCD Subroutine. ; The command to be sent must in LCD_char before calling the subroutine. ; Cmnd2LCD call Busy_Check ; clrf PortB ; bsf STATUS,B_RP0 ; Switch to bank 1 movlw 0x00 ; Enable PortB data pins movwf PortB ; bcf STATUS,B_RP0 ; Switch to bank 0 bcf PortB,LCD_rs ; Set LCD for commands (RS) bcf PortB,LCD_rw ; Set LCD for write ; movlw 0x0F ; Transfer the first nibble andwf PortB,f ; movf LCD_char,w ; andlw 0xF0 ; iorwf PortB,f ; bsf PortB,LCD_e ; Pulse the E line nop ; bcf PortB,LCD_e ; ; movlw 0x0F ; Transfer the second nibble andwf PortB,f ; swapf LCD_char,f ; movf LCD_char,w ; andlw 0xF0 ; iorwf PortB,f ; bsf PortB,LCD_e ; Pulse the E line nop ; bcf PortB,LCD_e ; return ; ; ;******************************************************************************* ; ; Send Data to the LCD Subroutine. ; The data to be sent must in LCD_char before calling the subroutine. ; Data2LCD call Busy_Check ; Wait until the LCD is free clrf PortB ; Clear PortB outputs bsf STATUS,B_RP0 ; Switch to bank 1 movlw 0x00 ; Set as inputs movwf PortB ; bcf STATUS,B_RP0 ; Switch to bank 0 bsf PortB,LCD_rs ; Set RS for data bcf PortB,LCD_rw ; Clear RW for write ; movlw 0x0F ; Transfer the first nibble andwf PortB,f ; movf LCD_char,w ; andlw 0xF0 ; iorwf PortB,f ; bsf PortB,LCD_e ; Pulse E line nop ; bcf PortB,LCD_e ; ; movlw 0x0F ; Transfer the second nibble andwf PortB,f ; swapf LCD_char,f ; movf LCD_char,w ; andlw 0xF0 ; iorwf PortB,f ; bsf PortB,LCD_e ; Pulse the E line nop ; bcf PortB,LCD_e ; return ; ; ;******************************************************************************* ; WriteEE bsf STATUS,B_RP0 ; Switch to bank 1 bsf EEdata,WREN ; movlw 0x55 ; movwf EEadr ; movlw 0xAA ; movwf EEadr ; bsf EEdata,WR ; bit_check btfsc EEdata,WR ; goto bit_check ; bcf EEdata,WREN ; bcf STATUS,B_RP0 ; Switch to bank 0 incf EEadr,f ; return ; ; ;******************************************************************************* ; ReadEE bsf STATUS,B_RP0 ; Switch to bank 1 bsf EEdata,RD ; bcf STATUS,B_RP0 ; Switch to bank 0 incf EEadr,f ; return ; ; ;******************************************************************************* ; ; Delay subroutines. ; Wait255 movlw 0xFF ; Set up timer movwf timer1 ; goto Wait16 ; Wait128 movlw 0x80 ; movwf timer1 ; goto Wait16 ; Wait64 movlw 0x40 ; movwf timer1 ; goto Wait16 ; Wait32 movlw 0x20 ; movwf timer1 ; goto Wait16 ; movlw 0x10 ; movwf timer1 ; Wait16 movlw 0xFF ; movwf timer2 ; inner decfsz timer2,f ; goto inner ; decfsz timer1,f ; goto Wait16 ; return ; ; ;******************************************************************************* ; END