;**************************************************************************** ; * HF VFO with AD9854 Direct Digital Synthesis * ; * Version 5.19b * ; * (Has pushbutton debounce code and leading zero suppression) * ; * December 28, 2004 * ; * * ;**************************************************************************** ; Authors - Craig Johnson, AA0ZZ and Bruce Stough, AA0ED ; ; Description: ; This code is for a PIC16F877 microcontroller which is used to control a ; VFO using an Analog Devices AD9854 DDS. ; ; In the July, 1997 issue of QEX magazine, Curtis Pruess, WB2V, published ; an article featuring a signal generator which used an Analog Devices ; AD9850 DDS device controlled by a PIC16F84 microcontroller. ; ; Since that time, a newer device has been introduced by Analog Devices - ; the AD9854. This device has some significant advantages over the AD9850. ; ; First, the AD9854 produces two outputs with a 90 degree phase difference. ; This phase relationship remains constant regardless of the frequency. ; ; Second, the AD9854 has a 12 Bit DAC (D/A converter) while the AD9850 has ; a 10 bit DAC. This is important in reducing the undesirable byproducts ; (spurs). ; ; Third, the AD9854 has a 300 MHz maximum clock speed, compared to 125 MHz ; for the AD9850. The faster clock speed raises the Nyquest limit, ; allowing the VFO to operate cleanly up through 30 MHz. ; ; This project is an extension of the concepts introduced in that original ; AD9850 project, but there have been many major changes. It still has ; an optical encoder (or optional mechanical encoder) for frequency ; selection and it still uses an LCD for displaying the frequency. However, ; the HF-VFO has been changed in these ways: ; ; 1) The HF-VFO now uses two PIC microcontrollers - one controlling the ; optical encoder and the other controlling the AD9854, the LCD, the ; pushbuttons and the LEDs. ; 2) The HF-VFO now uses a two-line LCD so more information is displayed. ; 3) The HF-VFO now operates as two VFOs with two frequencies. ; 4) The two-part VFO supports split-frequency operation. ; 5) The two AD9854 outputs are amplified to produce signal levels which ; are compatible with common receiver requirements (such as the R2 or ; R2Pro by Rick Campbell - KK7B.) ; ; Early versions of this HF-VFO project used a single PIC microcontroller ; for all VFO functions. However, the PIC's main code execution loop was ; very large and it varied a great deal in length, depending on if the LCD ; and DDS needed to be updated and if pushbuttons are being used. Since ; this PIC also monitored the input signals from the optical encoder, ; reading the "gray code" to determine how fast the encoder was being ; turned, a variable size main execution loop made accurate timing and ; smooth operation very difficult to accomplish. ; ; In this version we decided to use a dedicated PIC16F628 microcontroller ; for the optical encoder. Why? They are inexpensive and provide a ; rather elegant solution to the problem of accurate timing for smooth ; tuning operation. ; ; The encoder PIC monitors the output of the encoder. Periodically it ; sends information to the main ('877) microcontroller to indicate that ; the encoder tuning knob has been turned as well as the direction of ; change. The encoder ('628) microcontroller sends the data to the ; '877 PIC by setting up 3 data lines and a direction line and then ; generating a pulse on the '877 PIC's interrupt line. The interrupt ; causes the '877 PIC to stop its current operation for a very brief ; period of time, to examine the data lines, save the new magnitude and ; direction, set a flag saying a change has been made, and then return ; to the task it was executing at the time of the interrupt. A very ; short time later, code in the main execution loop will detect the ; "change" flag and make the appropriate frequency change in the DDS ; and LCD. ; ; For further details and documentation regarding this project, please see ; my web page: www.cbjohns.com/aa0zz ; ; The code for the encoder PIC, the PIC16F628, can also be found on the web ; site. ; ;***************************************************************************** ; ; Target Controller for optical shaft encoder - PIC16F628 ; __________ ; (Unused)-----------RA2 |1 18| RA1-I-----Encoder A ; (Unused)-----------RA3 |2 17| RA0-I-----Encoder B ; (Unused)-----------RA4 |3 16| RA7------(Unused) ; +5v---------!MCLR |4 15| RA6------(Unused) ; GND-----------Vss |5 14| VDD------+5 V ; (Unused)-----------RB0 |6 13| RB7-O-----ENC3 (16F877-RA0) (DIR) ; (Unused)-----------RB1 |7 12| RB6-O-----ENC2 (16F877-RA1) ; (Unused)-----------RB2 |8 11| RB5-O---- ENC1 (16F877-RA2) ; ENC Int.(16F877-RB0)------O-RB3 |9 10| RB4-O---- ENC0 (16F877-RA3) ; ---------- ; ; ; Target Controller for DDS/LCD - PIC16F877 ; __________ ; +5v-------!MCLR |1 40| RB7-O---- (Rsvd) - (RX VFO A - LED3) ;(DIR)ENC3(16F84-RB7)-------I-RA0 |2 39| RB6-O---- (Rsvd) - (RX VFO B - LED4) ; ENC2 (16F84-RB6)-------I-RA1 |3 38| RB5-I---- VFO FREQ A=B(Pushbutton5) ; ENC1 (16F84-RB5)-------I-RA2 |4 37| RB4-I---- Split (Pushbutton6) ; ENC0 (16F84-RB4)-------I-RA3 |5 36| RB3-O---- (Reserved) -(SPLIT - LED5) ; XMIT_keyed_low_active----I-RA4 |6 35| RB2-I---- RX VFO Toggle(Pushbutton7) ;(Pushbutton1) MHZ UP-------I-RA5 |7 34| RB1-I---- Calibrate (Pushbutton8) ;(Pushbutton2) MHZ DN-------I-RE0 |8 33| RB0/INT-I - ENC Int. (16F84-RB3) ;(Pushbutton3)FTToggle------I-RE1 |9 32| VDD------ +5v ;(Pushbutton4)-Debug--------I-RE2 |10 31| VSS------ GND ; +5v---------VDD |11 30| RD7-O---- Unused ; GND---------VSS |12 29| RD6-O---- DDS_clk (AD9854 pin 21) ; XTAL--------OSC1 |13 28| RD5-O---- DDS_ioud (AD9854 pin 20) ; XTAL--------OSC2 |14 27| RD4-O---- DDS_data (AD9854 pin 19) ; (LED0) Debug -------O-RC0 |15 26| RC7-O/I-- LCD_D7--(LCD pin 14) ; (LCD pin 6)-LCD_e -------O-RC1 |16 25| RC6-O/I-- LCD_D6--(LCD pin 13) ; (LCD pin 5)-LCD_rw-------O-RC2 |17 24| RC5-O/I-- LCD_D5--(LCD pin 12) ; (LCD pin 4)-LCD_rs-------O-RC3 |18 23| RC4-O/I-- LCD_D4--(LCD pin 11) ; (LED1) Debug -------O-RD0 |19 22| RD3-O---- DDS_ioreset(AD9854 pin 17) ; (LED2) Debug -------O-RD1 |20 21| RD2-O---- DDS_mreset (AD9854 pin 71) ; ---------- ; ;**************************************************************************** ; * Device type and options. * ;**************************************************************************** ; processor PIC16F877 ; include "p16f877.inc" ; Include only the definitions that are needed radix dec ; ;========================================================================== ; ; Configuration Bits ; ;========================================================================== _CP_OFF EQU 0x3FFF _DEBUG_ON EQU 0x37FF ; In-circuit Debugger mode (RB6,RB7) _DEBUG_OFF EQU 0x3FFF _LVP_OFF EQU 0x3F7F _PWRTE_OFF EQU 0x3FFF _PWRTE_ON EQU 0x3FF7 _WDT_ON EQU 0x3FFF _WDT_OFF EQU 0x3FFB _LP_OSC EQU 0x3FFC _XT_OSC EQU 0x3FFD _HS_OSC EQU 0x3FFE _RC_OSC EQU 0x3FFF ; __config _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC & _DEBUG_OFF & _LVP_OFF ; ; Info for Power-on display on LCD ; MCODE_REV_0 equ ' ' ; Current microcode revision level 5.17 MCODE_REV_1 equ '5' MCODE_REV_2 equ '1' MCODE_REV_3 equ '9' MCODE_REV_4 equ 'b' ; CLOCK_DISPLAY_0 equ '1' ; Clock Speed = 125 MHz CLOCK_DISPLAY_1 equ '2' CLOCK_DISPLAY_2 equ '5' ; ENCODER_DISPLAY_0 equ '1' ; Optical Encoder - 128 positions ENCODER_DISPLAY_1 equ '2' ENCODER_DISPLAY_2 equ '8' ENCODER_DISPLAY_0A equ ' ' ; Alternate - Mechanical Encoder - 24 pos. ENCODER_DISPLAY_1A equ '2' ENCODER_DISPLAY_2A equ '4' ; ;========================================================================== ; ; Register Definitions ; ;========================================================================== W EQU 0x0000 F EQU 0x0001 ; ;----- Register Files------------------------------------------------------ ; ;**************************************************************************** ; * RAM page independent file registers: * ;**************************************************************************** INDF EQU 0x0000 TMR0 EQU 0x0001 PCL EQU 0x0002 STATUS EQU 0x0003 FSR EQU 0x0004 INTCON EQU 0x000B ;**************************************************************************** ; * Bank 0 file registers: * ;**************************************************************************** PORTA EQU 0x0005 PORTB EQU 0x0006 PORTC EQU 0x0007 PORTD EQU 0x0008 PORTE EQU 0x0009 PCLATH EQU 0x000A INTCON EQU 0x000B ; ;**************************************************************************** ; * Bank 1 file registers: * ;**************************************************************************** OPTION_REG EQU 0x0081 TRISA EQU 0x0085 TRISB EQU 0x0086 TRISC EQU 0x0087 TRISD EQU 0x0088 TRISE EQU 0x0089 ADCON1 EQU 0x009F ; ;**************************************************************************** ; * Bank 2 file registers: * ;**************************************************************************** EEDATA EQU 0x010C EEADR EQU 0x010D ; ;**************************************************************************** ; * Bank 3 file registers: * ;**************************************************************************** EECON1 EQU 0x018C EECON2 EQU 0x018D ;----- STATUS Bits (All banks) --------------------------------------------- IRP EQU 0x0007 RP1 EQU 0x0006 RP0 EQU 0x0005 ; RP1:RP0 ; 1 1 = Bank 3 ; 1 0 = Bank 2 ; 0 1 = Bank 1 ; 0 0 = Bank 0 Z EQU 0x0002 DC EQU 0x0001 C EQU 0x0000 ;----- INTCON Bits -------------------------------------------------------- GIE EQU 0x0007 ; Global interrupt enable INTE EQU 0x0004 ; RB0 external interrupt enable INTF EQU 0x0001 ; RB0 external interrupt bit ;----- OPTION_REG Bits ----------------------------------------------------- NOT_RBPU EQU 0x0007 ;----- EECON1 Bits -------------------------------------------------------- EEPGD EQU 0x0007 WRERR EQU 0x0003 WREN EQU 0x0002 WR EQU 0x0001 RD EQU 0x0000 ;***************************************************************************** ; * General equates. These may be changed to accommodate the reference clock* ; * frequency, the desired upper frequency limit, and the default startup * ; * frequency. * ;***************************************************************************** ; ; ref_osc represents the change in the frequency control word which results ; in a 1 Hz change in output frequency. It is interpreted as a fixed point ; real number in the format . ; ; NOTE: This is the same 32-bit value that was used to calculate the 32-bit ; frequency control word for the AD9850. The AD9854 being used here requires ; a 48-bit frequency control word instead of 32. However, since 48-bit ; accuracy is not necessary for this application, for performance reasons we ; will continue to use the 32 bit value calculated here. We will construct ; the 48-bit frequency control word from this 32 bit value. We will multiply ; this 32-bit value by the desired output frequency (in Hz). We will use ; this 32-bit product as the most significant 32 bits of the 48-bit frequency ; tuning word, and will use 16 bits of zeros for the lowest order 16 bits. ; ; The values for common oscillator frequencies are as follows: ; ; Frequency ref_osc_3 ref_osc_2 ref_osc_1 ref_osc_0 ; ; 125.00 MHz 0x22 0x5C 0x17 0xD0 ; 120.00 MHz 0x23 0xCA 0x98 0xCE ; 100.00 MHz 0x2A 0xF3 0x1D 0xC4 ; 90.70 MHz 0x2F 0x5A 0x82 0x7A ; 66.66 MHz 0x40 0x6E 0x52 0xE7 ; 66.00 MHz 0x41 0x13 0x44 0x5F ; 60.00 MHz 0x47 0x95 0x31 0x9C ; 50.00 MHz 0x55 0xE6 0x3B 0x88 ; ; To calculate other values: ; ref_osc_3 = (2^32 / oscillator_freq_in_Hertz). ; ref_osc_2, ref_osc_1, and ref_osc_0 are the fractional part of ; (2^32 / oscillator_freq_in_Hertz) times 2^24. ; Note: 2^32 = 4294967296 and 2^24 = 16777216 ; ; For example, for a 125 MHz clock: ; ref_osc_3 is (2^32 / 125 x 10^6) = 34.359738368 truncated to 34 (0x22) ; ref_osc_2 is the high byte of (.359738368 x 2^24) = 6035408.303 ; 6035408.303 = 0x5C17D0, so high byte is 5C. ; ref_osc_1 is the next byte of 0x5C17D0, or 17 ; ref_osc_0 is the last byte of 0x5C17D0, or D0 ; ; For example, for a 120 MHz clock: ; ref_osc_3 is (2^32 / 120 x 10^6) = 35.791394133 truncated to 35 (0x23) ; ref_osc_2 is the high byte of (.791394133 x 2^24) = 13277390.32 ; 13277390.32 = 0xCA98CE, so high byte is CA. ; ref_osc_1 is the next byte of 0xCA98CE, or 98 ; ref_osc_0 is the last byte of 0xCA98CE, or CE ; ; for 60 MHz clock: ; ref_osc_3 is (2^32 / 60 x 10^6) = 71.58278827 truncated to 71 (0x47) ; ref_osc_2 is the high byte of (.58278827 x 2^24) = 9777564.688 ; 0999564.688 = 0x95319C, so high byte is 0x95 ; ref_osc_1 is the next byte of 0x95319C, or 0x31 ; ref_osc_0 is the next byte of 0x95319C, or 0x9C ; ; ;==== Currently set for 125 MHz Oscillator ======= ; ref_osc_3 equ 0x22 ; Most significant osc byte ref_osc_2 equ 0x5C ; Next byte ref_osc_1 equ 0x17 ; Next byte ref_osc_0 equ 0xD0 ; Least significant byte ; ; Limit contains the upper limit frequency as a 32 bit integer. ; This should not be set to more than one third of the reference oscillator ; frequency. The output filter of the DDS board must be designed to pass ; frequencies up to the maximum. ; ; Set up upper frequency limit = 30,000,000 = 0x01C9C380 limit_3 equ 0x01 ; Most significant byte for 30 MHz limit_2 equ 0xC9 ; Next byte limit_1 equ 0xC3 ; Next byte limit_0 equ 0x80 ; Least significant byte ; ; Default contains the default startup frequency as a 32 bit integer. ; default_3 equ 0x00 ; Most significant byte for 14.025 MHz default_2 equ 0xD6 ; Next byte default_1 equ 0x01 ; Next byte default_0 equ 0x28 ; Least significant byte ; band_end equ 0x28 ; The offset to the last band table entry ; ; The following definitions are used to drive the AD9854. ; freq_write_cmd equ 0x02 ; The command to write to the freq-1 words ;freq_write_2_cmd equ 0x03 ; The command to write to the freq-2 words (NOT USED) CR_write_cmd equ 0x07 ; The command to write to the CR registers CR_read_cmd equ 0x87 ; The command to read from the CR registers CR_byte_0 equ 0x10 ; The most significant CR byte (0x1D) (COMP PD) CR_byte_1 equ 0x20 ; The second CR byte (0x1E) (BYPASS PLL) CR_byte_2 equ 0x00 ; The third CR byte (0x1F) (NO SRC QDAC + EXTERNAL UPDATE) CR_byte_3 equ 0x40 ; The least significant CR byte (0x20) (BYPASS INV SINC) ; ; ; Equates for VFOcontrol register ; VFOA equ 0 ; Bit indicates VFO A active (for Receive) VFOB equ 1 ; Bit indicates VFO B active (for Receive) SPLIT equ 2 ; Bit indicates SPLIT operation active TRANSMIT equ 3 ; Bit indicates TRANSMIT active (use other VFO if SPLIT) ALTERNATE equ 4 ; Bit indicates Alternate Encoder is selected NEEDUPDATE equ 5 ; A change has been made, so need DDS and LCD update FAST_TUNING equ 6 ; Bit indicates fast tuning DIR_UP equ 7 ; Encoder UP direction (decode of ENC3 - for encdir) ; ; ; Equates for DebugControl register - Triggered by pushbutton 4 DDS_init_loop equ 0 ; Bit indicates DDS Init should loop ; ;**************************************************************************** ; * ID location information: * ; * (MPASM warns about DW here, don't worry) * ;**************************************************************************** ; ORG 0x2000 DATA 0x000F DATA 0x000F DATA 0x000F DATA 0x000F ; ; ;**************************************************************************** ; * Setup the initial constant, based on the frequency of the reference * ; * oscillator. This can be tweaked with the calibrate function. * ;**************************************************************************** ; ORG 0x2100 ; ; ref osc data starts at address 0 of EEPROM DATA ref_osc_0 DATA ref_osc_1 DATA ref_osc_2 DATA ref_osc_3 ; Clear 252 unused EEPROM bytes ; (16F877 has 256 bytes) ; 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 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 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 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 DATA 0,0,0,0,0,0,0,0,0,0,0,0 ; ; ; ;**************************************************************************** ; * Assign names to IO pins. * ; ; INPUT = 1 Output = 0 Bit Order: 7 6 5 4 3 2 1 0 ;**************************************************************************** ; ; ; A register bits: ; TRISA - 0x03F ; ENC3 equ 0x00 ; Input (from 16F84) (Direction) ENC2 equ 0x01 ; Input (from 16F84) ENC1 equ 0x02 ; Input (from 16F84) ENC0 equ 0x03 ; Input (from 16F84) XMIT_Keyed_Low_Active equ 0x04 ; Input - Transmitter Keyed (Low Active) PBMHZUP equ 0x05 ; Input - Pushbutton - MHZ UP ; 0x06 - Does not exist in 16F877 ; 0x07 - Does not exist in 16F877 ; ; B register bits: ; TRISB - 0x37 ; INTIN equ 0x00 ; Input - Interrupt from 16F84 PBCALIBRATE equ 0x01 ; Input - Pushbutton - Calibrate mode PBVFOTOGGLE equ 0x02 ; Input - Pushbutton - Receive VFO toggle LEDSPLIT equ 0x03 ; Output - LED5 - Split VFO operation PBSPLIT equ 0x04 ; Input - Pushbutton - Split operation select PBVFOAEQB equ 0x05 ; Input - Pushbutton - Match other VFO to RCV VFO LEDRECVFOB equ 0x06 ; Output - LED4 - Receive uses VFO B LEDRECVFOA equ 0x07 ; Output - LED3 - Receive uses VFO A ; ; C register bits: ; TRISC - 0x00 ; LED0 equ 0x00 ; Output - LED 0 (debug) LCD_e equ 0x01 ; Output 0=disable, 1=enable (LCD pin 6) LCD_rw equ 0x02 ; Output 0=write, 1=read (LCD pin 5) LCD_rs equ 0x03 ; Output 0=instruction, 1=data (LCD pin 4) LCD_D4 equ 0x04 ; Output /(input status) Data (LCD pin 11) LCD_D5 equ 0x05 ; Output /(input status) Data (LCD pin 12) LCD_D6 equ 0x06 ; Output /(input status) Data (LCD pin 13) LCD_D7 equ 0x07 ; Output /(input status) Data (LCD pin 14) ; ; D register bits: ; TRISD - 0x00 ; LED1 equ 0x00 ; Output - LED 1 (debug) LED2 equ 0x01 ; Output - LED 2 (debug) DDS_mreset equ 0x02 ; Output - AD9854 master reset (AD9854 pin 71) DDS_ioreset equ 0x03 ; Output - AD9854 I/O reset (AD9854 pin 17) DDS_data equ 0x04 ; Output - AD9854 serial data SDIO(AD9854 pin 19) DDS_ioud equ 0x05 ; Output - AD9854 IO Update IOUD(AD9854 pin 20) DDS_clk equ 0x06 ; Output - AD9854 write clock (AD9854 pin 21) ; 0x07 ; Unused ; ; E register bits: ; TRISE - 0x03 ; PBMHZDN equ 0x00 ; Input - Pushbutton - MHZ DOWN PBFTTOGGLE equ 0x01 ; Input - Pushbutton - Fast tuning toggle DEBUG equ 0x02 ; Input - Pushbutton - Debug active toggle ; 0x03 - Does not exist in 16F877 ; 0x04 - Does not exist in 16F877 ; 0x05 - Does not exist in 16F877 ; 0x06 - Does not exist in 16F877 ; 0x07 - Does not exist in 16F877 ; ;**************************************************************************** ; * Allocate variables in general purpose register space * ;**************************************************************************** ; CBLOCK 0x20 ; Start Bank 0 Data Block (16F877) ; code_level_1 ; Microcode level - byte 1 code_level_2 ; Microcode level - byte 2 W_Save ; Save W (interrupt routine) Status_Save ; Save Status (interrupt routine) FSR_Save ; Save FSR (interrupt routine) PCLATH_Save ; Save PCLATH (interrupt routine) encindex ; Encoder - change index tuning_index ; Index for fstep table lookup VFOcontrol ; Specifies active Receive VFO and if split ; VFOA equ 0 ; Bit indicates VFO A active (for Receive) ; VFOB equ 1 ; Bit indicates VFO B active (for Receive) ; SPLIT equ 2 ; Bit indicates SPLIT operation active ; TRANSMIT equ 3 ; Bit indicates TRANSMIT active (use other VFO if SPLIT) ; ALTERNATE equ 4 ; Bit indicates Alternate Encoder is used. ; NEEDUPDATE equ 5 ; A change has been made, so need DDS and LCD update ; FAST_TUNING equ 6 ; Bit indicates fast tuning ; DIR_UP equ 7 ; Bit indicates Tuning Direction UP DebugControl ; ; DDS_init_loop equ 0 ; Bit indicates DDS initialization loop active freq_0 ; VFO frequency - temp (hex) (4 bytes) freq_1 ; freq_0 is the least significant byte, freq_2 ; freq_3 ; freq_3 is the most significant byte. Afreq_0 ; VFO A frequency (hex) (4 bytes) Afreq_1 ; Afreq_0 is the least significant byte, Afreq_2 ; Afreq_3 ; Afreq_3 is the most significant byte. Bfreq_0 ; VFO B frequency (hex) (4 bytes) Bfreq_1 ; Bfreq_0 is the least significant byte, Bfreq_2 ; Bfreq_3 ; Bfreq_3 is the most significant byte. workfreq_0 ; Working registers for VFO updates (4 bytes) workfreq_1 ; workfreq_0 is the least significant byte, workfreq_2 ; workfreq_3 ; workfreq_3 is the most significant byte. BCD_0 ; Display frequency (BCD) (5 bytes) BCD_1 ; BCD_2 ; BCD_3 ; BCD_4 ; AD9854_0 ; AD9854 control word (5 bytes) AD9854_1 ; AD9854_2 ; AD9854_3 ; AD9854_4 ; fstep_0 ; Frequency inc/dec (4 bytes) fstep_1 ; fstep_2 ; fstep_3 ; BCD_count ; Used in bin2BCD routine BCD_temp ; " " " " mult_count ; Used in calc_dds_word bit_count ; " " " " byte2send ; osc_0 ; Current oscillator (4 bytes) osc_1 ; osc_2 ; osc_3 ; osc_temp_0 ; Oscillator frequency (4 bytes) osc_temp_1 ; osc_temp_2 ; osc_temp_3 ; LCD_char ; Character being sent to the LCD LCD_nibble ; Rearranged MS nibble for the LCD LCD_read ; Character read from the LCD timer1 ; Used in delay routines timer2 ; " " " " count ; loop counter (gets reused) band ; Used to index a table of frequencies rs_value ; The LCD rs line flag value status_save ; Save status during interrupt processing int_stack_count ; Count of Interrupts on Stack CR_work ; Work register for constructing CR byte CR_in0 ; Read first CR byte from AD9854 CR_in1 ; Read second CR byte from AD9854 CR_in2 ; Read third CR byte from AD9854 CR_in3 ; Read fourth CR byte from AD9854 button5set ; pb 5 is pushed - possibly want alt encoder RememberLED0 ; Save state of LED0 in LCD-busy routine ENDC ; End of Bank 0 Data Block ; ; *************************** CBLOCK 0xA0 ; Start Bank 1 Dummy Data Block (16F877) W_Save_bank1_reserve ; Save W (interrupt routine) ENDC ; End of Data 1 Block ; ; *************************** CBLOCK 0x120 ; Start Bank 2 Dummy Data Block (16F877) W_Save_bank2_reserve ; Save W (interrupt routine) ENDC ; End of Data 2 Block ; ; *************************** CBLOCK 0x1A0 ; Start Bank 3 Dummy Data Block (16F877) W_Save_bank3_reserve ; Save W (interrupt routine) ENDC ; End of Data 3 Block ; ;**************************************************************************** ;**************************************************************************** ; >>>>>>>>>>>>>>>>>> RESET AND INTERRUPT VECTORS <<<<<<<<<<<<<<<<<<<<<<<<<<<< ;**************************************************************************** ;**************************************************************************** ; ;**************************************************************************** ; * RESET VECTOR (16F877 resets to 0x00) * ;**************************************************************************** ORG 0x0000 reset_entry nop ; NOP required here for MPASM-ICD! goto start ; Jump around the band table to main ;**************************************************************************** ; * INTERRUPT VECTOR (at 0x04) * ;**************************************************************************** ORG 0x0004 interrupt_entry goto interrupt_handler ; Jump to interrupt handler ; ;**************************************************************************** ; * This is the band table. Each entry is four instructions long, with each* ; * group of four literals representing the frequency as a 32 bit integer. * ; * New entries can be added to the end of the table or between existing * ; * entries. The constant band_end must be incremented by 4 for each entry * ; * added. * ; * * ; * This table is placed near the top of the program to allow as large a * ; * a table as possible to be indexed with the eight bit value in W. * ; * * ;**************************************************************************** ; band_table addwf PCL,f ; retlw 0x00 ; 0 Hz retlw 0x00 ; retlw 0x00 ; retlw 0x00 ; 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 ; retlw 0x01 ; 30 MHz retlw 0xC9 ; retlw 0xC3 ; retlw 0x80 ; ; ; ======================================================= fstep_table ; Based on "Encshift" addwf PCL,f ; ; ; OPTICAL ENCODER ; Slow tuning table retlw 0x00 ; 0 (1) retlw 0x00 ; retlw 0x00 ; retlw 0x01 ; ; ----------------------------------------- retlw 0x00 ; 1 (2) retlw 0x00 ; retlw 0x00 ; retlw 0x02 ; ; ----------------------------------------- retlw 0x00 ; 2 (5) retlw 0x00 ; retlw 0x00 ; retlw 0x05 ; ; ----------------------------------------- retlw 0x00 ; 3 (10) retlw 0x00 ; retlw 0x00 ; retlw 0x0A ; ; ----------------------------------------- retlw 0x00 ; 4 (100) retlw 0x00 ; retlw 0x00 ; retlw 0x64 ; ; ----------------------------------------- retlw 0x00 ; 5 (300) retlw 0x00 ; retlw 0x01 ; retlw 0x2C ; ; ----------------------------------------- retlw 0x00 ; 6 (1000) retlw 0x00 ; retlw 0x03 ; retlw 0xE8 ; ; ----------------------------------------- retlw 0x00 ; 7 (5000) retlw 0x00 ; retlw 0x13 ; retlw 0x88 ; ; ; ----------------------------------------- ; OPTICAL ENCODER ; Fast tuning table ; retlw 0x00 ; 0 (1) retlw 0x00 ; retlw 0x00 ; retlw 0x01 ; ; ----------------------------------------- retlw 0x00 ; 1 (2) retlw 0x00 ; retlw 0x00 ; retlw 0x02 ; ; ----------------------------------------- retlw 0x00 ; 2 (10) retlw 0x00 ; retlw 0x00 ; retlw 0x0A ; ; ----------------------------------------- retlw 0x00 ; 3 (60) retlw 0x00 ; retlw 0x00 ; retlw 0x3C ; ; ----------------------------------------- retlw 0x00 ; 4 (600) retlw 0x00 ; retlw 0x02 ; retlw 0x58 ; ; ----------------------------------------- retlw 0x00 ; 5 (1800) retlw 0x00 ; retlw 0x07 ; retlw 0x08 ; ; ----------------------------------------- retlw 0x00 ; 6 (6000) retlw 0x00 ; retlw 0x17 ; retlw 0x70 ; ; ----------------------------------------- retlw 0x00 ; 7 (30000) retlw 0x00 ; retlw 0x75 ; retlw 0x30 ; ; ----------------------------------------- ; ; >>>>>>>> ALTERNATE ENCODER TABLE HERE <<<<<< ; MECHANICAL ENCODER ; Slow tuning table ; retlw 0x00 ; 0 (1) retlw 0x00 ; retlw 0x00 ; retlw 0x01 ; ; ----------------------------------------- retlw 0x00 ; 1 (5) retlw 0x00 ; retlw 0x00 ; retlw 0x05 ; ; ----------------------------------------- retlw 0x00 ; 2 (25) retlw 0x00 ; retlw 0x00 ; retlw 0x19 ; ; ----------------------------------------- retlw 0x00 ; 3 (50) retlw 0x00 ; retlw 0x00 ; retlw 0x32 ; ; ----------------------------------------- retlw 0x00 ; 4 (500) retlw 0x00 ; retlw 0x01 ; retlw 0xF4 ; ; ----------------------------------------- retlw 0x00 ; 5 (1500) retlw 0x00 ; retlw 0x05 ; retlw 0xDC ; ; ----------------------------------------- retlw 0x00 ; 6 (5000) retlw 0x00 ; retlw 0x13 ; retlw 0x88 ; ; ----------------------------------------- retlw 0x00 ; 7 (25000) retlw 0x00 ; retlw 0x61 ; retlw 0xA8 ; ; ----------------------------------------- ; MECHANICAL ENCODER ; Fast tuning table ; retlw 0x00 ; 0 (1) retlw 0x00 ; retlw 0x00 ; retlw 0x01 ; ; ----------------------------------------- retlw 0x00 ; 1 (7) retlw 0x00 ; retlw 0x00 ; retlw 0x07 ; ; ----------------------------------------- retlw 0x00 ; 2 (50) retlw 0x00 ; retlw 0x00 ; retlw 0x32 ; ; ----------------------------------------- retlw 0x00 ; 3 (300) retlw 0x00 ; retlw 0x01 ; retlw 0x2C ; ; ----------------------------------------- retlw 0x00 ; 4 (3000) retlw 0x00 ; retlw 0x0B ; retlw 0xB8 ; ; ----------------------------------------- retlw 0x00 ; 5 (9000) retlw 0x00 ; retlw 0x23 ; retlw 0x28 ; ; ----------------------------------------- retlw 0x00 ; 6 (30000) retlw 0x00 ; retlw 0x75 ; retlw 0x30 ; ; ----------------------------------------- retlw 0x00 ; 7 (150000) retlw 0x02 ; retlw 0x49 ; retlw 0xF0 ; ; ----------------------------------------- ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>> START AND MAIN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: This is the start of the program. It initializes the LCD. * ; * It sets the power-on frequency and enters the main loop. * ; * * ; * Input: The start up frequency is defined in the default_3 ... * ; * definitions above, and relies on the reference oscillator * ; * constant defined in ref_osc_3 ... ref_osc_0. * ; * * ; * Output: PIC, LCD, and DDS ready for normal VFO operation. * ; * * ;***************************************************************************** ; start call init_PIC ; Initialize the 16F877 call init_LCD ; Initialize the LCD call display_mcode_version ; Display microcode version on LCD for 2 sec. call init_DDS ; Initialize the AD9854 call init_freq ; Initialize default frequency and registers call allow_interrupts ; Now allow interrupts ; ; Display the power on frequency. ; call bin2BCD ; Convert it to BCD call show_freq ; Display it ; ; Send power on frequency to the DDS chip. ; call calc_dds_word ; Convert to delta value call send_dds_freq ; Send the power-on frequency to the ; AD9854 in serial mode ; ; Fall into the Main Program Loop ; ;***************************************************************************** ; * * ; * Purpose: This is the main program loop. * ; * * ; * 1) Check to see if frequency encoder knob has been turned * ; * If so, set NEEDUPDATE flag * ; * 2) Check for input from pushbuttons * ; * If so, perform appropriate action, set NEEDUPDATE flag * ; * 3) Check for input from keypad (future) * ; * If so, perform appropriate action, set NEEDUPDATE flag * ; * 4) Check for transmitter active * ; * If so, set flag for possible frequency shift * ; * 5) If NEEDUPDATE flag set, call routine to update DDS and LCD * ; * * ; * Input: None. * ; * * ; * Output: None. * ; * * ;***************************************************************************** ; main call encoder_look ; Check for knob movement call buttons_look ; Check for pushbuttons input call transmitter_active_look ; See if transmitter is active btfsc VFOcontrol,NEEDUPDATE ; Need a DDS and LCD update? call update_DDS_LCD ; Yes, update the DDS and LCD goto main ; Loop back ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>> INTERRUPT HANDLER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Purpose: This interrupt handling routine is called when an interrupt * ; * at RB0 is detected. Called from interrupt vector at 0x04. * ; * * ; * Upon interrupt: * ; * 1) Save environment * ; * 2) Process encoder change - amount and direction * ; * 3) Restore saved environment * ; * 4) Return to interrupted code * ; * * ;***************************************************************************** ; interrupt_handler ; movwf W_Save ; Copy W to save register IN CURRENT BANK! swapf STATUS,w ; Swap nibbles of STATUS, and move into W clrf STATUS ; Change to bank 0, regardless of current bank ; (Clears IRP, RP1, RP0) movwf Status_Save ; Save status (in bank 0 status save location) movf PCLATH,w ; Move PCLATH into W movwf PCLATH_Save ; and save it clrf PCLATH ; Change to page 0, regardless of current page movf FSR,w ; Move FSR into W movwf FSR_Save ; and save it ; ; Start ISR ; incf int_stack_count,f ; Increment interrupt stack count ; ; Determine encoder direction - save in encdir ; bcf INTCON,INTF ; Clear RB0 interrupt bsf VFOcontrol,DIR_UP ; Set direction to go UP (default) btfss PORTA,ENC3 ; Is direction bit set? (indicating UP) bcf VFOcontrol,DIR_UP ; No, clear UP direction bit (go DN) clrw ; Clear w ; ; Now set up encoder acceleration amount - encshift ; movlw 0x00 ; Clear w btfsc PORTA,ENC0 ; Is low order bit set? addlw 0x01 ; Yes, add 1 btfsc PORTA,ENC1 ; Is next higher bit set? addlw 0x02 ; Yes, add 2 btfsc PORTA,ENC2 ; Is next higher bit set? addlw 0x04 ; Yes, add 4 movwf encindex ; Store in encoder index ; ; End ISR Code. ; Now restore to saved state ; movf PCLATH_Save,w ; Restore saved PCLATH into W movwf PCLATH ; and move W into PCLATH ; movf FSR_Save,w ; Restore saved FSR into W movwf FSR ; and move W into FSR ; swapf Status_Save,w ; Swap status save nibbles, and move into W movwf STATUS ; Move W into STATUS register ; (Bank now in original state) ; swapf W_Save,f ; Swap nibbles within W_Save ; (for next swap instruction) swapf W_Save,w ; Swap nibbles of W_Save, and move into W ; retfie ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>> ENCODER AND FREQUENCY CHANGE ROUTINES <<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: This routine examines the signals sent from the encoder PIC * ; * and determines whether or not a change has occurred. * ; * If no change has occurred (no interrupt has occurred), it will * ; * return to the caller. * ; * If a change has occurred (an interrupt was received), this * ; * routine will determine the direction and the amount of * ; * change. * ; * It will increment or decrement the frequency displayed on the * ; * the LCD and send the updated frequency to the DDS. Then it * ; * will return to the caller. * ; * * ; * Input: * ; * * ; * Output: * ; * * ;***************************************************************************** ; encoder_look ; movf int_stack_count,w ; Get interrupt count btfsc STATUS,Z ; Is count zero? goto enclook_ret ; Yes, return ; ; An interrupt occurred. ; ; We should have only received one interrupt and will process it here. ; If more than one, we lost the encshift count! Detect it here with breakpoints. decf int_stack_count,f ; Decrement interrupt stack count btfsc STATUS,Z ; Is count zero now? goto encoder_none_lost ; If so, we didn't lose any ; ; Start Debug - See on scope if we are losing any interrupts ; bsf PORTD,7 ; Lost one. Set pulse to see on scope ; nop ; nop ; nop ; bsf PORTD,7 ; Clear pulse ; End Debug ; clrf int_stack_count ; Clear count encoder_none_lost ; ; Set encoder acceleration LEDs from encindex (set up by interrupt handler) ; bcf PORTC,LED0 ; Start with the three LEDs cleared bcf PORTD,LED1 ; bcf PORTD,LED2 ; btfsc encindex,0 ; Is bit 0 set? bsf PORTC,LED0 ; Yes, set LED0 btfsc encindex,1 ; Is bit 1 set? bsf PORTD,LED1 ; Yes, set LED1 btfsc encindex,2 ; Is bit 2 set? bsf PORTD,LED2 ; Yes, set LED2 ; ; START DEBUG See how hard it is to get all 3 bits set. ; ; btfss encindex,0 ; Is bit 0 set? ; goto notall ; No, notall ; btfss encindex,1 ; Is bit 1 set? ; goto notall ; No, notall ; btfss encindex,2 ; Is bit 2 set? ; goto notall ; No, notall ; bsf PORTD,7 ; Set debug bit (see on scope) ; nop ; nop ; nop ; bcf PORTD,7 ; Clear debug bit ;notall ; END DEBUG ; ; Now calculate fstep amount based on encshift ; ; Optical Encoder ; ENC ; 2 1 0 ENCINDEX FSTEP - SLOW FSTEP - FAST ; ----- ------- -------------- -------------- ; 0 0 0 0 1 1 ; 0 0 1 1 2 2 ; 0 1 0 2 5 10 ; 0 1 1 3 10 60 ; 1 0 0 4 100 600 ; 1 0 1 5 300 1800 ; 1 1 0 6 1000 6000 ; 1 1 1 7 5000 30000 ; ; Mechanical Encoder ; ENC ; 2 1 0 ENCINDEX FSTEP - SLOW FSTEP - FAST ; ----- ------- -------------- -------------- ; 0 0 0 0 1 1 ; 0 0 1 1 5 7 ; 0 1 0 2 25 50 ; 0 1 1 3 50 300 ; 1 0 0 4 500 3000 ; 1 0 1 5 1500 9000 ; 1 1 0 6 5000 30000 ; 1 1 1 7 25000 150000 ; Set up fstep (number of Hz to move) ; - if Fast Tuning NOT selected (normal), use slow table ; - if Fast Tuning selected, use fast table ; rlf encindex ; Multiply by 4 for start of table rlf encindex ; for this tuning digit movf encindex,W ; Default - slow tuning btfsc VFOcontrol,FAST_TUNING ; Fast tuning selected? addlw 0x20 ; yes, inc past slow tuning table btfsc VFOcontrol,ALTERNATE ; ALTERNATE encoder? addlw 0x40 ; yes, increment to correct table movwf tuning_index ; Save the index of the high byte call fstep_table ; Get high byte value into W movwf fstep_3 ; Save in fstep_3 incf tuning_index,f ; Increment index to next byte movf tuning_index,w ; Get next index into W call fstep_table ; Get next value into W movwf fstep_2 ; Save in fstep_2 incf tuning_index,f ; Increment index to next byte movf tuning_index,w ; Get next index into W call fstep_table ; Get next value into W movwf fstep_1 ; Save in fstep_1 incf tuning_index,f ; Increment index to next byte movf tuning_index,w ; Get next index into W call fstep_table ; Get next value into W movwf fstep_0 ; Save in fstep_0 ; ; Get current frequency to be updated from selected VFO (Afreq or Bfreq) ; and load into ; call VFO_freq_load ; Load with VFO regs btfss VFOcontrol,DIR_UP ; Is it UP direction? goto need_sub ; No, go subtract the step call add_step ; Yes, add step and update goto step_done ; Back to common code need_sub call sub_step ; Subtract a step and update step_done call check_add ; See if band limits reached. If so, adjust, ; and update call VFO_freq_save ; Save updated into ; selected VFO freq area (Afreq or Bfreq) ; ; Now set flag to indicate need to update DDS and LCD ; bsf VFOcontrol,NEEDUPDATE ; Say we need a DDS and LCD update ; enclook_ret ; return ; Return ; ;***************************************************************************** ; * * ; * Purpose: This routine will determine if there are any pushbuttons * ; * pressed. If so, this routine will perform the required action.* ; * NOTE: PUSHBUTTONS ARE LOW ACTIVE * ; * * ; * Input: * ; * * ; * Output: * ; * * ;***************************************************************************** ; buttons_look ; buttons_look_1 ; ; Look for A=B button. (Don't need DDS update for this one) ; btfsc PORTB,PBVFOAEQB ; Is pushbuttonset pressed for A=B ? goto buttons_1_done ; No, done with button ; ; Button A=B is pressed. First find active VFO and set this into other VFO. ; call wait_32ms ; Debounce press of button (v5.19a) btfsc VFOcontrol,VFOA ; Is VFO A selected now? goto buttons_setAtoB ; No, B is selected so set A to B movf Bfreq_0,w ; Get VFO B value movwf Afreq_0 ; and store in VFO A movf Bfreq_1,w ; Get VFO B value movwf Afreq_1 ; and store in VFO A movf Bfreq_2,w ; Get VFO B value movwf Afreq_2 ; and store in VFO A movf Bfreq_3,w ; Get VFO B value movwf Afreq_3 ; and store in VFO A goto buttons_1_wait ; Done buttons_setAtoB movf Afreq_0,w ; Get VFO A value movwf Bfreq_0 ; and store in VFO B movf Afreq_1,w ; Get VFO A value movwf Bfreq_1 ; and store in VFO B movf Afreq_2,w ; Get VFO A value movwf Bfreq_2 ; and store in VFO B movf Afreq_3,w ; Get VFO A value movwf Bfreq_3 ; and store in VFO B ; buttons_1_wait btfss PORTB,PBVFOAEQB ; Is pushbutton released? goto buttons_1_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; buttons_1_done ; ; -------------------------------------------------------- buttons_look_2 ; ; Look for SPLIT button. ; btfsc PORTB,PBSPLIT ; Is pushbutton set for SPLIT ? goto buttons_2_done ; No, button done ; call wait_32ms ; Debounce press of button (v5.19a) ; SPLIT button is set. Wait for release. ; buttons_2_wait btfss PORTB,PBSPLIT ; Is pushbutton released? goto buttons_2_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; ; See if SPLIT already set. If so, this button press is to END SPLIT ; btfss VFOcontrol,SPLIT ; Is SPLIT already set? goto SetSplit ; No, go to set SPLIT bcf VFOcontrol,SPLIT ; Yes, Clear SPLIT control flag bcf PORTB,LEDSPLIT ; Clear LED for SPLIT movlw 0xC0 ; Point LCD at digit 0 of 2nd line call cmnd2LCD ; movlw ' ' ; Clear "SPLIT" on LCD call data2LCD ; movlw ' ' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw ' ' ; call data2LCD ; goto buttons_2_done ; Done SetSplit bsf VFOcontrol,SPLIT ; Button SPLIT is pressed. Set control flag bsf PORTB,LEDSPLIT ; Turn on LED for SPLIT ; movlw 0xC0 ; Point LCD at digit 0 of 2nd line call cmnd2LCD ; movlw 'S' ; Say "SPLIT" on LCD call data2LCD ; movlw 'P' ; call data2LCD ; movlw 'L' ; call data2LCD ; movlw 'I' ; call data2LCD ; movlw 'T' ; call data2LCD ; ; buttons_2_done ; ; -------------------------------------------------------- buttons_look_3 ; btfsc PORTB,PBVFOTOGGLE ; Pushbutton asking for select OTHER VFO? goto buttons_3_done ; No, done with button ; ; Toggle pressed. Change VFO and update ; call wait_32ms ; Debounce press of button (v5.19a) ; btfss VFOcontrol,VFOA ; Is VFO A selected now? goto buttons_select_VFOA ; No, B is selected, so go and select A buttons_select_VFOB bsf VFOcontrol,VFOB ; Yes, select VFO B bcf VFOcontrol,VFOA ; and clear VFO A bcf PORTB,LEDRECVFOA ; Turn off LED for VFO A bsf PORTB,LEDRECVFOB ; Turn on LED for VFO B movlw 0xCB ; Point LCD at digit 11 of 2nd line call cmnd2LCD ; movlw 'V' ; Say "VFO B" call data2LCD ; movlw 'F' ; call data2LCD ; movlw 'O' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'B' ; call data2LCD ; goto toggle_done ; buttons_select_VFOA bsf VFOcontrol,VFOA ; Select VFO A bcf VFOcontrol,VFOB ; and clear VFO B bsf PORTB,LEDRECVFOA ; Turn on LED for VFO A bcf PORTB,LEDRECVFOB ; Turn off LED for VFO B movlw 0xCB ; Point LCD at digit 11 of 2nd line call cmnd2LCD ; movlw 'V' ; Say "VFO A" call data2LCD ; movlw 'F' ; call data2LCD ; movlw 'O' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'A' ; call data2LCD ; toggle_done bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD ; buttons_3_wait btfss PORTB,PBVFOTOGGLE ; Is pushbutton released? goto buttons_3_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; buttons_3_done ; ; -------------------------------------------------------- buttons_look_4 ; ; Check for Calibrate mode pushbutton ; btfsc PORTB,PBCALIBRATE ; Is pushbutton pressed for Calibrate? goto buttons_4_done ; No, done with button ; ; Calibrate pushbutton is set. ; call wait_32ms ; Debounce press of button (v5.19a) ; buttons_4_wait btfss PORTB,PBCALIBRATE ; Is pushbutton released? goto buttons_4_wait ; No, wait for release ; bcf VFOcontrol,SPLIT ; Make sure SPLIT control flag is CLEAR bcf PORTB,LEDSPLIT ; Clear LED for SPLIT too ; call calibrate ; Go adjust osc values. Calibrate calls ; encoder_look to adjust. Calibrate keeps ; looking for calibrate pushbutton to be ; pressed again to release. Then it returns ; here, and calibrate is done. ; ; bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD ; to use new osc values ; call wait_32ms ; Debounce release of button (v5.19a) ; buttons_4_done ; ; -------------------------------------------------------- buttons_look_5 ; ; Look for MHz UP button. ; btfsc PORTA,PBMHZUP ; Is pushbutton pressed for MHz UP ? goto buttons_5_done ; No, done with button ; call wait_32ms ; Debounce press of button (v5.19a) ; buttons_5_wait btfss PORTA,PBMHZUP ; Is pushbutton released? goto buttons_5_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; ; ; MHz UP button is pressed ; (1 MHz = 0xF4240) ; btfsc VFOcontrol,TRANSMIT ; Now transmitting? goto buttons_5_done ; Yes, ignore the MH UP! ; bsf button5set,1 ; Possibly want alt encoder (if pb8 also pushed) ; This flag will be cleared if PB8 is not pushed. ; clrf fstep_3 ; movlw 0x0F ; Set up step of 1 M movwf fstep_2 ; movlw 0x42 ; movwf fstep_1 ; movlw 0x40 ; movwf fstep_0 ; call VFO_freq_load ; Load with VFO regs call add_step ; Add a step and update call check_add ; See if band limits reached. If so, adjust, ; and update call VFO_freq_save ; Save updated into ; selected VFO freq area (Afreq or Bfreq) bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD ; buttons_5_done ; ; -------------------------------------------------------- buttons_look_6 ; ; Look for MHZ DN button. ; btfsc PORTE,PBMHZDN ; Is pushbutton pressed for MHz DN ? goto buttons_6_done ; No, done with button ; call wait_32ms ; Debounce press of button (v5.19a) ; buttons_6_wait btfss PORTE,PBMHZDN ; Is pushbutton released? goto buttons_6_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; ; MHz DN button is pressed ; (1 MHz = 0xF4240) ; btfsc VFOcontrol,TRANSMIT ; Now transmitting? goto buttons_6_done ; Yes, ignore the MH DN! ; clrf fstep_3 ; movlw 0x0F ; Set up step of 1 M movwf fstep_2 ; movlw 0x42 ; movwf fstep_1 ; movlw 0x40 ; movwf fstep_0 ; call VFO_freq_load ; Load with VFO regs call sub_step ; Subtract a step and update call VFO_freq_save ; Save updated into ; selected VFO freq area (Afreq or Bfreq) bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD ; buttons_6_done ; ; -------------------------------------------------------- buttons_look_7 ; ; Look for Fast Tuning Select button. ; btfsc PORTE,PBFTTOGGLE ; Is pushbutton pressed for Fast Tuning TOGGLE ? goto buttons_7_done ; No, done with button ; ; Fast Tuning Select Toggle button is pressed ; call wait_32ms ; Debounce press of button (v5.19a) ; ; See if fast tuning already set? ; btfsc VFOcontrol,FAST_TUNING ; Is Fast Tuning already set? goto buttons_clear_fast_tuning ; Yes, go and clear it bsf VFOcontrol,FAST_TUNING ; No, set Fast Tuning movlw 0x8F ; Point LCD at digit 15 of 1st line call cmnd2LCD ; movlw '+' ; Indicate Fast Tuning with "+" (default) btfsc VFOcontrol,ALTERNATE ; Is alternate encoder selected? movlw '#' ; Indicate Fast Tuning with "#" call data2LCD ; Update LCD goto buttons_7_wait ; and exit buttons_clear_fast_tuning bcf VFOcontrol,FAST_TUNING ; Clear Fast tuning movlw 0x8F ; Point LCD at digit 15 of 1st line call cmnd2LCD ; movlw ' ' ; Clear Fast Tuning indicator call data2LCD ; Update LCD buttons_7_wait btfss PORTE,PBFTTOGGLE ; Is pushbutton released? goto buttons_7_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; buttons_7_done ; ; -------------------------------------------------------- buttons_look_8 ; ; ; Check for Debug pushbutton ; This button must be held down at power-up time (DDS_init) to be useful! ; Pressing button again will release debug mode. ; btfsc PORTE,DEBUG ; Is pushbutton pressed for Debug? goto buttons_8_done ; No, done with button ; ; Debug pushbutton is pressed. ; call wait_32ms ; Debounce press of button (v5.19a) ; btfsc button5set,1 ; Is button5 also pressed at this time? bsf VFOcontrol,ALTERNATE ; Set Alternate encoder in use. ; Only clear is via power down. Too messy! ; See if Debug mode already set? ; btfsc DebugControl,DDS_init_loop ; Is DDS_init_loop already set? goto buttons_clear_init_loop ; Yes, go and clear it bsf DebugControl,DDS_init_loop ; No, set debug mode active goto buttons_8_wait ; buttons_clear_init_loop bcf DebugControl,DDS_init_loop ; Clear DDS init loop active buttons_8_wait btfss PORTE,DEBUG ; Is pushbutton released? goto buttons_8_wait ; No, wait for release ; call wait_32ms ; Debounce release of button (v5.19a) ; buttons_8_done clrf button5set ; Clear button-combo flag every time ; return ; All buttons have been examined ; ;***************************************************************************** ; * * ; * Purpose: This routine will determine if the transmitter is active. * ; * It will set or clear TRANSMIT flag based on the input signal * ; * from transmitter keying circuitry. * ; * * ; * Why can't we just use the transmitter_keyed signal alone, and * ; * forget about setting/clearing TRANSMIT flag? * ; * Because we need to control it and test the flag at several * ; * places in the code. We can't allow the transmit state to * ; * change while we are making decisions. * ; * * ; * Input: A wire comming from the transmitter keying circuitry goes to * ; * an input pin (RA4) of the 16F877. * ; * - This pin is set LOW when transmitting * ; * - This pin is set HIGH when not transmitting * ; * * ; * Output: TRANSMIT (in VFOcontrol) is set or clear, based on * ; * XMIT_Keyed_Low_Active being set or clear * ; * * ;***************************************************************************** ; transmitter_active_look ; ; Detect whether XMIT_keyed pin has just changed on this pass ; btfsc PORTA,XMIT_Keyed_Low_Active goto transmitter_not_being_keyed ; Transmitter is being keyed. See if it's a change from previous pass btfsc VFOcontrol,TRANSMIT ; Was it transmitting on last pass? goto transmit_setup_exit ; Yes, no change, so exit. bsf VFOcontrol,TRANSMIT ; No, set transmit active bsf VFOcontrol,NEEDUPDATE ; We need an update of DDS and LCD ; ; Write to LCD ; movlw 0xC6 ; Point LCD at digit 6 of 2nd line call cmnd2LCD ; movlw 'X' ; Say "XMIT " call data2LCD ; movlw 'M' ; call data2LCD ; movlw 'I' ; call data2LCD ; movlw 'T' ; call data2LCD ; goto transmit_setup_exit ; exit ; transmitter_not_being_keyed btfss VFOcontrol,TRANSMIT ; Was it transmitting on last pass? goto transmit_setup_exit ; No, no change, so exit. bcf VFOcontrol,TRANSMIT ; Yes, clear transmit active bsf VFOcontrol,NEEDUPDATE ; We need an update of DDS and LCD ; ; Write to LCD ; movlw 0xC6 ; Point LCD at digit 6 of 2nd line call cmnd2LCD ; movlw 'R' ; Say "RCV " call data2LCD ; movlw 'C' ; call data2LCD ; movlw 'V' ; call data2LCD ; movlw ' ' ; call data2LCD ; ; transmit_setup_exit return ; Return to caller ; ;***************************************************************************** ; * * ; * Purpose: This routine adds the 32 bit value of fstep to the 32 bit * ; * value in workfreq. When incrementing, the fstep value is a * ; * positive integer. When decrementing, fstep is the complement * ; * of the value being subtracted. * ; * * ; * Input: The 32 bit values in fstep and * ; * * ; * Output: The sum of fstep and freq is stored in freq. When incrementing * ; * this value may exceed the maximum. When decrementing, it may * ; * go negative. * ; * * ;***************************************************************************** ; add_step movf fstep_0,w ; Get low byte of the increment addwf workfreq_0,f ; Add it to the low byte of freq btfss STATUS,C ; Any carry? goto add1 ; No, add next byte incfsz workfreq_1,f ; Ripple carry up to the next byte goto add1 ; No new carry, add next byte incfsz workfreq_2,f ; Ripple carry up to the next byte goto add1 ; No new carry, add next byte incf workfreq_3,f ; Ripple carry up to the highest byte add1 movf fstep_1,w ; Get the next increment byte addwf workfreq_1,f ; Add it to the next higher byte btfss STATUS,C ; Any carry? goto add2 ; No, add next byte incfsz workfreq_2,f ; Ripple carry up to the next byte goto add2 ; No new carry, add next byte incf workfreq_3,f ; Ripple carry up to the highest byte add2 movf fstep_2,w ; Get the next to most significant increment addwf workfreq_2,f ; Add it to the freq byte btfss STATUS,C ; Any carry? goto add3 ; No, add last byte incf workfreq_3,f ; Ripple carry up to the highest byte add3 movf fstep_3,w ; Get the most significant increment byte addwf workfreq_3,f ; Add it to the most significant freq return ; Return to the caller ; ;***************************************************************************** ; * * ; * Purpose: Subtract the increment step from freq, checking that it does * ; * not go below zero. * ; * * ; * Input: The values in fstep and . * ; * * ; * Output: The updated value in freq. * ; * * ;***************************************************************************** ; sub_step comf fstep_0,f ; Subtraction of fstep from comf fstep_1,f ; freq is done by adding the comf fstep_2,f ; twos compliment of fstep to comf fstep_3,f ; freq. incfsz fstep_0,f ; Increment last byte goto comp_done ; Non-zero, continue incfsz fstep_1,f ; Increment next byte goto comp_done ; Non-zero, continue incfsz fstep_2,f ; Increment next byte goto comp_done ; Non-zero, continue incf fstep_3,f ; Increment the high byte comp_done call add_step ; Add the compliment to do the subtraction ; ; If the frequency has gone negative, clear it to zero. ; btfss workfreq_3,7 ; Is high order frequency byte "negative"? goto exit2 ; No, keep going set_min clrf workfreq_0 ; Yes, set the frequency to zero clrf workfreq_1 ; clrf workfreq_2 ; clrf workfreq_3 ; exit2 return ; Return to the caller ; ;***************************************************************************** ; * * ; * Purpose: Check if freq exceeds the upper limit (30 MHz). * ; * * ; * Input: The 32 bit values in * ; * * ; * Output: If freq is below the limit, it is unchanged. Otherwise, it is * ; * set to equal the upper limit. * ; * * ;***************************************************************************** ; check_add ; ; Check the most significant byte. ; movlw 0xFF-limit_3 ; Get (FF - limit of high byte) addwf workfreq_3,w ; Add it to the current high byte btfsc STATUS,C ; Was high byte too large? goto set_max ; Yes, apply limit movlw limit_3 ; Get high limit value subwf workfreq_3,w ; Subtract the limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the second most significant byte. ; movlw 0xFF-limit_2 ; Get (FF - limit of next byte) addwf workfreq_2,w ; Add it to the current byte btfsc STATUS,C ; Is the current value too high? goto set_max ; Yes, apply the limit movlw limit_2 ; Second limit byte subwf workfreq_2,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the third most significant byte. ; movlw 0xFF-limit_1 ; Get (FF - limit of next byte) addwf workfreq_1,w ; Add it to the current byte btfsc STATUS,C ; Is the current value too high? goto set_max ; Yes, apply the limit movlw limit_1 ; Third limit byte subwf workfreq_1,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the least significant byte. ; movlw limit_0 ; Fourth limit byte subwf workfreq_0,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. set_max movlw limit_0 ; Get least significant limit movwf workfreq_0 ; Set it in freq movlw limit_1 ; Get the next byte limit movwf workfreq_1 ; Set it in freq_1 movlw limit_2 ; Get the next byte limit movwf workfreq_2 ; Set it in freq_2 movlw limit_3 ; Get the most significant limit movwf workfreq_3 ; Set it in freq_3 exit1 return ; Return to the caller ; ;***************************************************************************** ; * * ; * Purpose: Load with with frequency of the * ; * selected RECEIVE VFO. * ; * * ; * Input: VFO_control flag byte set up * ; * or Bfreq_0:Bfreq_3> set up * ; * * ; * Output: set up * ; * * ;***************************************************************************** ; VFO_freq_load btfss VFOcontrol,VFOA ; Is VFO A selected ? goto VFO_freq_load_B ; No, VFO_freq_load_B movf Afreq_0,w ; Yes, set up for VFO A in freq registers movwf workfreq_0 movf Afreq_1,w movwf workfreq_1 movf Afreq_2,w movwf workfreq_2 movf Afreq_3,w movwf workfreq_3 goto VFO_freq_load_exit VFO_freq_load_B movf Bfreq_0,w ; Set up for VFO B in freq registers movwf workfreq_0 movf Bfreq_1,w movwf workfreq_1 movf Bfreq_2,w movwf workfreq_2 movf Bfreq_3,w movwf workfreq_3 VFO_freq_load_exit return ; ;***************************************************************************** ; * * ; * Purpose: Save into frequency registers for * ; * the selected VFO * ; * * ; * Input: VFO_control flag byte set up * ; * set up * ; * * ; * Output: or Bfreq_0:Bfreq_3> set up * ; * * ;***************************************************************************** ; VFO_freq_save btfss VFOcontrol,VFOA ; Is VFO A selected ? goto VFO_freq_save_B ; No, VFO_freq_save_B movf workfreq_0,w ; Yes, save updated workfreq registers movwf Afreq_0 ; in VFO A movf workfreq_1,w movwf Afreq_1 movf workfreq_2,w movwf Afreq_2 movf workfreq_3,w movwf Afreq_3 goto VFO_freq_save_exit VFO_freq_save_B movf workfreq_0,w ; Save updated workfreq registers movwf Bfreq_0 ; in VFO B movf workfreq_1,w movwf Bfreq_1 movf workfreq_2,w movwf Bfreq_2 movf workfreq_3,w movwf Bfreq_3 VFO_freq_save_exit return ; ;***************************************************************************** ; * * ; * Purpose: Update the DDS and LCD. * ; * This routine is called after a change has been detected, * ; * either via the encoder or a pushbutton. * ; * * ; * 1) Update . * ; * 2) Display this frequency on the LCD * ; * 3) Send this frequency to the DDS * ; * * ; * Input: VFOcontrol flag word set up * ; * or set up * ; * * ; * Output: set up * ; * LCD updated * ; * DDS updated * ; * NEEDUPDATE flag cleared * ; * * ;***************************************************************************** ; update_DDS_LCD btfsc VFOcontrol,VFOA ; Is VFO A selected for rx ? goto select_AR ; Yes, use VFO_A for receive ; ;----- VFO B on receive--------------------------------------------------------- ; select_BR btfsc VFOcontrol,SPLIT ; Are we operating SPLIT? goto BRsplit_check_transmit ; Yes, go check for reverse ; ; VFO B receive, not split, so just select B ; goto select_DDS_VFO_B ; Go set DDS to use VFO B ; ; VFO B receive, SPLIT, so check for transmit ; BRsplit_check_transmit btfsc VFOcontrol,TRANSMIT ; Now transmitting? goto select_DDS_VFO_A ; Yes, set up DDS to use A goto select_DDS_VFO_B ; Not transmitting, so set up DDS to use B ; ;----- VFO A on receive--------------------------------------------------------- ; select_AR btfsc VFOcontrol,SPLIT ; Are we operating SPLIT? goto ARsplit_check_transmit ; Yes, go check for reverse ; ; VFO A receive, not split, so select A ; select_VFO_A goto select_DDS_VFO_A ; Go set DDS to use VFO A ; ; VFO A receive, SPLIT, so check for transmit ; ARsplit_check_transmit btfsc VFOcontrol,TRANSMIT ; Now transmitting? goto select_DDS_VFO_B ; Yes, set up DDS to use B goto select_DDS_VFO_A ; No, set up DDS to use A ; ; ---- Select the DDS VFO -------------------------------------------------------- ; ; Set up for DDS and LCD routines ; select_DDS_VFO_A movf Afreq_0,w ; Set up VFO A save into active freq registers movwf freq_0 ; movf Afreq_1,w ; movwf freq_1 ; movf Afreq_2,w ; movwf freq_2 ; movf Afreq_3,w ; movwf freq_3 ; ; ; bcf PORTD,DDS_select_freq2 ; Clear 16F877 signal for pin for AD9854 pin 29 ; movlw 0xCB ; Point LCD at digit 11 of 2nd line call cmnd2LCD ; movlw 'V' ; Say "VFO A" call data2LCD ; movlw 'F' ; call data2LCD ; movlw 'O' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'A' ; call data2LCD ; goto select_VFO_exit ; and exit ; select_DDS_VFO_B movf Bfreq_0,w ; Set up VFO B save into active freq registers movwf freq_0 ; movf Bfreq_1,w ; movwf freq_1 ; movf Bfreq_2,w ; movwf freq_2 ; movf Bfreq_3,w ; movwf freq_3 ; ; ; bsf PORTD,DDS_select_freq2 ; Set 16F877 signal for pin for AD9854 pin 29 ; movlw 0xCB ; Point LCD at digit 11 of 2nd line call cmnd2LCD ; movlw 'V' ; Say "VFO B" call data2LCD ; movlw 'F' ; call data2LCD ; movlw 'O' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'B' ; call data2LCD ; ; select_VFO_exit ; call bin2BCD ; Convert the frequency to BCD call show_freq ; Display the frequency on the LCD call calc_dds_word ; Find the control word for the DDS chip call send_dds_freq ; Send the control word to the DDS chip ; bcf VFOcontrol,NEEDUPDATE ; Clear needupdate flag ; return ; ;***************************************************************************** ; * * ; * Purpose: This routine increments through the band table each time the * ; * knob moves a notch, updating the LCD and DDS, until the band * ; * button is no longer pushed. * ; * * ; * Input: The value of the band push button and the encoder bits * ; * * ; * Output: Updated freq value, and new frequency on the LCD and DDS. * ; * * ;***************************************************************************** ; ;change_band ; btfsc last_dir,1 ; Are we going up in the band list? ; goto band_up ; Yes, increment band address ; movlw 0x04 ; No, get 4 bytes to subtract ; subwf band,f ; Move down in band list ; movlw 0xFF-band_end ; Check to see if we have fallen off the ; addwf band,w ; bottom of the table. ; btfss STATUS,C ; Off the bottom? ; goto valid ; No, continue ; movlw band_end ; Yes, go to highest entry ; movwf band ; ;valid ; call get_band ; Get the new band frequency ; goto write_LCD_DDS ; Set the frequency and continue ;band_up ; movlw 0x04 ; Table entries are 4 bytes apart ; addwf band,f ; Increment the band pointer ; movlw 0xFF-band_end ; Check to see if we have gone over the ; addwf band,w ; top of the table. ; btfsc STATUS,C ; Did we go over the top of the table? ; clrf band ; Yes, go to the bottom entry ; call get_band ; Get the new band frequency ;write_LCD_DDS ; call bin2BCD ; Convert the frequency to BCD ; call show_freq ; Display the frequency on the LCD ; call calc_dds_word ; Find the control word for the DDS chip ; call send_dds_freq ; Send the control word to the DDS chip ; return ; Return ; ; ;***************************************************************************** ; * * ; * Purpose: This routine reads the frequency value of a band table entry * ; * pointed to by band and returns it in workfreq_3...workfreq_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 workfreq_3 ; Save it in workfreq_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 workfreq_2 ; Save it in workfreq_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 workfreq_1 ; Save it in workfreq_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 workfreq_0 ; Save it in workfreq_0 ; movlw 0x03 ; Get a constant three ; subwf band,f ; Restore original value of band ; return ; Return to the caller ; ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>>> DDS ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: Multiply the 32 bit number for oscillator frequency times the * ; * 32 bit number for the displayed frequency. * ; * * ; * * ; * Input: The reference oscillator value in osc_3 ... osc_0 and the * ; * current frequency stored in freq_3...freq_0. * ; * The reference oscillator value is treated as a fixed point * ; * real, with a 24 bit mantissa. * ; * * ; * Output: The result is stored in AD9854_3 ... AD9854_0. * ; * * ;***************************************************************************** ; calc_dds_word clrf AD9854_0 ; Clear the AD9854 control word bytes clrf AD9854_1 ; clrf AD9854_2 ; clrf AD9854_3 ; clrf AD9854_4 ; movlw 0x20 ; Set count to 32 (4 osc bytes of 8 bits) movwf mult_count ; Keep running count movf osc_0,w ; Move the four osc bytes movwf osc_temp_0 ; to temporary storage for this multiply movf osc_1,w ; (Don't disturb original osc bytes) movwf osc_temp_1 ; movf osc_2,w ; movwf osc_temp_2 ; movf osc_3,w ; movwf osc_temp_3 ; mult_loop bcf STATUS,C ; Start with Carry clear btfss osc_temp_0,0 ; Is bit 0 (Least Significant bit) set? goto noAdd ; No, don't need to add freq term to total movf freq_0,w ; Yes, get the freq_0 term addwf AD9854_1,f ; and add it in to total btfss STATUS,C ; Does this addition result in a carry? goto add7 ; No, continue with next freq term incfsz AD9854_2,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incfsz AD9854_3,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incf AD9854_4,f ; Yes, add one and continue add7 movf freq_1,w ; Use the freq_1 term addwf AD9854_2,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add8 ; No, continue with next freq term incfsz AD9854_3,f ; Yes, add one and check for another carry goto add8 ; No, continue with next freq term incf AD9854_4,f ; Yes, add one and continue add8 movf freq_2,w ; Use the freq_2 term addwf AD9854_3,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add9 ; No, continue with next freq term incf AD9854_4,f ; Yes, add one and continue add9 movf freq_3,w ; Use the freq_3 term addwf AD9854_4,f ; Add freq term to total in correct position noAdd rrf AD9854_4,f ; Shift next multiplier bit into position rrf AD9854_3,f ; Rotate bits to right from byte to byte rrf AD9854_2,f ; rrf AD9854_1,f ; rrf AD9854_0,f ; rrf osc_temp_3,f ; Shift next multiplicand bit into position rrf osc_temp_2,f ; Rotate bits to right from byte to byte rrf osc_temp_1,f ; rrf osc_temp_0,f ; decfsz mult_count,f ; One more bit has been done. Are we done? goto mult_loop ; No, go back to use this bit clrf AD9854_4 ; Yes, clear _4. Answer is in bytes _3 .._0 return ; Done. ; ;**************************************************************************** ; * * ; * Purpose: This routine sends the AD9854 control word to the DDS chip * ; * using a serial data transfer. * ; * * ; * Input: AD9854_3 ... AD9854_0 * ; * Note: Obviously this is a 32 bit value used as input. * ; * The 32 bit value will be used as the upper 32 bits of * << ; * the AD9854's 48-bit frequency control register. Then * << ; * two bytes of zero will be sent for the last 16 bits. * << ; * (48 bit accuracy is not needed in this application.) * << ; * * ; * Output: The AD9854 48-bit DDS frequency control register is updated. * << ; * * ;**************************************************************************** ; send_dds_freq ; ; Send the command word and the frequency value. ; bsf PORTD,DDS_ioreset ; Pulse the I/O reset line call wait_10_inst ; Wait 10 instructions bcf PORTD,DDS_ioreset ; ; movlw freq_write_cmd ; Get the command byte to load frequency movwf byte2send ; Pass it call send_DDS_byte ; Send it movf AD9854_3,w ; Get the most significant byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movf AD9854_2,w ; Get the next byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movf AD9854_1,w ; Get the next byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movf AD9854_0,w ; Get the least significant byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw 0x00 ; Get zero for last two bytes movwf byte2send ; Pass it call send_DDS_byte ; Send it call send_DDS_byte ; Send it again ; bsf PORTD,DDS_ioud ; Raise the IO update call wait_10_inst ; Wait 10 instructions bcf PORTD,DDS_ioud ; Drop the IO update ; return ; Return to the caller ; ; ;***************************************************************************** ; * * ; * Purpose: Send a byte to the AD9854 through the serial interface. * ; * * ; * Input: byte2send is the byte to transfer to the DDS, most significant * ; * bit first. * ; * * ; * Output: None * ; * * ;***************************************************************************** ; send_DDS_byte ; movlw 0x08 ; Get the loop count movwf bit_count ; Set counter next_DDS_bit bsf PORTD,DDS_data ; Guess we are sending a one rlf byte2send,f ; Test if next bit is 1 or zero btfss STATUS,C ; Was it zero? bcf PORTD,DDS_data ; Yes, clear data line nop nop nop nop nop nop bsf PORTD,DDS_clk ; Toggle write clock nop nop nop nop nop nop bcf PORTD,DDS_clk ; ; decfsz bit_count,f ; Has the whole byte been sent? goto next_DDS_bit ; No, keep going return ; ;***************************************************************************** ; * * ; * Purpose: Read a CR byte from the AD9854 * ; * * ; * Input: None * ; * * ; * Output: byte2read in W register. It is the byte transferred from the * ; * DDS, most significant bit first. * ;***************************************************************************** ; read_DDS_byte clrf CR_work ; movlw 0x08 ; Get the loop count movwf bit_count ; Set counter read_next_DDS_bit bcf STATUS,C ; Clear Carry bit (default) bsf PORTD,DDS_clk ; Toggle write clock nop nop ; ; Set up Carry bit with data btfsc PORTD,DDS_data ; Skip next if Data = 0 bsf STATUS,C ; Data = 1, so set Carry bit nop nop bcf PORTD,DDS_clk ; Clear write clock ; Now rotate Carry bit into work byte rlf CR_work,f ; Rotate carry bit into LS bit decfsz bit_count,f ; Has the whole byte been read? goto read_next_DDS_bit ; No, keep going ; movf CR_work,W ; Move final byte into W ; return ; ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>>> LCD ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: 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. * ; * * ; * Input: The value in freq_0 ... freq_3 * ; * * ; * Output: The BCD number in BCD_0 ... BCD_4 * ; * * ;***************************************************************************** ; bin2BCD movlw 0x20 ; Set loop counter movwf BCD_count ; to 32 clrf BCD_0 ; Clear output clrf BCD_1 ; " " clrf BCD_2 ; " " clrf BCD_3 ; " " clrf BCD_4 ; " " bin_loop bcf STATUS,C ; Clear carry bit in STATUS ; ; Rotate bits in freq bytes. Move from LS byte (freq_0) to next byte freq_1). ; Likewise, move from freq_1 to freq_2 and from freq_2 to freq_3. ; rlf freq_0,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlf freq_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_3,f ; Rotate left, Carry->LS bit, MS bit->Carry btfsc STATUS,C ; Is Carry clear? If so, skip next instruction bsf freq_0,0 ; Carry is set so wrap and set bit 0 in freq_0 ; ; Build BCD bytes. Move into LS bit of BCD bytes (LS of BCD_0) from MS bit of ; freq_3 via the Carry bit. ; rlf BCD_0,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_3,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_4,f ; Rotate left, Carry->LS bit, MS bit->Carry decf BCD_count,f ; Decrement loop count btfss STATUS,Z ; Is loop count now zero? goto adjust ; No, go to adjust return ; Yes, EXIT ;============================================================================ adjust ; Internal subroutine, called by bin2BCD main loop only ; ; As BCD bytes are being built, make sure the nibbles do not grow larger than 9. ; If a nibble gets larger than 9, increment to next higher nibble. ; (If the LS nibble of a byte overflows, increment the MS nibble of that byte.) ; (If the MS nibble of a byte overflows, increment the LS nibble of next byte.) ; movlw BCD_0 ; Get pointer to BCD_0 movwf FSR ; Put pointer in FSR for indirect addressing call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_1 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_2 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_3 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_4 call adj_BCD ; goto bin_loop ; Back to main loop of bin2BCD ;============================================================================ adj_BCD ; Internal subroutine, called by adjust only movlw 3 ; Add 3 addwf INDF,w ; to LS digit movwf BCD_temp ; Save in temp btfsc BCD_temp,3 ; Is LS digit + 3 > 7 (Bit 3 set) movwf INDF ; Yes, save incremented value as LS digit movlw 0x30 ; Add 3 addwf INDF,w ; to MS digit movwf BCD_temp ; Save as temp btfsc BCD_temp,7 ; Is MS digit + 3 > 7 (Bit 7 set) movwf INDF ; Yes, save incremented value as MS digit return ; Return to adjust subroutine ; ;***************************************************************************** ; * * ; * Purpose: Display the frequency setting on the LCD. * ; * Initial value is 14,025.000 kHz (14 characters) * ; * * ; * Input: The values in BCD_4 ... BCD_0 * ; * * ; * Output: The number displayed on the LCD * ; * * ;***************************************************************************** ; show_freq ; ============================================================================ ; Using for 16x1 LCD. (Actually 2 8-char lines, side by side.) ; Line 1 (left) addresses are 0x00 to 0x07 (0x80 to 0x87) ; Line 1 (right) addresses are 0x40 to 0x47 (0xC0 to 0xC7) ; Using for 16x2 LCD. ; Line 1 (left) addresses are 0x00 to 0x07 (0x80 to 0x8F) ; Line 1 (right) addresses are 0x08 to 0x0F (0x90 to 0x9F) ; Line 2 (left) addresses are 0x40 to 0x47 (0xC0 to 0xCF) ; Line 2 (right) addresses are 0x48 to 0x4F (0xD0 to 0xDF) ; ; Command: 1aaaaaaa (to position cursor to address aaaaaaa) ; Use 0x81 to position cursor to address 0x01 ; (We only display 14 characters so indent by 1) ; ============================================================================ movlw 0x81 ; Point the LCD to first LCD digit location call cmnd2LCD ; Send starting digit location to LCD ; ; Running 4-bit mode, so need to send Most Significant Nibble first. ; ; Extract and send "XXXX" from byte containing "XXXXYYYY" ; - Swap halves to get YYYYXXXX ; - Mask with 0x0F to get 0000XXXX ; - Add ASCII bias (0030XXXX) ; swapf BCD_3,w ; Swap 10MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) btfss STATUS,Z ; Is this digit a zero? goto digit_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_it ; and go send it digit_non_zero addlw 0x30 ; Add offset for ASCII char set (0030XXXX) send_it call data2LCD ; Send byte in W to LCD ; ; Extract and send "YYYY" from byte containing "XXXXYYYY" ; - Mask with 0x0F to get 0000YYYY ; - Add offset for ASCII character set in LCD (0030YYYY) ; movf BCD_3,w ; Put 1MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; movlw ',' ; Get a comma call data2LCD ; Send byte in W to LCD ; swapf BCD_2,w ; Swap 100KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; movf BCD_2,w ; Put 10KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; swapf BCD_1,w ; Swap 1KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; movlw '.' ; Set up W with ASCII Period call data2LCD ; Send data byte in W to LCD ; ================================================================================= ; For 16x1 LCD, need to move cursur to address 0x40 for the 9th character position ; Command: 1aaaaaaa (to position cursor to address aaaaaaa) ; movlw 0xC0 ; Point to LCD digit to address 0x40 for char 9 ; call cmnd2LCD ; Send command byte in W to LCD ;================================================================================== ; For 16x2 LCD, don't move cursor address. Just continue for 9th character positon ; ================================================================================= movf BCD_1,w ; Put 100 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send data byte in W to LCD ; swapf BCD_0,w ; Swap 10 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send data byte in W to LCD ; movf BCD_0,w ; Put 1 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; movlw ' ' ; Send a space call data2LCD ; to LCD ; movlw 'k' ; Send a 'k' call data2LCD ; to LCD ; movlw 'H' ; Send an "H" call data2LCD ; to LCD ; movlw 'z' ; Send a 'z' call data2LCD ; to LCD ; return ; ; ***************************************************************************** ; * * ; * Purpose: Check if LCD is done with the last operation. * ; * This subroutine polls the LCD busy flag to determine if * ; * previous operations are completed. * ; * * ; * Input: None * ; * * ; * Output: PORTC set as all outputs * ; ***************************************************************************** ; busy_check ; ; First we need to save the state of LED0 since it happens to be on the same ; port as the LCD and it is going to get destroyed when the entire port is ; cleared by this routine. ; bcf RememberLED0,0 ; Default - LED0 is clear btfsc PORTC,LED0 ; Is LED0 clear? bsf RememberLED0,0 ; No - save LED0 as set ; ; Now ready to clear the port and set up for busy check. ; clrf PORTC ; Clear all outputs on PORTC bcf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 1 movlw b'11110000' ; Set as inputs, outputs movwf TRISC ; via Tristate bcf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 0 bcf PORTC,LCD_rs ; Set up LCD for Read Busy Flag (RS = 0) bsf PORTC,LCD_rw ; Set up LCD for Read (RW = 1) movlw 0xFF ; Set up constant 255 movwf timer1 ; for timer loop counter LCD_is_busy bsf PORTC,LCD_e ; Set E high nop ; Wait nop ; nop ; nop ; movf PORTC,w ; Read PORTC into W movwf LCD_read ; Save W for later testing bcf PORTC,LCD_e ; Drop E again nop ; Wait at least .450 us nop ; (Each NOP instruction takes about .2 us nop ; when using 20 MHz clock) nop ; bsf PORTC,LCD_e ; Pulse E high (dummy read of lower nibble), nop ; Wait at least .450 us nop ; (Each NOP instruction takes about .2 us nop ; when using 20 MHz clock) nop ; bcf PORTC,LCD_e ; and drop E again decf timer1,f ; Decrement loop counter btfsc STATUS,Z ; Is loop counter down to zero? goto bailout ; If yes, bailout (end regardless) btfsc LCD_read,7 ; Is Busy Flag (RC7) in save byte clear? goto LCD_is_busy ; If not, it is busy so jump back goto not_busy ; Not busy bailout bsf PORTD,LED2 ; Set LED 2 - Interrupt bailout occurred not_busy clrf PORTC ; Clear all of Port C (inputs and outputs) bcf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 1 movlw 0x00 ; Set up to enable PORTC data pins movwf TRISC ; All pins (RC7..RC0) are back to outputs bcf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 0 ; ; Now restore LED0 to the state it was in at the start of this routine ; bcf PORTC,LED0 ; Clear LED0 (default) btfsc RememberLED0,0 ; Was LED0 clear when saved? bsf PORTC,LED0 ; No, set LED0 ; return ; ; ; ***************************************************************************** ; * Purpose: Send Command or Data byte to the LCD * ; * Entry point cmnd2LCD: Send a Command to the LCD * ; * Entry Point data2LCD: Send a Data byte to the LCD * ; * * ; * Input: W has the command or data byte to be sent to the LCD. * ; * * ; * Output: None * ; ***************************************************************************** ; cmnd2LCD ; ****** Entry point ****** movwf LCD_char ; Save byte to write to LCD clrf rs_value ; Remember to clear RS (clear rs_value) bcf PORTC,LCD_rs ; Set RS for Command to LCD goto write2LCD ; Go to common code data2LCD ; ****** Entry point ******** movwf LCD_char ; Save byte to write to LCD bsf rs_value,0 ; Remember to set RS (set bit 0 of rs_value) bsf PORTC,LCD_rs ; Set RS for Data to LCD write2LCD call busy_check ; Check to see if LCD is ready for new data bcf PORTC,LCD_rw ; Set LCD back to Write mode (RW = 0) bcf PORTC,LCD_rs ; Guess RS should be clear btfsc rs_value,0 ; Should RS be clear? (is bit 0 == 0?) bsf PORTC,LCD_rs ; No, set RS ; ; Transfer Most Significant nibble (XXXX portion of XXXXYYYY) ; movlw 0x0F ; Set up mask andwf PORTC,f ; Clear old movf LCD_char,w ; Put byte of data into W andlw 0xF0 ; Mask to give XXXX0000 in W iorwf PORTC,f ; Send to without changing bsf PORTC,LCD_e ; Pulse the E line high, nop ; Wait at least .450 us nop ; (Each NOP instruction takes about .2 us nop ; when using 20 MHz clock) nop ; bcf PORTC,LCD_e ; Now drop it again ; ; Transfer Least Significant nibble (YYYY portion of XXXXYYYY) ; movlw 0x0F ; Set up mask andwf PORTC,f ; Clear old swapf LCD_char,w ; Move LS nibble of data to MS position in W andlw 0xF0 ; Mask to give YYYY0000 in W iorwf PORTC,f ; Send to without changing bsf PORTC,LCD_e ; Pulse the E line high, nop ; Wait at least .450 us nop ; (Each NOP instruction takes about .2 us nop ; when using 20 MHz clock) nop ; bcf PORTC,LCD_e ; and drop it again return ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>> EEPROM ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ; ***************************************************************************** ; * * ; * Purpose: Write the byte of data at EEDATA to the EEPROM at address * ; * EEADR and then increment the address. * ; * * ; * Input: The values at EEDATA and EEADR. * ; * * ; * Output: The EEPROM value is updated. * ; * Exit in Bank 0 ! * ; * * ; ***************************************************************************** ; write_EEPROM bsf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 3 bsf EECON1,WREN ; Set the EEPROM write enable bit bcf EECON1,EEPGD ; Point to data memory (not program memory) (16f877) ; Start required sequence bcf INTCON, GIE ; Disable interrupts movlw 0x55 ; Write 0x55 and 0xAA to EEPROM movwf EECON2 ; control register, as required movlw 0xAA ; for the write movwf EECON2 ; bsf EECON1,WR ; Set WR to initiate write bsf INTCON, GIE ; Enable interrupts again ; End required sequence bit_check btfsc EECON1,WR ; Has the write completed? goto bit_check ; No, keep checking bcf EECON1,WREN ; Clear the EEPROM write enable bit ;bsf STATUS,RP1 ; Select bank 2 bcf STATUS,RP0 ; (was 3) incf EEADR,f ; Increment the EE write address bcf STATUS,RP1 ; Select bank 0 ;bcf STATUS,RP0 ; (was 2) return ; Return to the caller ; ;***************************************************************************** ; * * ; * Purpose: Read a byte of EEPROM data at address EEADR into EEDATA * ; * and then increment the address. * ; * * ; * Input: The address EEADR. * ; * * ; * Output: EEDATA value in W * ; * Exit in Bank 0 ! * ; * * ;***************************************************************************** ; read_EEPROM bsf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 3 bcf EECON1,EEPGD ; Point to DATA memory (new - 16f877) bsf EECON1,RD ; Request the read EE nop ; Any instruction here is ignored as program nop ; memory is read in third cycle after BSF EECON1,RD ;bsf STATUS,RP1 ; Select bank 2 bcf STATUS,RP0 ; (was 3) incf EEADR,f ; Increment the read address movf EEDATA,w ; W = EEDATA bcf STATUS,RP1 ; Select bank 0 ;bcf STATUS,RP0 ; (was 2) return ; Return to the caller ; ; ;***************************************************************************** ; * * ; * Purpose: Read the CR bytes from the AD9854 into PIC memory (DEBUG ONLY) * ; * Need to add Trigger with some button press. * ; * * ; * Note: AD9854 APP note says "Reads are not guaranteed at 100 MHz as * ; * they are intended for software debug only." * ; * * ; * Need to enhance this to write the data to the LCD. * ; * Currently only works with Debugger, looking at registers. * ; * (Not fully debugged and guaranteed to work.) * ; * * ; * Input: None * ; * * ; * Output: CR Bytes in PIC memory * ; * Bank 0 selected * ; * * ;***************************************************************************** ; read_CR_bytes ; ; Pulse the IO reset ; bsf PORTD,DDS_ioreset ; Raise the IO reset nop ; Wait a bit nop ; And again nop ; And again bcf PORTD,DDS_ioreset ; Drop the IO reset nop nop nop nop ; Clear Input CR registers movlw 0xFF movwf CR_in0 movwf CR_in1 movwf CR_in2 movwf CR_in3 ; ; Send the command to Read the CR register. ; movlw CR_read_cmd ; Get the read CR bytes command movwf byte2send ; Pass it call send_DDS_byte ; Send it ; bcf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 1 movlw 0x10 ; Tristate port D movwf TRISD ; to all outputs except DDS_Data (RD4) as input bcf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 0 ; call read_DDS_byte ; Read first CR byte movwf CR_in0 ; Save it ; call read_DDS_byte ; Read second CR byte movwf CR_in1 ; Save it ; call read_DDS_byte ; Read third CR byte movwf CR_in2 ; Save it ; call read_DDS_byte ; Read fourth CR byte movwf CR_in3 ; Save it ; bcf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 1 clrf TRISD ; Set port D back to all outputs bcf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 0 ; return ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>> INITIALIZATION ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: Power on initialization of 16F877. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; init_PIC call wait_128ms ; Wait for stable environment ; clrf INTCON ; Disable interrupts until we are ready ; bcf STATUS,RP1 ; Select bsf STATUS,RP0 ; bank 1 ; bsf OPTION_REG,NOT_RBPU ; Disable weak pullups (LOW ACTIVE) movlw 00111111b ; (5,4,3,2,1,0) = inputs movwf TRISA ; to Port A (bits 7,6 don't exist) movlw 00110111b ; (5,4,2,1,0) = inputs, (7,6,3) = outputs movwf TRISB ; to Port B movlw 00000000b ; (7,6,5,4,3,2,1,0) = outputs movwf TRISC ; to Port C clrf TRISD ; Set Port D to all outputs movlw 00000111b ; (2, 1, 0) = inputs movwf TRISE ; to Port E (bits 7,6,5,4,3 don't exist) movlw 00000111b ; Set 0111 in PCFG3:PCFG0 for all movwf ADCON1 ; digital pins RA0-RA5 and RE0-RE2 bcf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 0 ; bcf PORTC,LED0 ; Clear LED 0 bcf PORTD,LED1 ; Clear LED 1 bcf PORTD,LED2 ; Clear LED 2 bsf PORTB,LEDRECVFOA ; Turn on LED for VFO A bcf PORTB,LEDRECVFOB ; Turn off LED for VFO B ; bcf PORTB,LEDSPLIT ; Clear LED ; clrf encindex ; Initialize clrf int_stack_count ; Initialize interrupt stack count bcf VFOcontrol,NEEDUPDATE ; Clear needupdate flag bcf VFOcontrol,TRANSMIT ; Clear transmitting flag bcf VFOcontrol,SPLIT ; Start with split off bcf VFOcontrol,ALTERNATE ; Start with primary encoder selected clrf DebugControl ; Clear debug control word ; bcf PORTD,DDS_select_freq2 ; Clear it (use freq1 only) (unused) return ; return ; ;***************************************************************************** ; * * ; * Purpose: Power on initialization of Liquid Crystal Display. The LCD * ; * controller chip must be equivalent to an Hitachi 44780. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; init_LCD bsf PORTD,LED1 ; Set LED - Entering Init LCD code ; call wait_64ms ; Wait for LCD to power up movlw 0x30 ; LCD init instruction (First) movwf PORTC ; Send to LCD via bsf PORTC,LCD_e ; Set the LCD E line high, call wait_64ms ; wait a "long" time bcf PORTC,LCD_e ; and then Clear E movlw 0x30 ; LCD init instruction (Second) movwf PORTC ; Send to LCD via RC7..RC0 bsf PORTC,LCD_e ; Set E high, call wait_32ms ; wait a while, bcf PORTC,LCD_e ; and then Clear E movlw 0x30 ; LCD init instruction (Third) movwf PORTC ; Send to LCD via bsf PORTC,LCD_e ; Set E high, call wait_32ms ; wait a while, bcf PORTC,LCD_e ; and then Clear E movlw 0x20 ; 4-bit mode instruction movwf PORTC ; Send to LCD via bsf PORTC,LCD_e ; Set E high, call wait_16ms ; wait a while, bcf PORTC,LCD_e ; and then Clear E movlw 0x28 ; 1/16 duty cycle, 5x8 matrix call cmnd2LCD ; Send command in w to LCD movlw 0x08 ; Display off, cursor and blink off call cmnd2LCD ; Send command to LCD movlw 0x01 ; Clear and reset cursor call cmnd2LCD ; Send command in w to LCD movlw 0x06 ; Set cursor to move right, no shift call cmnd2LCD ; Send command in w to LCD movlw 0x0C ; Display on, cursor and blink off call cmnd2LCD ; Send command in w to LCD ; bcf PORTD,LED1 ; Clear LED 0 - Leaving Init LCD code ; return ; ; ;***************************************************************************** ; * * ; * Purpose: Display microcode revision and other info on LCD for 2 seconds * ; * upon power-up * ; * * ; * Input: MCODE_REV_0 through MCODE_REV_4 set up * ; * * ; * Output: LCD displays debug info * ; * * ;***************************************************************************** ; display_mcode_version movlw 0x80 ; Point LCD at char 0 of first line call cmnd2LCD ; movlw ' ' ; Clear char 0 of first line call data2LCD ; movlw 'H' ; Char 1 of first line call data2LCD ; movlw 'F' ; Char 2 of first line call data2LCD ; movlw 'V' ; Char 3 of first line call data2LCD ; movlw 'F' ; Char 4 of first line call data2LCD ; movlw 'O' ; Char 5 of first line call data2LCD ; movlw ' ' ; Char 6 of first line call data2LCD ; movlw 'V' ; Char 7 of first line call data2LCD ; movlw 'e' ; Char 8 of first line call data2LCD ; movlw 'r' ; Clear char 9 of first line call data2LCD ; movlw MCODE_REV_0 ; Get mcode rev byte call data2LCD ; and display it movlw MCODE_REV_1 ; Get mcode rev byte call data2LCD ; and display it movlw MCODE_REV_2 ; Get mcode rev byte call data2LCD ; and display it movlw MCODE_REV_3 ; Get mcode rev byte call data2LCD ; and display it movlw MCODE_REV_4 ; Get mcode rev byte call data2LCD ; and display it movlw ' ' ; char 15 call data2LCD ; ; ; Now display second line on LCD ; movlw 0xC0 ; Point LCD at char 0 of second line call cmnd2LCD ; movlw 'C' ; CLK starts at char 0 of second line call data2LCD ; movlw 'l' ; call data2LCD ; movlw 'k' ; call data2LCD ; movlw ':' ; call data2LCD ; movlw CLOCK_DISPLAY_0 ; Get first clock display byte call data2LCD ; and display it movlw CLOCK_DISPLAY_1 ; Get second clock display byte call data2LCD ; and display it movlw CLOCK_DISPLAY_2 ; Get third clock display byte call data2LCD ; and display it movlw ' ' ; Clear char 7 of second line call data2LCD ; movlw ' ' ; Clear char 8 of second line call data2LCD ; movlw 'E' ; Char 9 of second line call data2LCD ; movlw 'n' ; Char 10 of second line call data2LCD ; movlw 'c' ; Char 11 of second line call data2LCD ; movlw ':' ; Char 12 of second line call data2LCD ; btfsc VFOcontrol,ALTERNATE ; Is alternate encoder selected? goto DisplayAlternate ; Yes, go to display alternate movlw ENCODER_DISPLAY_0 ; Get first encoder display byte call data2LCD ; and display it movlw ENCODER_DISPLAY_1 ; Get next encoder display byte call data2LCD ; and display it movlw ENCODER_DISPLAY_2 ; Get next encoder display byte call data2LCD ; and display it goto DisplayEncoderDone ; DisplayAlternate movlw ENCODER_DISPLAY_0A ; Get first encoder display byte call data2LCD ; and display it movlw ENCODER_DISPLAY_1A ; Get next encoder display byte call data2LCD ; and display it movlw ENCODER_DISPLAY_2A ; Get next encoder display byte call data2LCD ; and display it DisplayEncoderDone ; call wait_a_sec ; Wait one second call wait_a_sec ; Wait one second ; ; Now clear parts of the second line that need clearing. ; movlw 0xC0 ; Point LCD at char 0 of second line call cmnd2LCD ; movlw ' ' ; Clear char 0 of second line call data2LCD ; movlw ' ' ; Clear char 1 of second line call data2LCD ; movlw ' ' ; Clear char 2 of second line call data2LCD ; movlw ' ' ; Clear char 3 of second line call data2LCD ; movlw ' ' ; Clear char 4 of second line call data2LCD ; movlw ' ' ; Clear char 5 of second line call data2LCD ; ; return ;***************************************************************************** ; * * ; * Purpose: Initialize the AD9854 by sending the CR register bit settings. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; init_DDS bsf PORTD,LED2 ; Set LED - Entering Init DDS code call wait_8ms ; ; ; Pulse the Master Reset to initialize the DDS chip. ; bsf PORTD,DDS_mreset ; Raise the master reset call wait_8ms ; Wait a long time bcf PORTD,DDS_mreset ; Drop the master reset call wait_8ms ; Wait a long time ; ; Now pulse the IO reset ; bsf PORTD,DDS_ioreset ; Raise the IO reset call wait_8ms ; Wait a long time bcf PORTD,DDS_ioreset ; Drop the IO reset ; ; Send the command to Write the CR register. ; movlw CR_write_cmd ; Get the write CR bytes command movwf byte2send ; Pass it call send_DDS_byte ; Send it ; ; Send the four CR bytes. ; movlw CR_byte_0 ; Get the first DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_1 ; Get the second DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_2 ; Get the third DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_3 ; Get the 4th DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it ; ; External Update is now set. The 4th byte probably not sent, ; so need a new IO Reset and new command ; ; Now pulse the IO reset to start a new command ; bsf PORTD,DDS_ioreset ; Raise the IO reset call wait_8ms ; Wait a long time bcf PORTD,DDS_ioreset ; Drop the IO reset ; ; Send the command to Write the CR register again. ; movlw CR_write_cmd ; Get the write CR bytes command movwf byte2send ; Pass it call send_DDS_byte ; Send it ; ; Send the four CR bytes again. ; movlw CR_byte_0 ; Get the first DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_1 ; Get the second DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_2 ; Get the third DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it movlw CR_byte_3 ; Get the 4th DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it ; ; Now we need to send external update to make the entire Init "take" ; bsf PORTD,DDS_ioud ; Raise the IO update call wait_10_inst ; Wait 10 instructions bcf PORTD,DDS_ioud ; Drop the IO update ; call buttons_look ; Look at buttons to possibly clear debug mode btfsc DebugControl,DDS_init_loop ; Is Debug mode set? goto init_DDS ; Yes, loop forever (clear by power down) ; bcf PORTD,LED2 ; Clear LED - Leaving Init DDS code ; return ; Return to the caller ; ;***************************************************************************** ; * * ; * Purpose: Initialize default frequency and constants. * ; * Get the reference oscillator constant from the EEPROM. * ; * (Add code later to tweak these values in EEPROM with a * ; * calibrate routine.) * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; init_freq ; bcf VFOcontrol,FAST_TUNING ; Start by fast tuning off (Normal tuning) ; bsf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 2 clrf EEADR ; Set the EEPROM read address to zero ; call read_EEPROM ; Read EEPROM and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_0 ; Save osc frequency ; call read_EEPROM ; Get next byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_1 ; Save it ; call read_EEPROM ; Get the third byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_2 ; Save it ; call read_EEPROM ; Get the fourth byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_3 ; Save it ; ; Set the power on frequency to the defined value. ; movlw default_0 ; Get the least significant byte movwf freq_0 ; Save it movwf Afreq_0 ; Save it movwf Bfreq_0 ; Save it movlw default_1 ; Get the next byte movwf freq_1 ; Save it movwf Afreq_1 ; Save it movwf Bfreq_1 ; Save it movlw default_2 ; And the next movwf freq_2 ; Save it movwf Afreq_2 ; Save it movwf Bfreq_2 ; Save it movlw default_3 ; Get the most significant byte movwf freq_3 ; Save it movwf Afreq_3 ; Save it movwf Bfreq_3 ; Save it ; bsf VFOcontrol,VFOA ; Set VFO A selected for rx ; ; Write to LCD ; movlw 0xC6 ; Point LCD at digit 6 of 2nd line call cmnd2LCD ; movlw 'R' ; Say "RCV " call data2LCD ; movlw 'C' ; call data2LCD ; movlw 'V' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'V' ; Say "VFO A" call data2LCD ; movlw 'F' ; call data2LCD ; movlw 'O' ; call data2LCD ; movlw ' ' ; call data2LCD ; movlw 'A' ; call data2LCD ; ; return ; Return ; ;***************************************************************************** ; * * ; * Purpose: Change INTCON bits to allow interrupts. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; allow_interrupts bcf INTCON,INTF ; Clear any RB0 interrupt that may now exist bsf INTCON,GIE ; Global interrupt enable bsf INTCON,INTE ; RB0 interrupt enable return ; Return ; ;***************************************************************************** ; * * ; * Purpose: This routine is entered if the Calibrate push button is * ; * pressed. "10,000.00 CAL" is displayed on the LCD, and the * ; * the DDS chip is programmed to produce 10 MHz, based on the * ; * osc value stored in the EEPROM. As long as calibrate mode is * ; * active, the osc value is slowly altered to allow the output * ; * to be trimmed to exactly 10 MHz. Once the Calibrate push * ; * button is pressed again, the new osc value is stored in * ; * the EEPROM and normal operation begins. * ; * * ; * Input: The original osc constant in EEPROM * ; * * ; * Output: The corrected osc constant in EEPROM * ; * * ;***************************************************************************** ; calibrate ; movlw 0x80 ; Set frequency to 10MHz by movwf freq_0 ; setting freq to the binary equivalent movlw 0x96 ; of 10,000,000. movwf freq_1 ; . movlw 0x98 ; . movwf freq_2 ; . movlw 0x00 ; . movwf freq_3 ; . ; ; Read the starting reference oscillator value from EEPROM. ; bsf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 2 clrf EEADR ; Set the EEPROM read address to zero call read_EEPROM ; Read EEPROM and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_0 ; Save osc frequency call read_EEPROM ; Get next byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_1 ; Save it call read_EEPROM ; Get the third byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_2 ; Save it call read_EEPROM ; Get the fourth byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_3 ; Save it call bin2BCD ; Calculate BCD version of 10,000.00 call show_freq ; Display the frequency on the LCD movlw 0xC0 ; Point LCD at digit 0 of 2nd line call cmnd2LCD ; movlw 'C' ; Send a C call data2LCD ; movlw 'A' ; Send an A call data2LCD ; movlw 'L' ; Send an L call data2LCD ; movlw ' ' ; Send an SPACE call data2LCD ; movlw ' ' ; Send an SPACE call data2LCD ; calibrate_loop call calc_dds_word ; Calculate DDS value based on current osc call send_dds_freq ; Update the DDS calibrate_wait_for_int movf int_stack_count,w ; Get interrupt stack count btfss STATUS,Z ; Any interrupts? goto calibrate_interrupt_occurred ; Yes, go process the change ; ; Check to see if Calibrate pushbutton has been pressed again (to end calibrate) ; btfsc PORTB,PBCALIBRATE ; Is Calibrate pushbutton pressed for CAL release? goto calibrate_wait_for_int ; No, loop back - wait for interrupt calibrate_release_wait btfss PORTB,PBCALIBRATE ; pushbutton released yet goto calibrate_release_wait ; No, wait for release goto calibrate_done ; Yes, go end calibrate ; calibrate_interrupt_occurred clrf int_stack_count ; Clear count (start over with interrupts) clrf fstep_3 ; Clear the three most significant clrf fstep_2 ; bytes of fstep clrf fstep_1 ; movlw 0x10 ; Assume that we are adjusting slowly movwf fstep_0 ; Use small increment (default) movf encindex,w ; Get amount into w btfsc STATUS,Z ; Is it zero goto update_osc ; Yes, go use default movlw 0x80 ; No, then use the large increment movwf fstep_0 ; update_osc btfsc PORTA,ENC3 ; Is direction bit clear? (indicating DN) goto calibrate_increase ; No, go and increase the osc value ; Yes, decrease it comf fstep_0,f ; Subtraction of fstep is done by comf fstep_1,f ; adding the twos compliment of fstep comf fstep_2,f ; to osc comf fstep_3,f ; incfsz fstep_0,f ; Increment last byte goto calibrate_increase ; Non-zero, continue incfsz fstep_1,f ; Increment next byte goto calibrate_increase ; Non-zero, continue incfsz fstep_2,f ; Increment next byte goto calibrate_increase ; Non-zero, continue incf fstep_3,f ; Increment the high byte calibrate_increase movf fstep_0,w ; Get the low byte increment addwf osc_0,f ; Add it to the low osc byte btfss STATUS,C ; Was there a carry? goto add4 ; No, add the next bytes incfsz osc_1,f ; Ripple carry up to the next byte goto add4 ; No new carry, add the next bytes incfsz osc_2,f ; Ripple carry up to the next byte goto add4 ; No new carry, add the next bytes incf osc_3,f ; Ripple carry up to the highest byte add4 movf fstep_1,w ; Get the second byte increment addwf osc_1,f ; Add it to the second osc byte btfss STATUS,C ; Was there a carry? goto add5 ; No, add the third bytes incfsz osc_2,f ; Ripple carry up to the next byte goto add5 ; No new carry, add the third bytes incf osc_3,f ; Ripple carry up to the highest byte add5 movf fstep_2,w ; Get the third byte increment addwf osc_2,f ; Add it to the third osc byte btfss STATUS,C ; Was there a carry? goto add6 ; No, add the fourth bytes incf osc_3,f ; Ripple carry up to the highest byte add6 movf fstep_3,w ; Get the fourth byte increment addwf osc_3,f ; Add it to the fourth byte ; ; Check to see if Calibrate pushbutton has been pressed again (to end calibrate) ; btfsc PORTB,PBCALIBRATE ; Is Calibrate pushbutton pressed for release? goto calibrate_loop ; No, stay in calibrate mode ; calibrate_done ; ; Write final value to EEPROM bsf STATUS,RP1 ; Select bcf STATUS,RP0 ; bank 2 clrf EEADR ; Set the EEPROM write address to zero bcf STATUS,RP1 ; Select bank 0 ;bcf STATUS,RP0 ; (was 2) movf osc_0,w ; Record the first osc byte bsf STATUS,RP1 ; Select bank 2 ;bcf STATUS,RP0 ; (was 0) movwf EEDATA ; Set up osc byte as EEDATA call write_EEPROM ; Write it to EEPROM and increment address ; (Sets up Bank3, EEDATA to EEPROM, exits in Bank0) movf osc_1,w ; Record the second bsf STATUS,RP1 ; Select bank 2 ;bcf STATUS,RP0 ; (was 0) movwf EEDATA ; Set up osc byte as EEDATA call write_EEPROM ; Write it to EEPROM and increment address ; (Sets up Bank3, EEDATA to EEPROM, exits in Bank0) movf osc_2,w ; Record the third bsf STATUS,RP1 ; Select bank 2 ;bcf STATUS,RP0 ; (was 0) movwf EEDATA ; Set up osc byte as EEDATA call write_EEPROM ; Write it to EEPROM and increment address ; (Sets up Bank3, EEDATA to EEPROM, exits in Bank0) movf osc_3,w ; Record the fourth bsf STATUS,RP1 ; Select bank 2 ;bcf STATUS,RP0 ; (was 0) movwf EEDATA ; Set up osc byte as EEDATA call write_EEPROM ; Write it to EEPROM and increment address ; (Sets up Bank3, EEDATA to EEPROM, exits in Bank0) movlw 0xC0 ; Point LCD at digit 0 of 2nd line call cmnd2LCD ; movlw ' ' ; Clear it call data2LCD ; movlw ' ' ; Clear it call data2LCD ; movlw ' ' ; Clear it call data2LCD ; return ; Return to the caller ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>> GENERAL USAGE ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * * ; * Purpose: Wait for a specified number of instructions or milliseconds. * ; * * ; * Entry point wait_10_inst : Wait for 10 instructions * ; * Entry point wait_20_inst : Wait for 20 instructions * ; * Entry point wait_a_sec : Wait for 1 second * ; * Entry point wait_256ms : Wait for 256 msec * ; * Entry point wait_128ms : Wait for 128 msec * ; * Entry point wait_64ms : Wait for 64 msec * ; * Entry point wait_32ms : Wait for 32 msec * ; * Entry point wait_16ms : Wait for 16 msec * ; * Entry point wait_8ms : Wait for 8 msec * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ;***************************************************************************** ; wait_20_inst ; ***** Entry point ****** nop nop nop nop nop nop nop nop nop nop ; fall through wait_10_inst ; ***** Entry point ****** nop nop nop nop nop nop nop nop return ; (call and return account for 2 instructions) ;========================================================= wait_a_sec ; ***** Entry point ****** call wait_256ms call wait_256ms call wait_256ms call wait_256ms return ;========================================================= wait_256ms ; ***** Entry point ****** call wait_128ms call wait_128ms return ;========================================================= wait_128ms ; ***** Entry point ****** call wait_64ms call wait_64ms return ; ========================================================= wait_64ms ; ****** Entry point ****** movlw 0xFF ; Set up outer loop goto outer_loop ; Go to wait loops wait_32ms ; ****** Entry point ****** movlw 0x80 ; Set up outer loop to 64 goto outer_loop ; Go to wait loops wait_16ms ; ****** Entry point ****** movlw 0x40 ; Set up outer loop to 32 goto outer_loop ; Go to wait loops wait_8ms ; ****** Entry point ****** movlw 0x20 ; Set up outer loop to 16 ; Fall through into wait loops ; ; Wait loops used by other wait routines ; - .2 microsecond per instruction cycle ; (with a 20 MHz microprocessor clock) ; (.05 microseconds per clock) * (4 clocks per instruction cycle) ; - Most instructions take 1 cycle, but some take two. ; - (255 * 5) = 1275 instruction cycles per inner loop ; - 1275 instruction cycles * .2us/cycle = 255 us per outer loop ; (Measured inner loop time is 270 us.) ; - Divide required time by ms to get timer1 value ; For example, 8ms / .255ms = 31.3; use 0x20 for timer1 in wait_8ms ; movwf timer1 ; Store outer loop count outer_loop movlw 0xFF ; Set up inner loop counter movwf timer2 ; to 255 inner_loop nop ; 1 instruction cycle nop ; 1 instruction cycle decfsz timer2,f ; Decrement inner loop counter (1 cycle) goto inner_loop ; Inner loop counter down to zero ? ; No, go back to inner loop again (2 cycles) decfsz timer1,f ; Yes, decrement outer loop counter goto outer_loop ; Outer loop counter down to zero ? ; No, then go back to outer loop again return ; Yes, return to caller ; END