; ################# SELECT ONE OF THESE AND TURN THE OTHER OFF !! ########## ;#define InterfacePIC ; Select the PIC #define DriverPIC ; ; #define Debugger 0 ; #define OpticalEncoder ; errorlevel -302 ; Turn off all bank warnings ; ; *************************************************************************** ; * * ; * IQPro - VFO using an AD9854 for Direct Digital Synthesis * ; * * ; * Version 1.112 * ; * February 23, 2008 * ; * * ; *************************************************************************** ; ; Author - Craig Johnson, AAØZZ ; ; Description: ; This code is for the two PIC16F877 microcontrollers which are used to control ; a VFO using an Analog Devices AD9854 DDS. ; ; In the July, 1997 issue of QEX magazine, Curtis Preuss, 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 version of the AD9854 used in this project has a 200 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 for frequency selection and it still uses an LCD for ; displaying the frequency. However, the IQPro VFO has been changed in ; these ways: ; ; 1) The IQPro VFO now uses two PIC microcontrollers - one controlling the ; user interface (optical encoder, pushbuttons, and LEDs) and the other ; controlling the AD9854, the LCD, and the band-switching relay interface. ; 2) The IQPro VFO now uses a two-line LCD so more information is displayed. ; 3) The IQPro 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, ; MiniR2, or R2Pro by Rick Campbell, KK7B.) ; ; Early versions of this VFO project (WB2V follow-on) 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 were being pressed. Since ; the single PIC needed to monitor the input signals from the optical encoder, ; detecting the number of changes in the "gray code" to determine how fast the ; encoder was being turned, the variable size main execution loop made accurate ; timing and smooth operation very difficult to accomplish. ; ; In the next version of project, which I developed with Bruce Stough, AAØED, ; we decided to use a dedicated PIC16F628 microcontroller to handle the ; optical encoder. Why? They are inexpensive and provide a rather elegant ; solution to the problem of accurate timing for smooth tuning operation. ; ; It is changed even more in the IQPro. Now there are two 16F877 PICs. One is ; defined as the Interface PIC and the other the Driver PIC. The encoder logic ; has been moved into the Interface PIC. The Interface PIC monitors the ; "gray code" output of the encoder and periodically sends information to the ; Driver PIC (by way of an encoder message) to indicate that how far the encoder ; shaft has been turned since the last update as well as the direction of turn. ; The Interface PIC sends this data to the Driver PIC by setting up the ; tick-count (up to 6 bits - indicating the number of encoder "transitions" ; detected since the last update) and the direction bit in a message byte and ; then sending this encoder message to the Driver PIC. When the Driver PIC gets ; the encoder message, it extracts the tick-count and direction from the message ; number, updates the working registers with the new frequency, and sets a flag ; that indicates the DDS and LCD need updating. A very short time later, code ; in the main execution loop of the Driver PIC will detect the "changed" flag ; and call the appropriate routines to update the frequency in the DDS and the ; LCD. ; ; The Interface PIC also handles the pushbuttons. When a pushbutton is ; pressed, the Interface PIC detects it and sends a "message" to the Driver PIC, ; causing the Driver PIC to take the appropriate action. Similarly, whenever ; the Driver PIC needs to turn an LED on or off, it sends a "message" to the ; Interface PIC and the Interface PIC performs that action. ; ; The Driver PIC sets up output signals on an header when the VFO frequency ; changes to a different band. These signals are intended to set/reset band relays ; to activate band-specific filters for a transmitter output. ; ; For further details and documentation regarding this project, please see ; the construction manual on my web page: www.cbjohns.com/aa0zz ; ; The latest version of the code for the two PIC microcontrollers can also be ; found on the web site. ; ;***************************************************************************** ; ; PIC16F877-B (Interface PIC) ; __________ ; +5v------!MCLR |1 40| RB7-O---- (Rsvd) PB_ROW3 ; PB_COL0------I-RA0 |2 39| RB6-O---- (Rsvd) PB_ROW2 ; PB_COL1------I-RA1 |3 38| RB5-O---- PB_ROW1 ; PB_COL2------I-RA2 |4 37| RB4-O---- PB_ROW0 ; PB_COL3------I-RA3 |5 36| RB3-O---- (Rsvd) ; PB_COL4------I-RA4 |6 35| RB2-O---- (Unused) ; PB_COL5------I-RA5 |7 34| RB1-O---- (Unused) ; ENCODER_B------I-RE0 |8 33| RB0-O---- DR PIC RESET ; ENCODER_A------I-RE1 |9 32| VDD------ +5v ; (Unused)------O-RE2 |10 31| VSS------ GND ; +5v--------VDD |11 30| RD7-O---- (Unused) ; GND--------VSS |12 29| RD6-O---- (Unused) ; XTAL-------OSC1 |13 28| RD5-O---- LED5 (LEDSPLIT) ; XTAL-------OSC2 |14 27| RD4-O---- LED6 (LEDFTUNE) ; (LEDVFOB)LED4------O-RC0 |15 26| RC7-O---- LED7 (LEDCAL) ; (LEDVFOA)LED3------O-RC1 |16 25| RC6-O---- LED8 (LEDERRI) ; (LEDUSB)LED2------O-RC2 |17 24| RC5-I---- ACKIN (DR ACKOUT RD0) ; (LEDLSB)LED1------O-RC3 |18 23| RC4-I---- DATAIN (DR DATAOUT RD1) ; ACKOUT (DR ACKIN RC5)----O-RD0 |19 22| RD3-I---- CLOCKIN (DR CLOCKOUT RD2) ; DATAOUT(DR DATAIN RC4)---O-RD1 |20 21| RD2-O---- CLOCKOUT (DR CLOCKIN RD3) ; ---------- ; ; PIC16F877-A (Driver PIC) ; __________ ; (INT RB0) +5v------!MCLR |1 40| RB7-O---- (Rsvd) BANDRELAY5 (LSB-LNA) ; (LCD pin 11) LCD_DB4---O/I-RA0 |2 39| RB6-O---- (Rsvd) BANDRELAY4 (mid-LNA) ; (LCD pin 12) LCD_DB5---O/I-RA1 |3 38| RB5-O---- BANDRELAY3 (MSB-LNA) ; (LCD pin 13) LCD_DB6---O/I-RA2 |4 37| RB4-O---- BANDRELAY2 (LSB-LPF) ; (LCD pin 14) LCD_DB7/B-O/I-RA3 |5 36| RB3-O---- (Rsvd) BANDRELAY1 (mid-LPF) ; (LCD pin 6) LCD_e-------O-RA4 |6 35| RB2-O---- BANDRELAY0 (MSB-LPF) ; (LCD pin 5) LCD_rw------O-RA5 |7 34| RB1-O---- IQRelayReset ; (LCD pin 4) LCD_rs------O-RE0 |8 33| RB0/INT-O-IQRelaySet ; (Unused)------O-RE1 |9 32| VDD------ +5v ; (Unused)------O-RE2 |10 31| VSS------ GND ; +5v--------VDD |11 30| RD7-O---- DDS_mreset (AD9854 pin 71) ; GND--------VSS |12 29| RD6-O---- DDS_ioreset(AD9854 pin 17) ; XTAL-------OSC1 |13 28| RD5-O---- DDS_data (AD9854 pin 19) ; XTAL-------OSC2 |14 27| RD4-O---- DDS_ioud (AD9854 pin 20) ; Backlight-----O-RC0 |15 26| RC7-O---- DDS_clk (AD9854 pin 21) ; (Unused)-----O-RC1 |16 25| RC6-I---- XMIT_Keyed_Low_Active ; (Unused)-----O-RC2 |17 24| RC5-I---- ACKIN (INT ACKOUT RD0) ; (LEDERRD) LED9-----O-RC3 |18 23| RC4-I---- DATAIN (INT DATAOUT RD1) ;ACKOUT (INT ACKIN RC5)----O-RD0 |19 22| RD3-I---- CLOCKIN (INT CLOCKOUT RD2) ;DATAOUT(INT DATAIN RC4)---O-RD1 |20 21| RD2-O---- CLOCKOUT (INT CLOCKIN RD3) ; ---------- ; ; PUSHBUTTONS ; COL 0 COL 1 COL 2 COL 3 COL 4 COL 5 ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; ROW 0 | 1 | | 2 | | 3 | |UP MHz | |SPL Tog | |A/B Tog | ; | MSG 00 | | MSG 01 | | MSG 02 | | MSG 03 | | MSG 04 | | MSG 05 | ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; ROW 1 | 4 | | 5 | | 6 | |DN MHz | | Mode | | A=B | ; | MSG 10 | | MSG 11 | | MSG 12 | | MSG 13 | | MSG 14 | | MSG 15 | ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; ROW 2 | 7 | | 8 | | 9 | |UP Band | |Tone Set| | FT Tog | ; | MSG 20 | | MSG 21 | | MSG 22 | | MSG 23 | | MSG 24 | | MSG 25 | ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; ROW 3 | * | | 0 | | # | |DN Band | |Backlight |Calibrate ; | MSG 30 | | MSG 31 | | MSG 32 | | MSG 33 | | Toggle | | MSG 35 | ; +---------+ +--------+ +--------+ +--------+ +--------+ +--------+ ; ; ; LEDs ; +---------+ +---------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ ; | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | ; | LSB/CW- | | USB/CW+ | | VFOA | | VFOB | | SPLIT | | FTUNE | | CAL | | ERR-A | | ERR-B | ; +---------+ +---------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ ; ; *************************************************************************** ; * Device type and options. * ; *************************************************************************** ; processor PIC16F877 ; include "p16f877.inc" ; Include only the definitions that are needed radix dec ; ; ICD-2 debugger needs: START=0x70 END=0x70 (SHARED data bank - first byte) ; START=0x1E5 END=0x1EF (Data bank - top bytes of bank 3) ; START=0x1F00 END=0x1FFF (Code bank- top of PAGE 3) ; ;========================================================================== ; ; Configuration Bits for 16F877 (OBSOLETE) ; ;========================================================================== ;_CP_ALL EQU H'0FCF' ; Bit 13,12, 5,4 = 00 00 ;_CP_HALF EQU H'1FDF' ; Bit 13,12, 5,4 = 01 01 ;_CP_UPPER_256 EQU H'2FEF' ; Bit 13,12, 5,4 = 10 10 ;_CP_OFF EQU H'3FFF' ; Bit 13,12, 5,4 = 11 11 ;_DEBUG_ON EQU 0x37FF ; Bit 11 = 0 ;_DEBUG_OFF EQU H'3FFF' ; Bit 11 = 1 ;_WRT_ENABLE_ON EQU H'3FFF' ; Bit 9 = 1 ;_WRT_ENABLE_OFF EQU H'3DFF' ; Bit 9 = 0 ;_CPD_OFF EQU H'3FFF' ; Bit 8 = 1 ;_CPD_ON EQU H'3EFF' ; Bit 8 = 0 ;_LVP_ON EQU H'3FFF' ; Bit 7 = 1 ;_LVP_OFF EQU H'3F7F' ; Bit 7 = 0 ;_BODEN_ON EQU H'3FFF' ; Bit 6 = 1 ;_BODEN_OFF EQU H'3FBF' ; Bit 6 = 0 ;_PWRTE_OFF EQU H'3FFF' ; Bit 3 = 1 ;_PWRTE_ON EQU H'3FF7' ; Bit 3 = 0 ;_WDT_ON EQU H'3FFF' ; Bit 2 = 1 ;_WDT_OFF EQU H'3FFB' ; Bit 2 = 0 ;_RC_OSC EQU H'3FFF' ; Bit 1,0 = 11 ;_HS_OSC EQU H'3FFE' ; Bit 1,0 = 10 ;_XT_OSC EQU H'3FFD' ; Bit 1,0 = 01 ;_LP_OSC EQU H'3FFC' ; Bit 1,0 = 00 ; ; __config _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_OFF & _WDT_OFF & _HS_OSC ; ;========================================================================== ; ; Configuration Bits for 16F877A ; ;========================================================================== _CP_ALL EQU H'1FFF' ; Bit 13=0 _CP_OFF EQU H'3FFF' ; Bit 13=1 _DEBUG_OFF EQU H'3FFF' ; Bit 11=1 _DEBUG_ON EQU H'37FF' ; Bit 11=0 _WRT_OFF EQU H'3FFF' ; Bit 10,9 = 11 ; No prog memmory write protection _WRT_256 EQU H'3DFF' ; Bit 10,9 = 10 ; First 256 prog memmory write protected _WRT_1FOURTH EQU H'3BFF' ; Bit 10,9 = 01 ; First quarter prog memmory write protected _WRT_HALF EQU H'39FF' ; Bit 10,9 = 00 ; First half memmory write protected _CPD_OFF EQU H'3FFF' ; Bit 8 = 1 _CPD_ON EQU H'3EFF' ; Bit 8 = 0 _LVP_ON EQU H'3FFF' ; Bit 7 = 1 _LVP_OFF EQU H'3F7F' ; Bit 7 = 0 _BODEN_ON EQU H'3FFF' ; Bit 6 = 1 _BODEN_OFF EQU H'3FBF' ; Bit 6 = 0; _PWRTE_OFF EQU H'3FFF' ; Bit 3 = 1 _PWRTE_ON EQU H'3FF7' ; Bit 3 = 0 _WDT_ON EQU H'3FFF' ; Bit 2 = 0 _WDT_OFF EQU H'3FFB' ; Bit 2 = 0 _RC_OSC EQU H'3FFF' ; Bit 1,0 = 11 _HS_OSC EQU H'3FFE' ; Bit 1,0 = 10 _XT_OSC EQU H'3FFD' ; Bit 1,0 = 01 _LP_OSC EQU H'3FFC' ; Bit 1,0 = 00 ; __config _CP_OFF & _DEBUG_OFF & _WRT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_OFF & _WDT_OFF & _HS_OSC ; ; Info for Power-on display on LCD ; MCODE_REV_0 equ '1' ; Current microcode revision level 1.112 MCODE_REV_1 equ '.' MCODE_REV_2 equ '1' MCODE_REV_3 equ '1' MCODE_REV_4 equ '2' ; CLOCK_DISPLAY_0 equ '1' ; Clock Speed = 125 MHz CLOCK_DISPLAY_1 equ '2' CLOCK_DISPLAY_2 equ '5' ; #ifdef OpticalEncoder ENCODER_DISPLAY_0 equ '1' ; Encoder - 128 positions ENCODER_DISPLAY_1 equ '2' ENCODER_DISPLAY_2 equ '8' #else ENCODER_DISPLAY_0A equ ' ' ; Mechanical Encoder - 24 pos. ENCODER_DISPLAY_1A equ '2' ENCODER_DISPLAY_2A equ '4' #endif ; ;========================================================================== ; ; 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 PCLATH EQU 0x000A INTCON EQU 0x000B ; *************************************************************************** ; * Bank 0 file registers: * ; *************************************************************************** PORTA EQU 0x0005 PORTB EQU 0x0006 PORTC EQU 0x0007 PORTD EQU 0x0008 PORTE EQU 0x0009 PIR1 EQU 0x000C PIR2 EQU 0x000D TMR1L EQU 0x000E TMR1H EQU 0x000F T1CON EQU 0x0010 ; ; *************************************************************************** ; * Bank 1 file registers: * ; *************************************************************************** OPTION_REG EQU 0x0081 TRISA EQU 0x0085 TRISB EQU 0x0086 TRISC EQU 0x0087 TRISD EQU 0x0088 TRISE EQU 0x0089 PIE1 EQU 0x008C 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 NTO EQU 0x0004 NPD EQU 0x0003 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 ; #ifdef InterfacePIC ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ####################### ###################### ; ####################### INTERFACE PIC ###################### ; ####################### ###################### ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ;***************************************************************************** ; * General equates. * ;***************************************************************************** ; Equates for HeartbeatFlags byte INT_OCCURRED equ 0 ; Bit indicates an interrupt occurred HEARTBEAT_NEEDED equ 1 ; Bit indicates 10 interrupts occurred so HB needed OUTSTANDING equ 2 ; Set when heartbeat sent, cleared when any msg rcvd ; ; Equates for encoder_msg ENCMSG equ 7 ; Bit indicates it's an encoder message ENCDIR equ 6 ; Direction. If set, clockwise. ; ; Equates for comm_flags ; Inter-PIC communications RCVINPROGRESS equ 0 ; Comm - Receive in progress SENDINPROGRESS equ 1 ; Comm - Send in progress ; *************************************************************************** ; * Assign names to IO pins. * ; *************************************************************************** ; ; PORTA bits (INTERFACE PIC): ; TRISA - 0x3F ; PB_COL0 equ 0x00 ; Input PB_COL1 equ 0x01 ; Input PB_COL2 equ 0x02 ; Input PB_COL3 equ 0x03 ; Input PB_COL4 equ 0x04 ; Input PB_COL5 equ 0x05 ; Input ; 0x06 - Does not exist in 16F877 ; 0x07 - Does not exist in 16F877 ; ; PORTB bits (INTERFACE PIC): ; TRISB - 0x00 ; RESET equ 0x00 ; Output (Reset the Driver PIC) ; equ 0x01 ; Output (Unused) ; equ 0x02 ; Output (Unused) ; equ 0x03 ; Output (Used by ICD) PB_ROW0 equ 0x04 ; Output PB_ROW1 equ 0x05 ; Output PB_ROW2 equ 0x06 ; Output (Used by ICD) PB_ROW3 equ 0x07 ; Output (Used by ICD) ; ; PORTC bits (INTERFACE PIC): ; TRISC - 0x30 ; LEDVFOB equ 0x00 ; Output LED4 LEDVFOA equ 0x01 ; Output LED3 LEDUSB equ 0x02 ; Output LED2 LEDLSB equ 0x03 ; Output LED1 DATAIN equ 0x04 ; Input - Inter-PIC communications ACKIN equ 0x05 ; Input - Inter-PIC communications LEDERRI equ 0x06 ; Output LED8 LEDCAL equ 0x07 ; Output LED7 ; ; PORTD bits (INTERFACE PIC): ; TRISD - 0x08 ; ACKOUT equ 0x00 ; Output - Inter-PIC communications DATAOUT equ 0x01 ; Output - Inter-PIC communications CLOCKOUT equ 0x02 ; Output - Inter-PIC communications CLOCKIN equ 0x03 ; Input - Inter-PIC communications LEDFTUNE equ 0x04 ; Output - LED6 LEDSPLIT equ 0x05 ; Output - LED5 ; equ 0x06 ; (Unused) ; equ 0x07 ; (Unused) ; ; PORTE bits (INTERFACE PIC): ; TRISE - 0x03 ; ENCODER_A equ 0x00 ; Input ENCODER_B equ 0x01 ; Input ; 0x02 ; Output (Unused) ; 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 ; TIM5MSLOW EQU 0x57 ; Low byte for 5 ms timer TIM5MSHIGH EQU 0x9E ; High byte for 5 ms timer TIM5MSPRESCALE EQU 0x01 ; Set 1:1 Prescale for 5 ms timer ; TIM10MSLOW EQU 0xAF ; Low byte for 10 ms timer TIM10MSHIGH EQU 0x3C ; High byte for 10 ms timer TIM10MSPRESCALE EQU 0x01 ; Set 1:1 Prescale for 10 ms timer ; TIM20MSLOW EQU 0xAF ; Low byte for 20 ms timer TIM20MSHIGH EQU 0x3C ; High byte for 20 ms timer TIM20MSPRESCALE EQU 0x11 ; Set 1:2 Prescale for 20 ms timer ; TIM25MSLOW EQU 0xDB ; Low byte for 25 ms timer <<<<<<< DEFAULT TIM25MSHIGH EQU 0x0B ; High byte for 25 ms timer TIM25MSPRESCALE EQU 0x11 ; Set 1:2 Prescale for 25 ms timer ; TIM50MSLOW EQU 0xDB ; Low byte for 50 ms timer TIM50MSHIGH EQU 0x0B ; High byte for 50 ms timer TIM50MSPRESCALE EQU 0x21 ; Set 1:4 Prescale for 50 ms timer ;; ; *************************************************************************** ; * Allocate variables in general purpose register space * ; *************************************************************************** ; CBLOCK 0x20 ; Start Data Block ; Save_W ; Save W (for ISR) Save_S ; Save STATUS (for ISR) Save_PCLATH ; Save PCLATH (for ISR) Save_FSR ; Save FSR (for ISR) timeloop1 ; Used in delay routines timeloop2 ; " encoder_new ; New value of encoder pins A and B encoder_old ; Old value of encoder pins A and B encoder_read ; Encoder pins A and B last_dir ; next_dir ; Last encoder direction direction ; Encoder direction (output line) enc_bits ; Encoder change magnitude/direction bits tick_count ; The count for this period HeartbeatFlags ; Heartbeat flags ; INT_OCCURRED equ 0 ; 50 ms interrupt occurred ; HEARTBEAT_NEEDED equ 1 ; Bit indicates an interrupt occurred so HB needed ; OUTSTANDING equ 2 ; Hearbeat outstanding with no response messages HeartbeatCounter ; Counter of 50ms interrupts before heartbeat needed PB_row ; Pushbutton row PB_column ; Pushbutton column row_number_pressed ; Row number save nonb_msg_number ; Non-button message number comm_count0 ; Counter for communications routines comm_count1 ; Counter for communications routines comm_count2 ; Counter for communications routines encoder_msg ; Message from INT-PIC to DRIVER-PIC (encoder data) ; ENCMSG equ 7 ; - Bit indicates this message is an ENCODER msg ; ENCDIR equ 6 ; - Bit indicates encoder direction (1 = clockwise) ; Bits 5 - 0 = Tick_count ; - 6 bits indicates number of ticks (in 50 ms) comm_flags ; Inter-PIC communications flags ; RCVINPROGRESS equ 0 ; - Inter-PIC communications - Receive in progress ; SENDINPROGRESS equ 1 ; - Inter-PIC communications - Send in progress ENDC ; End of Bank 0 Unique Data Block (MAX IS 6F ) ; ; -------------------------- CBLOCK 0x70 ; Start Shared Data Block (ACCESS FROM ALL BANKS) ; ORDER OF THESE MUST BE SAME AS BANK 1! reserved_for_debugger_B0 ; 0x070 is reserved for ICD-2 comm_in ; Communications byte received comm_out ; Communications byte sent comm_out_temp ; Temporary storage of comm_out s_bit ; Inter-PIC communications s_bit_count ; Inter-PIC communications r_bit ; Inter-PIC communications r_bit_count ; Inter-PIC communications r_bit_wait_count ; Inter-PIC communications gen_parity_byte ; Inter-PIC communications ; (70 - 7F is SHARED!) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 0 Shared Data Block (MAX IS 7F ) ; ; *************************** CBLOCK 0xA0 ; Start Bank 1 Dummy Data Block (16F877) W_Save_bank1_reserve ; Save W (interrupt routine) ENDC ; End of Bank 0 Data Block (MAX IS EF ) ; -------------------------- CBLOCK 0xF0 ; Start Shared Data Block (ACCESS FROM ALL BANKS) ; ORDER OF THESE MUST BE SAME AS BANK 0! reserved_for_debugger_B1 ; 0xF0 (0x070) is reserved for ICD-2 comm_in_bank1 ; Communications byte received (Same as comm_in) comm_out_bank1 ; Communications byte sent (Same as comm_out) comm_out_temp_bank1 ; Temporary storage of comm_out_bank1 (SHARED) s_bit_bank1 ; (SHARED) - Inter-PIC communications s_bit_count_bank1 ; (SHARED) - Inter-PIC communications r_bit_bank1 ; (SHARED) - Inter-PIC communications r_bit_count_bank1 ; (SHARED) - Inter-PIC communications r_bit_wait_count_bank1 ; (SHARED) - Inter-PIC communications gen_parity_byte_bank1 ; (SHARED) - Inter-PIC communications ; (F0 - FF is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 1 Shared Data Block (MAX IS FF ) ; ; *************************** CBLOCK 0x120 ; Start Bank 2 Dummy Data Block (16F877) W_Save_bank2_reserve ; Save W (interrupt routine) ; (170 - 17F is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 0 Data Block (MAX IS 17F ) ; ; *************************** CBLOCK 0x1A0 ; Start Bank 3 Dummy Data Block (16F877) W_Save_bank3_reserve ; Save W (interrupt routine) ; (1F0 - 1FF is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 0 Data Block (MAX IS 1FF ) ; ; *************************************************************************** ; * 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 nop ; Fall through to interrupt-handler_shell interrupt_handler_shell ; ; "BEWARE!!! You must avoid GOTO INTERRUPT at the interrupt vector as this ; inherits the page bits from the page that's interrupted! Just put a NOP ; at the interrupt vector and drop through to an interrupt routine in bank 0 ; at address h'0005'" (Per PICLIST, Roger Froud) ; ; Found to be very true in this project, since there is communications code ; in PAGE-1. Previously, with a GOTO Interrupt_handler here, when the PAGE-1 ; comm code got interrupted, the result was a hang since it tried to go to ; the Interrupt_handler address in PAGE-1 while it's actually in PAGE-0. ; movwf Save_W ; Save the contents of W swapf STATUS,w ; Save STATUS contents in W clrf STATUS ; Change to bank 0, regardless of current bank ; (Clears IRP, RP1, RP0) movwf Save_S ; Save status (in bank 0 status save location) movf PCLATH,w ; Move PCLATH into W movwf Save_PCLATH ; and save it clrf PCLATH ; Change to page 0, regardless of current page movf FSR,w ; Move FSR into W movwf Save_FSR ; and save it ; call interrupt_handler_main ; Go do the main interrupt processing ; ; End ISR Code. Now restore to saved state movf Save_PCLATH,w ; Restore saved PCLATH into W movwf PCLATH ; and move W into PCLATH movf Save_FSR,w ; Restore saved FSR into W movwf FSR ; and move W into FSR swapf Save_S,w ; Swap status save nibbles, and move into W movwf STATUS ; Move W into STATUS register ; (Bank now in original state) swapf Save_W,f ; Swap nibbles within W_Save ; (for next swap instruction) swapf Save_W,w ; Swap nibbles of W_Save, and move into W retfie ; ;**************************************************************************** ; * TABLES ;**************************************************************************** ; Table to convert a column number (column0 through column7 respectively) into a mask. ; The "1" bit is in the position of the PORT pin (7-0) to which that column is connected. ; makeposmask addwf PCL,f ; retlw 00000001b ; Select RA0 (Col_0) retlw 00000010b ; Select RA1 (Col_1) retlw 00000100b ; Select RA2 (Col_2) retlw 00001000b ; Select RA3 (Col_3) retlw 00010000b ; Select RA4 (Col_4) retlw 00100000b ; Select RA5 (Col_5) ; ; Table to convert row number (row0 through row3 respectively) into a mask. ; The "0" bit is in the position of the PORT pin (7-0) to which that row is connected. ; makenegmask addwf PCL,f retlw 11101111b ; Select RB4 (Row_0) retlw 11011111b ; Select RB5 (Row_1) retlw 10111111b ; Select RB6 (Row_2) retlw 01111111b ; Select RB7 (Row_3) ; ;**************************************************************************** ; start call init_INT_PIC ; Initialize the INTERFACE PIC bcf PORTB,RESET ; Issue a DRIVER PIC Reset call wait_16ms ; Wait bsf PORTB,RESET ; Release the DRIVER PIC Reset ;call wait_64ms ; Wait for the Driver PIC to initialize ; Only needed if heartbeats active ; ; Fall into the Main Program Loop ; ; **************************************************************************** ; * Name: main * ; * * ; * Purpose: This is the Main Program Loop. * ; * * ; * Input: None. * ; * * ; * Output: None. * ; **************************************************************************** ; main call handle_incoming ; Check/handle incoming messages call poll_encoder ; Check for encoder movement ; Wait (25 ms max.) for encoder to move or ; an input message waiting to come in call handle_incoming ; Check/handle incoming messages call check_pushbuttons ; Check the pushbuttons. Send msg if needed. ; call check_comm_link ; Send a heartbeat if it's time goto main ; Loop forever ; ; **************************************************************************** ; * Name: poll_encoder * ; * * ; * Purpose: This routine polls the encoder for up to 25 ms. * ; * When an encoder change is found, the direction the knob was * ; * turned is recorded in curr_dir, a tick_counter is incremented, * ; * and then it returns to caller. If the 25 ms time interval * ; * elapses (indicated by the interrupt_occurred flag) we will * ; * exit this routine anyway. Will also exit if it detects that * ; * there is a message waiting to be brought in. * ; * * ; * Note that this routine DOES NOT send the encoder updates to * ; * the Driver PIC, but simply accumulates the movements. * ; * Encoder updates (if any) are sent to the Driver PIC by an * ; * encoder message at the end of the 25 ms interval. * ; * * ; * Input: The A and B encoder inputs (RE0 and RE1). * ; * * ; * Output: last_dir set up with direction the knob was turned. (1=CW) * ; * tick_count incremented once for every transition detected * ; * (tick_count is cleared by int_handler when message sent) * ; **************************************************************************** ; poll_encoder clrwdt ; Reset the watchdog timer call input_message_detect ; Message waiting to be received? btfss STATUS,Z ; See if message waiting goto poll_exit ; Z=0 (W=1) if message, so exit immediately ; Z=1 (W=0) if no message. Keep going here movf PORTE,w ; Get the current encoder value movwf encoder_read ; Save it movlw 0x03 ; Get encoder mask (to isolate RE0 and RE1) andwf encoder_read,w ; Isolated encoder bits into W movwf encoder_new ; Save new value xorwf encoder_old,w ; Check to see if it changed btfss STATUS,Z ; Has it changed? (Z-flag set if no change) goto read_encoder ; Yes, encoder changed, so go look at it btfsc HeartbeatFlags,INT_OCCURRED ; No, did 25 ms interrupt occur yet? goto poll_exit ; Yes, it's time to exit (check buttons) goto poll_encoder ; If no encoder change and no interrupt ; we loop back to the top and keep looking read_encoder ; The encoder moved. Now determine which direction it turned. ;============================================================================= ; Encoder bits are on RE0 and RE1 - the two low order bits of ren_new ; RE0 is bit 0 (ENCODER_B) and RE1 is bit 1 (ENCODER_A) ; A and B are "gray code" - 90 degrees out of phase (quadrature) ; ___ ___ ; | | | | ; RE0 ___| |___| |___ ; ___ ___ ; | | | | ; RE1 ___| |___| |___ ; ^ ^ ^ ^ ^ ^ ^ ^ ; a b c d a b c d ; ; CW ROTATION =====> ; ; RE0 RE1 ; At point a: 0 0 ; At point b: 1 0 ; At point c: 1 1 ; At point d: 0 1 ; ; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is: ; 00, 10, 11, 01, 00, 10, 11, 01, etc. ; ; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is: ; 01, 11, 10, 00, 01, 11, 10, 00, etc. ; ; To determine if the sequence is UP or DOWN: ; 1) Take the "Right-Bit" of any pair. ; 2) XOR it with the "Left-Bit" of the next pair in the sequence. ; 3) If the result is non-zero it is UP ; If the result is 0 it is DOWN ; ; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning for test ;============================================================================= bcf HeartbeatFlags,INT_OCCURRED ; Clear interrupt flag bcf STATUS,C ; Clear the carry bit to prepare for rotate rlf encoder_old,f ; Rotate old bits left to align "Right-Bit" movf encoder_new,w ; Set up new bits in W xorwf encoder_old,f ; XOR old (left shifted) with new bits movf encoder_old,w ; Put XOR results into W also andlw 0x02 ; Mask to look at only "Left-Bit" of pair movwf next_dir ; Save result (in W) as direction (bit=UP) xorwf last_dir,w ; See if direction is same as before ; ; Prevent encoder slip from giving a false change in direction. ; btfsc STATUS,Z ; Zero flag set? (i.e, is direction same?) goto pe_continue ; Yes, same direction so no slip; keep going movf next_dir,w ; No Zero-flag, so direction changed movwf last_dir ; Update the direction indicator movf encoder_new,w ; Save the current encoder bits (now in W) movwf encoder_old ; for next time goto read_encoder ; Try again pe_continue clrf last_dir ; Clear last_dir (default is DN) btfss encoder_old,ENCODER_B ; Are we going UP? (look at bit 1) goto exit3 ; No, exit3 up2 movlw 0x02 ; Get UP value movwf last_dir ; and set as direction exit3 movf encoder_new,w ; Get the current encoder bits movwf encoder_old ; Save them in ren_old for the next time movf tick_count,w ; Get the tick_count value sublw 0xFF ; Has it reached the limit? btfss STATUS,Z ; Skip increment if equal incf tick_count,f ; Increment the tick count ; ; Note: The tick count and direction is sent to the Driver PIC by ; an encoder message every 25 ms (unless none occurred). poll_exit return ; Return to the caller ; ; **************************************************************************** ; * Name: check_comm_link * ; * * ; * Purpose: See if it's time to send a heartbeat message to the Driver PIC. * ; * Before sending next heartbeat (after 500 ms) see if a response * ; * was receive from the last one. If not, reset Driver PIC * ; * * ; * NOTE: Heartbeats work fine as implemented here; however, they * ; * tend to collide with messages initiated by the Driver PIC, such * ; * as the SSB LED messages (40 and 41). This causes a comm * ; * timeout, and thus an annoying 8-second freeze/reset. * ; * Because of this, leave heartbeats off for now. Good for debug. * ; * * ; * Input: HeartbeatFlags * ; * * ; * Output: None * ; * * ; **************************************************************************** ; check_comm_link btfss HeartbeatFlags,HEARTBEAT_NEEDED ; Is flag set? (by int handler) goto check_comm_exit ; No, exit bcf HeartbeatFlags,HEARTBEAT_NEEDED ; Clear flag until next interrupt ; btfsc HeartbeatFlags,OUTSTANDING ; Was HB sent but no response ? goto reset_driver_pic ; Yes, we missed it, so reset ; movlw 0x3A ; Get PIC-to-Driver heartbeat message number movwf comm_out ; Put in output message box pagesel send_byte ; Select page 1 call send_byte ; Send the message byte pagesel start ; Back to page 0 btfss STATUS,Z ; Good result? goto reset_driver_pic ; No, go and reset the Driver PIC bsf HeartbeatFlags,OUTSTANDING ; We sent at Heartbeat goto check_comm_exit ; Yes, exit ; reset_driver_pic bcf HeartbeatFlags,OUTSTANDING ; Clear heartbeat_outstanding flag clrf HeartbeatCounter ; Initialize counter bsf PORTC,LEDERRI ; Set the LCD indicating bad comm link bcf PORTB,RESET ; Reset the Driver PIC (low active) call wait_a_sec ; Leave for 1 second bsf PORTB,RESET ; and then release it (make it high again) bcf PORTC,LEDERRI ; Clear the LED call wait_a_sec ; Wait for call wait_a_sec ; the Driver PIC call wait_a_sec ; to initialize check_comm_exit return ; ; ; **************************************************************************** ; * Name: check_pushbuttons * ; * * ; * Purpose: Look at the pushbuttons. When one is pressed send the message * ; * to Driver PIC. * ; * * ; * Note: A "quick scan" takes 35 instructions. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; **************************************************************************** ; check_pushbuttons ; ; PB scanning method: ; 1) Select a single row by setting it low. (Others high.) ; 2) Look at each column to see it it's being pulled low by a pushbutton. ; 3) If it's low, that PB is being pressed. Exit to handle that PB. ; 4) If it's high, select next row and repeat. ; ; NOTE: MAKE SCAN PATHLENGTH AS SHORT AS POSSIBLE, EVEN IF IT REQUIRES ; ADDITIONAL MEMORY ; ; Assume bits 7-4 contain row number and bits 3-0 contain column number. ; Since the maximum row number is 3 and the maximum column number is 5, ; the largest Row/Column message number is 35. ; ; Reserve messages 50 through FF for other message types. ; ; It's dangerous to do bit manipulation on PORT bits because of the ; read-modify-write problem. Single bit changes are OK but if multiple PORT ; bits are to be changed consecutively (as they are in this routine) care must ; be taken. Here will do bit manipulation plus waits. bcf PORTB,PB_ROW0 ; Start by selecting Row 0 (set low) nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW1 ; All other rows are high nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW2 ; nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW3 ; ; btfss PORTA,PB_COL0 ; See if it's R0C0 goto R0C0 ; Yes, handle it btfss PORTA,PB_COL1 ; See if it's R0C1 goto R0C1 ; Yes, handle it btfss PORTA,PB_COL2 ; See if it's R0C2 goto R0C2 ; Yes, handle it btfss PORTA,PB_COL3 ; See if it's R0C3 goto R0C3 ; Yes, handle it btfss PORTA,PB_COL4 ; See if it's R0C4 goto R0C4 ; Yes, handle it btfss PORTA,PB_COL5 ; See if it's R0C5 goto R0C5 ; Yes, handle it ; bsf PORTB,PB_ROW0 ; De-select Row 0 nop ; Let it settle (Read-modify-write problem) bcf PORTB,PB_ROW1 ; Select Ro1 1 (set low) ; btfss PORTA,PB_COL0 ; See if it's R1C0 goto R1C0 ; Yes, handle it btfss PORTA,PB_COL1 ; See if it's R1C1 goto R1C1 ; Yes, handle it btfss PORTA,PB_COL2 ; See if it's R1C2 goto R1C2 ; Yes, handle it btfss PORTA,PB_COL3 ; See if it's R1C3 goto R1C3 ; Yes, handle it btfss PORTA,PB_COL4 ; See if it's R1C4 goto R1C4 ; Yes, handle it btfss PORTA,PB_COL5 ; See if it's R1C5 goto R1C5 ; Yes, handle it ; bsf PORTB,PB_ROW1 ; De-select Row 1 #if (Debugger == 0) nop ; Let it settle (Read-modify-write problem) bcf PORTB,PB_ROW2 ; Select Ro1 2 (set low) ; btfss PORTA,PB_COL0 ; See if it's R2C0 goto R2C0 ; Yes, handle it btfss PORTA,PB_COL1 ; See if it's R2C1 goto R2C1 ; Yes, handle it btfss PORTA,PB_COL2 ; See if it's R2C2 goto R2C2 ; Yes, handle it btfss PORTA,PB_COL3 ; See if it's R2C3 goto R2C3 ; Yes, handle it btfss PORTA,PB_COL4 ; See if it's R2C4 goto R2C4 ; Yes, handle it btfss PORTA,PB_COL5 ; See if it's R2C5 goto R2C5 ; Yes, handle it ; bsf PORTB,PB_ROW2 ; De-select Row 2 nop ; Let it settle (Read-modify-write problem) bcf PORTB,PB_ROW3 ; Select Ro1 3 (set low) ; btfss PORTA,PB_COL0 ; See if it's R3C0 goto R3C0 ; Yes, handle it btfss PORTA,PB_COL1 ; See if it's R3C1 goto R3C1 ; Yes, handle it btfss PORTA,PB_COL2 ; See if it's R3C2 goto R3C2 ; Yes, handle it btfss PORTA,PB_COL3 ; See if it's R3C3 goto R3C3 ; Yes, handle it btfss PORTA,PB_COL4 ; See if it's R3C4 goto R3C4 ; Yes, handle it btfss PORTA,PB_COL5 ; See if it's R3C5 goto R3C5 ; Yes, handle it #endif goto CheckButtonsExit ; No button is being pushed R0C0 movlw 0x00 goto GotRowCol R0C1 movlw 0x01 goto GotRowCol R0C2 movlw 0x02 goto GotRowCol R0C3 movlw 0x03 goto GotRowCol R0C4 movlw 0x04 goto GotRowCol R0C5 movlw 0x05 goto GotRowCol R1C0 movlw 0x10 goto GotRowCol R1C1 movlw 0x11 goto GotRowCol R1C2 movlw 0x12 goto GotRowCol R1C3 movlw 0x13 goto GotRowCol R1C4 movlw 0x14 goto GotRowCol R1C5 movlw 0x15 goto GotRowCol R2C0 movlw 0x20 goto GotRowCol R2C1 movlw 0x21 goto GotRowCol R2C2 movlw 0x22 goto GotRowCol R2C3 movlw 0x23 goto GotRowCol R2C4 movlw 0x24 goto GotRowCol R2C5 movlw 0x25 goto GotRowCol R3C0 movlw 0x30 goto GotRowCol R3C1 movlw 0x31 goto GotRowCol R3C2 movlw 0x32 goto GotRowCol R3C3 movlw 0x33 goto GotRowCol R3C4 movlw 0x34 goto GotRowCol R3C5 movlw 0x35 ; GotRowCol movwf comm_out ; Save message byte to send ; (same as comm_out_bank1) bsf PORTB,PB_ROW0 ; Clear row select nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW1 ; Clear row select nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW2 ; Clear row select nop ; Let it settle (Read-modify-write problem) bsf PORTB,PB_ROW3 ; Clear row select ; movf comm_out,W ; Get message byte again andlw 0x0F ; Isolate column number (from R/C in w) movwf PB_column ; and save it swapf comm_out,w ; Move row into LS position (in w) andlw 0x0F ; Isolate row number movwf PB_row ; and save it ; SendMessageToDriver ; Send message to the Driver PIC ; pagesel send_byte ; Select page 1 call send_byte ; Send the message byte pagesel start ; Back to page 0 btfsc STATUS,Z ; Good result? goto CheckButtonsRelease ; Yes, go wait for release bsf PORTC,LEDERRI ; Turn on LED ERROR-I call wait_a_sec ; Wait one second bcf PORTC,LEDERRI ; and then turn it off again ; CheckButtonsRelease call wait_32ms ; Wait for a while (debounce button press) ; ; Wait for keypress to be released ; movf PB_row,w ; Get row number in W call makenegmask ; Make mask (all 1 except correct bit is 0) andwf PORTB,f ; Select (low) the row containing active PB ; but don't change other PORTB bits CheckReleaseLoop movf PB_column,w ; Get column number in W call makeposmask ; Make mask (all 0 except correct bit is 1) andwf PORTA,w ; See if selected column bit is set in PORTA ; xxxx0xxx = SPIN (button still down) xxxx1xxx = Ready to go on btfsc STATUS,Z ; Is result zero? goto CheckReleaseLoop ; Yes, PB is still down so loop ; No, PB is now relesed to fall through call wait_32ms ; Wait for a while (debounce button release) ; CheckButtonsExit return ; ; **************************************************************************** ; * Name: handle_incoming * ; * * ; * Purpose: See if Driver PIC is trying to send us a message. * ; * If so, bring in the message and perform appropriate action. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; **************************************************************************** ; * MESSAGE NUMBERS * ; * * ; * C0 C1 C2 C3 C4 C5 * ; * -- -- -- -- -- -- * ; * Pushbutton MSG numbers - Row0: 00 01 02 03 04 05 * ; * Row / Column Row1: 10 11 12 13 14 15 * ; * RRRRCCCC Row2: 20 21 22 23 24 25 * ; * Row3: 30 31 32 33 34 35 * ; * * ; * MSG 00 - I->D Keypad 1 * ; * MSG 01 - I->D Keypad 2 * ; * MSG 02 - I->D Keypad 3 * ; * MSG 03 - I->D MHz UP * ; * MSG 04 - I->D Split Toggle * ; * MSG 05 - I->D A/B Toggle * ; * MSG 10 - I->D Keypad 4 * ; * MSG 11 - I->D Keypad 5 * ; * MSG 12 - I->D Keypad 6 * ; * MSG 13 - I->D MHz DN * ; * MSG 14 - I->D Mode Cycle * ; * MSG 15 - I->D A=B * ; * MSG 20 - I->D Keypad 7 * ; * MSG 21 - I->D Keypad 8 * ; * MSG 22 - I->D Keypad 9 * ; * MSG 23 - I->D Band UP * ; * MSG 24 - I->D Offset Toggle * ; * MSG 25 - I->D FastTune Toggle * ; * MSG 30 - I->D Keypad * * ; * MSG 31 - I->D Keypad 0 * ; * MSG 32 - I->D Keypad # * ; * MSG 33 - I->D Band DN * ; * MSG 34 - I->D Backlight Toggle * ; * MSG 35 - I->D Calibrate * ; * * ; * MSG 36 - I->D (Reserved) * ; * MSG 37 - I->D (Reserved) * ; * MSG 38 - I->D (Reserved) * ; * MSG 39 - I->D (Reserved) * ; * MSG 3A - I->D (Heartbeat) * ; * * ; * MSG 3B - D->I (Heartbeat Response) * ; * MSG 3C - D->I (Reserved) * ; * MSG 3D - D->I (Reserved) * ; * MSG 3E - D->I (Reserved) * ; * MSG 3F - D->I (Reserved) * ; * * ; * MSG 40 - D->I LED1 ON (SSB) and LED2 OFF * ; * MSG 41 - D->I LED2 ON (CW) and LED1 OFF * ; * MSG 42 - D->I LED3 ON (LEDVFOA) and LED4 OFF * ; * MSG 43 - D->I LED4 ON (LEDVFOB) and LED3 OFF * ; * MSG 44 - D->I LED5 ON (LEDSPLIT) * ; * MSG 45 - D->I LED6 ON (LEDFTUNE) * ; * MSG 46 - D->I LED7 ON (LEDCAL) * ; * MSG 47 - D->I (Unused) * ; * MSG 48 - D->I (Unused) * ; * MSG 49 - D->I (Unused) * ; * MSG 4A - D->I (Unused) * ; * MSG 4B - D->I (Unused) * ; * MSG 4C - D->I (Unused) * ; * MSG 4D - D->I (Unused) * ; * MSG 4E - D->I (Unused) * ; * MSG 4F - D->I (Unused) * ; * * ; * MSG 50 - D->I (Unused) * ; * MSG 51 - D->I (Unused) * ; * MSG 52 - D->I (Unused) * ; * MSG 53 - D->I (Unused) * ; * MSG 54 - D->I LED5 OFF (LEDSPLIT) * ; * MSG 55 - D->I LED6 OFF (LEDFTUNE) * ; * MSG 56 - D->I LED7 OFF (LEDCAL) * ; * MSG 57 - D->I (Unused) * ; * MSG 58 - D->I (Unused) * ; * MSG 59 - D->I (Unused) * ; * MSG 5A - D->I (Unused) * ; * MSG 5B - D->I (Unused) * ; * MSG 5C - D->I (Unused) * ; * MSG 5D - D->I (Unused) * ; * MSG 5E - D->I (Unused) * ; * MSG 5F - D->I (Unused) * ; * * ; **************************************************************************** ; handle_incoming call input_message_detect ; Message waiting to be received? btfsc STATUS,Z ; W=1 if message waiting goto check_in_exit ; W=0 if no message, so exit ; bcf HeartbeatFlags,OUTSTANDING ; Clear heartbeat_outstanding flag clrf HeartbeatCounter ; Start heartbeat counter over pagesel receive_byte ; Select page 1 call receive_byte ; Bring in the message byte pagesel start ; Back to page 0 btfsc STATUS,Z ; Good result? goto look_at_incoming ; Yes, look at the message bsf PORTC,LEDERRI ; Turn on LED call wait_a_sec ; Wait one second bcf PORTC,LEDERRI ; and then turn it off again goto handle_incoming ; Go look for more incoming messages look_at_incoming ; ; Check for legal Driver->Interface message. ; D->I messages are number 3B - 3F ; movlw 0x3B ; First legal D->I message subwf comm_in,w ; See if msg is legal number btfss STATUS,C ; Look at Carry (set if positive or zero) goto incoming_check_invalid ; Not set, so msg < 3B, so it's bad ; ; incoming_check_HB_response movlw 0x3B ; heartbeat_response message subwf comm_in,w ; See if message is a heartbeat_response btfss STATUS,Z ; Is it heartbeat_response (Yes if Zero set) goto incoming_check_driver_button ; Not set, so no match. Keep checking call handle_heartbeat_response ; Go handle heartbeat_response message goto handle_incoming ; Go look for more incoming messages ; incoming_check_driver_button movlw 0x40 ; Highest legal Driver-Button message + 1 subwf comm_in,w ; See if msg is a button press btfsc STATUS,C ; Look at Carry (set if positive or zero) goto incoming_check_LED_ON ; Set, so go check for good LED_ON message call handle_button_msg ; C clear, so it's button msg. Handle it... goto handle_incoming ; Go look for more incoming messages ; incoming_check_LED_ON ; ; Check for request to light a LED ; LED ON messages are number 40 - 4F ; movlw 0x50 ; Highest LED message + 1 subwf comm_in,w ; See if msg is a LED_ON message btfsc STATUS,C ; Look at Carry bit (C is positive after sub) goto incoming_check_LED_OFF ; Set, so go check for good LED_OFF message call LED_ON ; C clear so it's good LED_ON msg. Light it... goto handle_incoming ; Go look for more incoming messages ; incoming_check_LED_OFF ; ; Check for request to TURN OFF a LED ; LED OFF messages are number 50 - 5F ; movlw 0x60 ; Highest LED message + 1 subwf comm_in,w ; See if msg is a LED_OFF message btfsc STATUS,C ; Look at Carry bit (C is positive after sub) goto incoming_check_invalid ; Carry set so msg too big (not valid) call LED_OFF ; C clear so it's good LED_OFF msg. Turn off... goto handle_incoming ; Go look for more incoming messages ; incoming_check_invalid ; ; No more valid messages yet ; Messages above 5F are invalid ; bsf PORTC,LEDERRI ; It's invalid, so turn on LED ERROR-I call wait_a_sec ; Wait one second bcf PORTC,LEDERRI ; and then turn it off again goto handle_incoming ; Go look for more incoming messages ; check_in_exit return ; ; **************************************************************************** ; * Name: handle_heartbeat_response * ; * * ; * Purpose: A heartbeat_response message is returned from the Driver PIC * ; * immediately after it receives a heartbeat message. * ; * The only action is to clear the heartbeat outstanding flag. * ; * This flag, which is set when the heartbeat is issued, indicates * ; * that a response should be coming. If the heartbeat response * ; * (or another input message) is not received by the time we are * ; * ready to send the next heartbeat, (i.e., the outstanding flag * ; * is still set after 500 ms), the Driver PIC must be hung up so * ; * we reset it. * ; * * ; * Input: None * ; * * ; * Output: None * ; **************************************************************************** ; handle_heartbeat_response bcf HeartbeatFlags,OUTSTANDING ; Clear heartbeat_outstanding flag return ; ; **************************************************************************** ; * Name: handle_button_msg * ; * * ; * Purpose: * ; * * ; * Input: None * ; * * ; * Output: None * ; **************************************************************************** ; handle_button_msg ; ; This should not happen. Add code if button presses are some day passed this ; way. Messages 3B - 3F are reserved for Driver->Interface messages ; return ; ; **************************************************************************** ; * Name: LED_ON * ; * * ; * Purpose: Turn on appropriate LED * ; * * ; * Input: message number in comm_in * ; * * ; * Output: None * ; **************************************************************************** ; LED_ON movlw 0x40 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 40? goto LED1_ON ; Yes, go light LED1 (LSB) movlw 0x41 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 40? goto LED2_ON ; Yes, go light LED2 (USB) movlw 0x42 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 41? goto LED3_ON ; Yes, go light LED3 (LEDVFOA) movlw 0x43 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 42? goto LED4_ON ; Yes, go light LED4 (LEDVFOB) movlw 0x44 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 44? goto LED5_ON ; Yes, go light LED5 (LEDSPLIT) movlw 0x45 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 45? goto LED6_ON ; Yes, go light LED6 (LEDFTUNE) movlw 0x46 ; Check for msg subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 46? goto LED7_ON ; Yes, go light LED7 (LEDCAL) ; LED_ON_Error bsf PORTC,LEDERRI ; St LED8 goto LED_ON_Error ; /* HANG - Need more debugging */ ; LED1_ON bsf PORTC,LEDLSB ; Set LED1 ON nop ; Make sure it's stable (read-modify-write) bcf PORTC,LEDUSB ; and LED2 OFF goto LED_ON_exit ; and exit LED2_ON bsf PORTC,LEDUSB ; Set LED2 ON nop ; Make sure it's stable (read-modify-write) bcf PORTC,LEDLSB ; and LED1 OFF goto LED_ON_exit ; and exit LED3_ON bsf PORTC,LEDVFOA ; Set LED3 nop ; Make sure it's stable (read-modify-write) bcf PORTC,LEDVFOB ; and CLEAR LED4 goto LED_ON_exit ; and exit LED4_ON bsf PORTC,LEDVFOB ; Set LED4 nop ; Make sure it's stable (read-modify-write) bcf PORTC,LEDVFOA ; and CLEAR LED3 goto LED_ON_exit ; and exit LED5_ON bsf PORTD,LEDSPLIT ; Set LED5 goto LED_ON_exit ; and exit LED6_ON bsf PORTD,LEDFTUNE ; Set LED6 goto LED_ON_exit ; and exit LED7_ON bsf PORTC,LEDCAL ; Set LED7 goto LED_ON_exit ; and exit LED_ON_exit return ; ; **************************************************************************** ; * Name: LED_OFF * ; * * ; * Purpose: Turn off appropriate LED * ; * * ; * Input: message number in comm_in * ; * * ; * Output: None * ; **************************************************************************** ; LED_OFF ; ; Note: LED1 and LED2 (LEDLSB and LEDVUSB) are turned OFF without messages. ; LED3 and LED4 (LEDVFOA and LEDVFOB) are turned OFF without messages. ; In both cases, when one is turned ON, the other is turned off. ; movlw 0x54 ; Check for msg 54 subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 54? goto LED5_OFF ; Yes, turn LED5 off (LEDSPLIT) movlw 0x55 ; Check for msg 55 subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 55? goto LED6_OFF ; Yes, turn LED6 off (LEDFTUNE) movlw 0x56 ; Check for msg 56 subwf comm_in,w ; Check msg btfsc STATUS,Z ; Is it msg 56 goto LED7_OFF ; Yes, turn LED7 off (LEDCAL) ; LED_OFF_Error bsf PORTC,LEDERRI ; Set LED8 goto LED_OFF_Error ; /* HANG - Need more debugging */ ; LED5_OFF bcf PORTD,LEDSPLIT ; Clear LED5 goto LED_OFF_exit ; and exit LED6_OFF bcf PORTD,LEDFTUNE ; Clear LED6 goto LED_OFF_exit ; and exit LED7_OFF bcf PORTC,LEDCAL ; Clear LED7 goto LED_OFF_exit ; and exit LED_OFF_exit return ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>> INITIALIZATION ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Name: init_INT_PIC * ; * * ; * Purpose: Power on initialization of 16F877. * ; * * ; * Input: None * ; * * ; * Output: None * ;***************************************************************************** ; init_INT_PIC clrf INTCON ; No interrupts for now banksel TRISA ; Select 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) clrf TRISB ; Port B is all outputs movlw 00110000b ; (7,6,3,2,1,0) = outputs (5,4) = inputs movwf TRISC ; to Port C movlw 00001000b ; (7,6,5,4,2,1,0) = outputs, (3) = inputs movwf TRISD ; to Port D movlw 00000011b ; (2) = output, (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 ; ; New code for timer 1 and interrupt. ; movlw 0xC0 ; Get GIE and PEIE bits movwf INTCON ; Enable general interrupts banksel PIE1 ; Switch to bank 1 to enable timer 1 movlw 0x01 ; Get bit to enable timer 1 interrupt movwf PIE1 ; Set TMR1IE banksel PORTA ; Switch to bank 0 ; ; Initialize the TIMER1 timer to expire after 25ms and generate an interrupt. ; 1)Set TMR1IE bit so an interrupt is generated when the TIMER1 rolls over. ; 2)Set a value into . Note that it must be less than FFFF. If more than FFFF, need Prescalar ; Note: Will also set up this starting value when after an interrupt occurs. ; Calculate a value for : ; With no prescaler, TIMER1 gets incremented on every instruction cycle which is (clock frequency / 4). ; The crystal frequency is 20 MHz, so instruction cycle is 5 MHz. ; With no Prescaler, we would have 2.0 x10^-7 seconds per TIMER1 tick. ; This means that 25 ms we would have: .025 sec / (2.0 x10^-7 seconds per TIMER1 tick) = 125,000 TIMER1 ticks ; This means we want TIMER1 to "roll over" after 125,000 TIMER1 Ticks. ; 125,000 is 0x1E848. Since this is larger than FFFF, we can't use it directly. Must use prescalar. ; Try prescaler of 2. Now Timer gets updated on every TWO instructions. ; Thus, we only need to need 62,500 TIMER1 ticks. ; 62,500 is 0xF424 is less than 0xFFFF, so it will fit. ; To get value for TIMER1 such that it will "roll-over" after 0xF424 TIMER1 ticks, ; subtract 0xFFFF - 0xF424 = 0x0BDB. ; Thus we want to put 0x0BDB into TIMER1 now and again after every roll-over interrupt. ; 3)Set Prescaler value of 1:2, (set = 10) for reason explained above. ; clrf T1CON ; Turn TIMER1 off movlw TIM25MSLOW ; Get low byte for 25 ms movwf TMR1L ; Set low timer byte movlw TIM25MSHIGH ; Get high byte for 25 ms movwf TMR1H ; Set high timer byte movlw TIM25MSPRESCALE ; Turn on TIMER1 with 1:2 Prescale movwf T1CON ; Turn the timer on banksel PORTA ; Switch back to bank 0 ; ; ; PORTA doesn't need init (all inputs) movlw 0x01 ; Clear all of PORTB except for movwf PORTB ; the DR-PIC RESET pin (RB0) clrf PORTC ; Initialize PORTC clrf PORTD ; Initialize PORTD clrf PORTE ; Initialize PORTE clrf tick_count ; Initialize tick count to zero clrf comm_flags ; Initialize movf PORTE,w ; Get the current encoder position at power-up movwf encoder_old ; Save it so we don't get a tick on first check clrf HeartbeatFlags ; Initialize heartbeat flags clrf HeartbeatCounter ; Initialize counter return ; ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>> INTERRUPT HANDLER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; **************************************************************************** ; * Name: interrupt_handler_main * ; * * ; * Purpose: This interrupt handler gets called on each timer interrupt. * ; * It sends any encoder steps to the Driver PIC by sending a * ; * message to the Driver PIC. The message contains the * ; * tick_count and the direction. * ; * * ; * Note that this interrupt_handler_main routine is called from * ; * the interrupt_handler routine starting at location 0x005. The * ; * first part HAS to be there; we cannot simply put a GOTO in the * ; * interrupt vector. (Reason is explained there.) * ; * * ; * Input: None * ; * * ; * Output: None (encoder updates sent to Driver PIC) * ; **************************************************************************** ; interrupt_handler_main movlw 0x00 ; Clear movwf PIR1 ; timer 1 interrupt flag ; ; Check the tick_count. If non-zero, send a message ; ; bsf PORTC,LEDERRI ; Turn on to see pulses in scope ; call wait_20_inst ; to measure ; bcf PORTC,LEDERRI ; update frequency movf tick_count,f ; Look at the tick_count btfsc STATUS,Z ; Is the tick_count zero? goto reset_timer ; Yes, tick_count was zero, skip strobe decf tick_count,w ; Decrement into W, so zero can be used for 1 Hz ; ; The encoder has been turned and the direction has been determined. ; Move the tick_count to encoder_msg bits 5 - 0. Don't worry about tick_counter ; being more than 6 bits (63 ticks) because the upper bits will be overwritten to ; the correct value anyway. Bit 6 will set/cleared to indicate direction and ; bit 7 will always be set to indicate it's an encoder message. ; ; Is a 6-bit tick_counter sufficient? Yes. A 128-position optical encoder ; produces a total of 512 transitions per revolution. Assuming we want to be able ; to use all 512 transitions when turning the encoder at a rate of 2 rev/sec, we ; must be able to handle 1024 transitions (ticks) per second. Since we are sending ; the tick counter over every 25 ms (40 times per second), the largest tick count ; we have to handle is 1024/40 = 25.6. Thus, we have plenty of spare bits (6 bits) ; for design changes or for turning the encoder faster than two rev/sec. ; ; Note that the actual tick_counter is decremented by 1 so that a zero value in ; the tick_count field is a valid message and indicates one tick. No message is ; sent if there are no ticks. ; movwf encoder_msg ; Move tick_counter into encoder_msg bsf encoder_msg,ENCMSG ; Set the encoder_message bit ; ; Set the direction bit. ; bsf encoder_msg,ENCDIR; Assume clockwise - Set direction bit movf last_dir,w ; Get the direction value (0=CCW, 2=CW) btfss STATUS,Z ; Clockwise? bcf encoder_msg,ENCDIR; No: Indicate counter clockwise ; ; Send the encoder message ; btfsc comm_flags,RCVINPROGRESS ; Was Comm already doing Receive_byte? goto encoder_skip_send ; SKIP SENDING THE MESSAGE! ; It's better than a hang! movf encoder_msg,w ; Get encoder_message in W movwf comm_out ; Put in output message box pagesel send_byte ; Select page 1 call send_byte ; Send the message byte pagesel start ; Back to page 0 btfss STATUS,Z ; Good result? nop ; No - ignore the update encoder_skip_send ; ; Reset the timer to start another 25 ms interval. ; reset_timer clrf T1CON ; Turn TIMER1 off movlw TIM25MSLOW ; Get low byte for 25 ms timer movwf TMR1L ; Set low timer byte movlw TIM25MSHIGH ; Get high byte for 25 ms timer movwf TMR1H ; Set high timer byte movlw TIM25MSPRESCALE ; Bits to turn on TIMER1 with 1:2 Prescale movwf T1CON ; Turn the timer on clrf tick_count ; Clear the tick count bsf HeartbeatFlags,INT_OCCURRED ; An interrupt occurred incf HeartbeatCounter,f ; Increment heartbeat counter movlw 0x0A ; See if count has subwf HeartbeatCounter,w ; reached 10 yet btfss STATUS,Z ; Zero set if it's at 10 goto int_heartbeat_OK ; Not zero yet, so continue clrf HeartbeatCounter ; Init counter to minus 10 (HB every 500ms) bsf HeartbeatFlags,HEARTBEAT_NEEDED; Set flag - we need a heartbeat int_heartbeat_OK return ; Return and restore registers ; #endif ; End InterfacePIC Only #ifdef DriverPIC ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ####################### ###################### ; ####################### DRIVER PIC ###################### ; ####################### ###################### ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; **************************************************************************** ; * 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 ; integer in the format . ; ; 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 default_band_index equ 0x14 ; 14.025 MHz is band index 0x14 ; highest_band_base equ 0x28 ; The offset to base of 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) VFOCHANGED equ 4 ; VFO changed (A or B) so need to send message NEEDUPDATE equ 5 ; A change has been made, so need DDS and LCD update FAST_TUNE equ 6 ; Bit indicates fast tuning DIR_UP equ 7 ; Encoder UP direction (decode of ENC3 - for encdir) ; ; Equates for VFOaction register VFOCALIBRATE equ 0 ; Bit indicates Calibrate is in progress KEYPAD_ACTIVE equ 1 ; Keypad buffering in progress TONE_SET_ACTIVE equ 2 ; SideTone adjustment is in progress ; ; Equates for ModeSelect register LSB equ 0 ; LSB USB equ 1 ; USB CW equ 2 ; CW- (lower side) RevCW equ 3 ; CW+ (upper side) ; ; Equates for Encoder_message bits ENCMSG equ 7 ; Bit indicates it's an Encoder message ENCDIR equ 6 ; Bit set if encoder turned in clockwise direction ; ; Equates for comm_flags ; Inter-PIC communications RCVINPROGRESS equ 0 ; Comm - Receive in progress SENDINPROGRESS equ 1 ; Comm - Send in progress ; TUNE6 equ 4 ; Normal Tune Acceleration constants (for 25 ms updates) TUNE5 equ 4 ; " " " " TUNE4 equ 3 ; " " " " TUNE3 equ 3 ; " " " " TUNE2 equ 2 ; " " " " TUNE1 equ 1 ; " " " " FASTTUNE6 equ 2 ; Fast Tune Acceleration constants (for 25 ms updates) FASTTUNE5 equ 2 ; " " " " FASTTUNE4 equ 2 ; " " " " FASTTUNE3 equ 2 ; " " " " FASTTUNE2 equ 2 ; " " " " FASTTUNE1 equ 2 ; " " " " ; HIGHESTBAND equ 0x0A ; ; CWOFFSET_1 equ 0x02 ; Default CW Offset is 0x258 = 600 CWOFFSET_0 equ 0x58 ; ; MODE equ 1 ; Default Mode is 1 (LSB) ; LIGHT_ON equ 0 ; BacklightControl bit ; (1 = on, 0 = off) ; ; *************************************************************************** ; * 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 ; ; ******* NOTE: ALL HARD CODED ADDRESSES! *********** ; 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 ; CW Offset data starts at address 4 of EEPROM DATA CWOFFSET_0 ; LS byte of CW Offset DATA CWOFFSET_1 ; MS byte of CW Offset ; Mode starts at address 6 of EEPROM DATA MODE ; Last Mode ; ; Clear 249 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 ; ; ; ; *************************************************************************** ; * Assign names to IO pins. * ; * * ; * INPUT = 1 Output = 0 Bit Order: 7 6 5 4 3 2 1 0 * ; *************************************************************************** ; ; ; A register bits (DRIVER PIC): ; TRISA - 0x00 ; LCD_DB4 equ 0x00 ; Output /(input status) Data (LCD pin 11) LCD_DB5 equ 0x01 ; Output /(input status) Data (LCD pin 12) LCD_DB6 equ 0x02 ; Output /(input status) Data (LCD pin 13) LCD_DB7 equ 0x03 ; Output /(input status) Data/Busy (LCD pin 14) LCD_BUSY equ 0x03 ; Output/Input - LCD Busy in DB7 (LCD pin 14) LCD_e equ 0x04 ; Output 0=instruction, 1=data (LCD pin 4) LCD_rw equ 0x05 ; Output 0=write, 1=read (LCD pin 5) ; 0x06 - Does not exist in 16F877 ; 0x07 - Does not exist in 16F877 ; ; B register bits (DRIVER PIC): ; TRISB - 0x00 ; IQRelaySet equ 0x00 ; Output - IQ Relay Set IQRelayReset equ 0x01 ; Output - IQ Relay Reset BANDRELAY0 equ 0x02 ; Output - Band Relay 0 BANDRELAY1 equ 0x03 ; Output - Band Relay 1 BANDRELAY2 equ 0x04 ; Output - Band Relay 2 BANDRELAY3 equ 0x05 ; Output - Band Relay 3 BANDRELAY4 equ 0x06 ; Output - Band Relay 4 BANDRELAY5 equ 0x07 ; Output - Band Relay 5 ; ; C register bits (DRIVER PIC): ; TRISC - 0x70 ; BACKLIGHT equ 0x00 ; Output - LCD Backlight Toggle (1=ON) ; equ 0x01 ; Output - (Unused) ; equ 0x02 ; Output - (Unused) LEDERRD equ 0x03 ; Output - LED9 DATAIN equ 0x04 ; Input - Inter-PIC communications ACKIN equ 0x05 ; Input - Inter-PIC communications XMIT_Keyed_Low_Active equ 0x06 ; Input - Signal from XMTR (low active when keyed) DDS_clk equ 0x07 ; Output - AD9854 write clock (AD9854 pin 21) ; ; D register bits (DRIVER PIC): ; TRISD - 0x08 ; ACKOUT equ 0x00 ; Output - Inter-PIC communications DATAOUT equ 0x01 ; Output - Inter-PIC communications CLOCKOUT equ 0x02 ; Output - Inter-PIC communications CLOCKIN equ 0x03 ; Input - Inter-PIC communications DDS_ioud equ 0x04 ; Output - AD9854 IO Update IOUD(AD9854 pin 20) DDS_data equ 0x05 ; Output - AD9854 serial data SDIO(AD9854 pin 19) DDS_ioreset equ 0x06 ; Output - AD9854 I/O reset (AD9854 pin 17) DDS_mreset equ 0x07 ; Output - AD9854 master reset (AD9854 pin 71) ; ; E register bits (DRIVER PIC): ; TRISE - 0x00 ; LCD_rs equ 0x00 ; Output 0=disable, 1=enable (LCD pin 6) ; equ 0x01 ; Output (Unused) ; equ 0x02 ; Output (Unused) ; 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) Save_W ; Save W (interrupt routine) Save_S ; Save Status (interrupt routine) Save_FSR ; Save FSR (interrupt routine) Save_PCLATH ; Save PCLATH (interrupt routine) encindex ; Encoder - change index ; tuning_index ; Index for fstep table lookup VFOcontrol ; Specifies VFO control flags ; 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) ; VFOCHANGED equ 4 ; VFO changed (A or B) so need to send message ; NEEDUPDATE equ 5 ; A change has been made, so need DDS and LCD update ; FAST_TUNE equ 6 ; Bit indicates fast tuning ; DIR_UP equ 7 ; Bit indicates Tuning Direction UP VFOaction ; Specifies VFO activities ; VFOCALIBRATE equ 0 ; Bit indicates Calibrate is in progress ; KEYPAD_ACTIVE equ 1 ; Bit indicates keypad buffering in progress ; TONE_SET_ACTIVE equ 2 ; Bit indicates side tone set is in progress ModeSelect ; Setlect the Mode ; LSB equ 0 ; LSB ; USB equ 1 ; USB ; CW equ 2 ; CW ; RevCW equ 3 ; Reverse CW 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 ; freq_0 ; VFO frequency - (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 (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 (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 ; LCD_char ; Character being sent to the LCD LCD_nibble ; Rearranged MS nibble for the LCD LCD_read ; Character read from the LCD timeloop1 ; Used in delay routines timeloop2 ; " " " " count ; loop counter (gets reused) band_index ; Index to lowest byte of current band in table new_band ; Temporary band index before full update done old_band_index ; Save the old band index when changing to new index band_changed_flag ; Set to 1 if band changed, 0 if not changed new_band_base ; Index to lowest byte of new band in freq table new_band_index ; Index to current byte of new band in freq table ; (NOTE: highest_band_base set up as an EQUATE!) band_number_temp ; Temp storage for bandswitch routine rs_value ; The LCD rs line flag value comm_count0 ; Counter for communications routines comm_count1 ; Counter for communications routines comm_count2 ; Counter for communications routines comm_in_digit ; Digit to be written to LCD tick_count2 ; counter of encoder movements - MS byte tick_count1 ; " " " " tick_count0 ; " " " " - LS byte enc_shift_count ; Shift count for calculating acceleration comm_flags ; Flag to see if Inter-PIC comm is active ; RCVINPROGRESS equ 0 ; Bit indicates Comm Receive is in progress ; SENDINPROGRESS equ 1 ; Bit indicates Comm Send is in progress CWOffset_0 ; Current CW Offset value as loaded from EEPROM CWOffset_1 ; " " " " " " " " BacklightControl ; Backlight control byte ; LIGHT_ON equ 0 ; Bit indicates Backlight ON scratch ; General purpose scratch register ; ENDC ; -------------------------- CBLOCK 0x70 ; Start Shared Data Block (ACCESS FROM ALL BANKS) ; ORDER OF THESE MUST BE SAME AS BANK 1! reserved_for_debugger_B0 ; 0x70 is reserved for ICD-2 comm_in ; Communications byte received comm_out ; Communications byte sent comm_out_temp ; Temporary storage of comm_out s_bit ; Inter-PIC communications s_bit_count ; Inter-PIC communications r_bit ; Inter-PIC communications r_bit_count ; Inter-PIC communications r_bit_wait_count ; Inter-PIC communications gen_parity_byte ; Inter-PIC communications ; (70 - 7F is SHARED!) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 0 Shared Data Block (MAX IS 7F ) ; ; *************************** CBLOCK 0xA0 ; Start Bank 1 Dummy Data Block (16F877) W_Save_bank1_reserve ; Save W (interrupt routine) pb_col ; Column number (isolated from input messge) pb_row ; Row number (isolated from input messge) char_wait_count ; Counter for filling buffer keypad_buffer_0 ; Keypad buffer (first character) keypad_buffer_1 ; " " (referenced indirectly) keypad_buffer_2 ; " " (referenced indirectly) keypad_buffer_3 ; " " (referenced indirectly) keypad_buffer_4 ; " " (referenced indirectly) keypad_BCD_0 ; Keypad packed BCD LS Digit keypad_BCD_1 ; " " " keypad_BCD_2 ; Keypad packed BCD MS Digit keypad_binary_low ; Keypad binary frequency (LS) keypad_binary_high ; " " " (MS) keypad_binary_0 ; Keypad final binary freq (LS) keypad_binary_1 ; " " " " keypad_binary_2 ; " " " " keypad_binary_3 ; Keypad final binary freq (MS) mult_temp_0 ; Scratch for multiply 1K routine mult_temp_1 ; " " " " " mult_temp_2 ; " " " " " mult_temp_3 ; " " " " " BCD_convert_counter ; buffer_count ; Count of valid keypad character ; ENDC ; End of Bank 1 Data Block (MAX IS EF ) ; -------------------------- CBLOCK 0xF0 ; Start Shared Data Block (ACCESS FROM ALL BANKS) ; ORDER OF THESE MUST BE SAME AS BANK 0! reserved_for_debugger_B1 ; 0xF0 (0x070) is reserved for ICD-2 comm_in_bank1 ; Communications byte received (Same as comm_in) comm_out_bank1 ; Communications byte sent (Same as comm_out) comm_out_temp_bank1 ; Temporary storage of comm_out_bank1 (SHARED) s_bit_bank1 ; (SHARED) - Inter-PIC communications s_bit_count_bank1 ; (SHARED) - Inter-PIC communications r_bit_bank1 ; (SHARED) - Inter-PIC communications r_bit_count_bank1 ; (SHARED) - Inter-PIC communications r_bit_wait_count_bank1 ; (SHARED) - Inter-PIC communications gen_parity_byte_bank1 ; (SHARED) - Inter-PIC communications ENDC ; End of Bank 1 Shared Data Block (MAX IS FF ) ; (F0 - FF is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ; ; *************************** CBLOCK 0x120 ; Start Bank 2 Dummy Data Block (16F877) W_Save_bank2_reserve ; Save W (interrupt routine) ; (170 - 17F is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 2 Data Block (MAX IS 17F ) ; ; *************************** CBLOCK 0x1A0 ; Start Bank 3 Dummy Data Block (16F877) W_Save_bank3_reserve ; Save W (interrupt routine) ; (1F0 - 1FF is SHARED with 70 - 7F ) ; (70 is reserved for DEBUGGER use) ENDC ; End of Bank 3 Data Block (MAX IS 1FF ) ; ;**************************************************************************** ;**************************************************************************** ; >>>>>>>>>>>>>>>>>> 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 nop ; Fall through to interrupt-handler_shell interrupt_handler_shell ; (See reason for doing it this way in INT-PIC's interrupt_handler_shell.) movwf Save_W ; Save the contents of W swapf STATUS,w ; Save STATUS contents in W clrf STATUS ; Change to bank 0, regardless of current bank ; (Clears IRP, RP1, RP0) movwf Save_S ; Save status (in bank 0 status save location) movf PCLATH,w ; Move PCLATH into W movwf Save_PCLATH ; and save it clrf PCLATH ; Change to page 0, regardless of current page movf FSR,w ; Move FSR into W movwf Save_FSR ; and save it ; call interrupt_handler_main ; Go do the main interrupt processing ; ; End ISR Code. Now restore to saved state ; movf Save_PCLATH,w ; Restore saved PCLATH into W movwf PCLATH ; and move W into PCLATH movf Save_FSR,w ; Restore saved FSR into W movwf FSR ; and move W into FSR swapf Save_S,w ; Swap status save nibbles, and move into W movwf STATUS ; Move W into STATUS register ; (Bank now in original state) swapf Save_W,f ; Swap nibbles within W_Save ; (for next swap instruction) swapf Save_W,w ; Swap nibbles of W_Save, and move into W retfie ; ;**************************************************************************** ; * 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. * ; * * ; * NOTE: IF THIS TABLE IS CHANGED, MUST UPDATE HIGHTEST_BAND_BASE ALSO! * ;**************************************************************************** ; band_table addwf PCL,f ; retlw 0x00 ; 0 Hz retlw 0x00 ; retlw 0x00 ; retlw 0x00 ; (LS digit) ; ----------------------------------------- retlw 0x00 ; 160 meters retlw 0x1B ; (1.8 MHz) retlw 0x77 ; retlw 0x40 ; (LS digit) ; ----------------------------------------- retlw 0x00 ; 80 meters retlw 0x35 ; (3.5 MHz) retlw 0x67 ; retlw 0xE0 ; (LS digit) ; ----------------------------------------- retlw 0x00 ; 40 meters retlw 0x6A ; (7.0 MHz) retlw 0xCF ; retlw 0xC0 ; (LS digit) ; ----------------------------------------- retlw 0x00 ; 30 meters retlw 0x9A ; (10.1 MHz) retlw 0x1D ; retlw 0x20 ; (LS digit) ; ----------------------------------------- retlw 0x00 ; 20 meters retlw 0xD5 ; (14.0 MHz) retlw 0x9F ; retlw 0x80 ; (LS digit) ; ----------------------------------------- retlw 0x01 ; 17 meters retlw 0x13 ; (18.068 MHz) retlw 0xB2 ; retlw 0x20 ; (LS digit) ; ----------------------------------------- retlw 0x01 ; 15 meters retlw 0x40 ; (21.0 MHz) retlw 0x6F ; retlw 0x40 ; (LS digit) ; ----------------------------------------- retlw 0x01 ; 12 meters retlw 0x7B ; (24.890 MHz) retlw 0xCA ; retlw 0x90 ; (LS digit) ; ----------------------------------------- retlw 0x01 ; 10 meters retlw 0xAB ; (28.0 MHz) retlw 0x3F ; retlw 0x00 ; (LS digit) ; ----------------------------------------- retlw 0x01 ; 30 MHz retlw 0xC9 ; retlw 0xC3 ; retlw 0x80 ; (LS digit) ; ;**************************************************************************** ; * 5 Relay scheme to remove 2nd and higher harmonics in ham band ranges * ; * * ; * LPF 1 0 - 3.499999 MHz (160 Meters 2nd harmonic removed) * ; * LPF 2 3.5 - 6.999999 MHz ( 80 Meters 2nd harmonic removed) * ; * LPF 3 7 - 13.999999 MHz (40,30 Meters 2nd harmonic removed) * ; * LPF 4 14 - 20.999999 MHz (20,17 Meters 2nd harmonic removed) * ; * LPF 5 18.068- 30 MHz (15,12,10 Meters 2nd harmonic removed) * ; * * ;**************************************************************************** ;**************************************************************************** ; * set_band_relay_select * ; * * ; * This is needed because several bands may use the same filter. * ; * Note: need to subtract 1 from LPF number before encoding * ; * * ; * RB7 = LSB of LNA * ; * RB6 = mid of LNA * ; * RB5 = MSB of LNA * ; * RB4 = LSB of LNA * ; * RB3 = mid of LNA * ; * RB2 = MSB of LNA * ; * * ; * Enter with band_number in W * ; * * ; * Return a value in w which is sent to the PORT for the band-switch header* ; * * ; * NORE: If number of bands changes, must update HIGHESTBAND also. * * ;**************************************************************************** ; set_band_relay_select ;(Changed v1.110) addwf PCL,f ; retlw 0x00 ; BN=0 LNA (none) LPF 1 0 - 1.799999 MHz retlw 0x00 ; BN=1 LNA (none) LPF 1 1.8 - 3.499999 MHz retlw 0x90 ; BN=2 LNA 1 LPF 2 3.5 - 6.999999 MHz retlw 0x48 ; BN=3 LNA 2 LPF 3 7.0 - 10.099999 MHz retlw 0xC8 ; BN=4 LNA 3 LPF 3 10.1 - 13.999999 MHz retlw 0x38 ; BN=5 LNA 4 LPF 4 14.0 - 18.067999 MHz retlw 0xB8 ; BN=6 LNA 5 LPF 4 18.068 - 20.999999 MHz retlw 0xA4 ; BN=7 LNA 5 LPF 5 21.0 - 24.889999 MHz retlw 0x64 ; BN=8 LNA 6 LPF 5 24.89 - 27.999999 MHz retlw 0x64 ; BN=9 LNA 6 LPF 5 28.0 - 29.999999 MHz retlw 0x64 ; BN=10 LNA 6 LPF 5 30.0 - 30 MHz ; ;**************************************************************************** ; * reset_band_relay_select table NOT USED IN VERSION 1.110 * ; * * ; * This is needed because several bands may use the same filter. * ; * * ; * Enter with band_number in W * ; * * ; * Return a value in w which is band relay number shifted left by 2. * ; * (This lines up the W value so it can be sent directly to the PORT.) * ; * * ;**************************************************************************** ;THIS CODE IS UNUSED IN v1.110 ;reset_band_relay_select ; addwf PCL,f ; ; retlw 0xFB ; BN=0 Relay 0 0 - 1.799999 MHz ; retlw 0xFB ; BN=1 Relay 0 1.8 - 3.499999 MHz ; retlw 0xF7 ; BN=2 Relay 1 3.5 - 6.999999 MHz ; retlw 0xEF ; BN=3 Relay 2 7.0 - 10.099999 MHz ; retlw 0xEF ; BN=4 Relay 2 10.1 - 13.999999 MHz ; retlw 0xDF ; BN=5 Relay 3 14.0 - 18.067999 MHz ; retlw 0xBF ; BN=6 Relay 4 18.068 - 20.999999 MHz ; retlw 0xBF ; BN=7 Relay 4 21.0 - 24.889999 MHz ; retlw 0xBF ; BN=8 Relay 4 24.89 - 27.999999 MHz ; retlw 0xBF ; BN=9 Relay 4 28.0 - 29.999999 MHz ; retlw 0xBF ; BN=10 Relay 4 30.0 - 30 MHz ; ;**************************************************************************** ; * message_jump table * ; * * ; * Index into table by message number (in W on entry) * ; * Jump to correct routine to handle that message. * ;**************************************************************************** ; message_jump addwf PCL,f ; goto handle_keypad ; MSG 00 - Keypad 1 goto handle_keypad ; MSG 01 - Keypad 2 goto handle_keypad ; MSG 02 - Keypad 3 goto handle_msg03 ; MSG 03 goto handle_msg04 ; MSG 04 goto handle_msg05 ; MSG 05 goto handle_error_msg ; 6 goto handle_error_msg ; 7 goto handle_error_msg ; 8 goto handle_error_msg ; 9 goto handle_error_msg ; A goto handle_error_msg ; B goto handle_error_msg ; C goto handle_error_msg ; D goto handle_error_msg ; E goto handle_error_msg ; F goto handle_keypad ; MSG 10 - Keypad 4 goto handle_keypad ; MSG 11 - Keypad 5 goto handle_keypad ; MSG 12 - Keypad 6 goto handle_msg13 ; MSG 13 goto handle_msg14 ; MSG 14 - SSB Toggle goto handle_msg15 ; MSG 15 goto handle_error_msg ; 16 goto handle_error_msg ; 17 goto handle_error_msg ; 18 goto handle_error_msg ; 19 goto handle_error_msg ; 1A goto handle_error_msg ; 1B goto handle_error_msg ; 1C goto handle_error_msg ; 1D goto handle_error_msg ; 1E goto handle_error_msg ; 1F goto handle_keypad ; MSG 20 - Keypad 7 goto handle_keypad ; MSG 21 - Keypad 8 goto handle_keypad ; MSG 22 - Keypad 9 goto handle_msg23 ; MSG 23 goto handle_msg24 ; MSG 24 - Tone Set toggle goto handle_msg25 ; MSG 25 goto handle_error_msg ; 26 goto handle_error_msg ; 27 goto handle_error_msg ; 28 goto handle_error_msg ; 29 goto handle_error_msg ; 2A goto handle_error_msg ; 2B goto handle_error_msg ; 2C goto handle_error_msg ; 2D goto handle_error_msg ; 2E goto handle_error_msg ; 2F goto handle_keypad ; MSG 30 - * goto handle_keypad ; MSG 31 - 0 goto handle_keypad ; MSG 32 - # goto handle_msg33 ; MSG 33 goto handle_msg34 ; MSG 34 - Backlight toggle goto handle_msg35 ; MSG 35 ; ;**************************************************************************** ; * translate_to_digit table * ; * * ; * Index into table by message number (in W on entry) * ; * (only keypad messages come here) * ; * Return with keypad digit in W * ; * Invalid messages return FF in W * ;**************************************************************************** ; translate_to_digit addwf PCL,f ; retlw 0x01 ; MSG 00 - 1 retlw 0x02 ; MSG 01 - 2 retlw 0x03 ; MSG 02 - 3 retlw 0xFF ; MSG 03 retlw 0xFF ; MSG 04 retlw 0xFF ; MSG 05 retlw 0xFF ; 06 retlw 0xFF ; 07 retlw 0xFF ; 08 retlw 0xFF ; 09 retlw 0xFF ; 0A retlw 0xFF ; 0B retlw 0xFF ; 0C retlw 0xFF ; 0D retlw 0xFF ; 0E retlw 0xFF ; 0F retlw 0x04 ; MSG 10 - 4 retlw 0x05 ; MSG 11 - 5 retlw 0x06 ; MSG 12 - 6 retlw 0xFF ; MSG 13 retlw 0xFF ; MSG 14 retlw 0xFF ; MSG 15 retlw 0xFF ; 16 retlw 0xFF ; 17 retlw 0xFF ; 18 retlw 0xFF ; 19 retlw 0xFF ; 1A retlw 0xFF ; 1B retlw 0xFF ; 1C retlw 0xFF ; 1D retlw 0xFF ; 1E retlw 0xFF ; 1F retlw 0x07 ; MSG 20 - 7 retlw 0x08 ; MSG 21 - 8 retlw 0x09 ; MSG 22 - 9 r