; ################# 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 retlw 0xFF ; MSG 23 retlw 0xFF ; MSG 24 retlw 0xFF ; MSG 25 retlw 0xFF ; 26 retlw 0xFF ; 27 retlw 0xFF ; 28 retlw 0xFF ; 29 retlw 0xFF ; 2A retlw 0xFF ; 2B retlw 0xFF ; 2C retlw 0xFF ; 2D retlw 0xFF ; 2E retlw 0xFF ; 2F retlw 0xFF ; MSG 30 - * retlw 0x00 ; MSG 31 - 0 retlw 0xFF ; MSG 32 - # retlw 0xFF ; MSG 33 retlw 0xFF ; MSG 34 retlw 0xFF ; MSG 35 ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>> START AND MAIN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Name: start * ; * * ; * 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 pagesel init_Driver_PIC ; Switch to PAGE 1 call init_Driver_PIC ; Initialize the 16F877 (PAGE 1) call init_LCD ; Initialize the LCD (PAGE 1) call display_mcode_version ; Display mcode version on LCD for 2 sec. (PAGE 1) call init_DDS ; Initialize the AD9854 (PAGE 1) call init_freq ; Initialize default frequency and registers (PAGE 1) call init_mode ; Initialize mode (PAGE 1) clrf band_index ; Initialize this to force update (v1.110) pagesel update_band_relays_DDS_LCD call update_band_relays_DDS_LCD ; Set default relay, DDS freq, display (PAGE 0) pagesel allow_interrupts call allow_interrupts ; Now allow interrupts (PAGE 1) pagesel start ; Back to PAGE 0 ; ; Fall into the Main Program Loop ; ;***************************************************************************** ; * Name: main * ; * * ; * Purpose: This is the main program loop. * ; * * ; * 1) Check for any input message. (keypad or other pushbutton) * ; * If so, perform appropriate action, set NEEDUPDATE flag * ; * 2) Check to see if frequency encoder knob has been turned * ; * If so, set NEEDUPDATE flag * ; * 3) Check for transmitter active * ; * If so, set flag for possible frequency shift * ; * 4) If NEEDUPDATE flag set, call routine to update DDS and LCD * ; * * ; * Input: None. * ; * * ; * Output: None. * ;***************************************************************************** ; main call handle_incoming ; Look for incoming messages from Int-PIC call transmitter_active_look ; See if transmitter is active btfsc VFOcontrol,NEEDUPDATE ; Need a DDS and LCD update? call update_band_relays_DDS_LCD ; Yes, update the band relays and ; the DDS and LCD goto main ; Loop forever ; ;***************************************************************************** ; * Name: handle_incoming * ; * * ; * Purpose: See if Interface PIC is trying to send the Driver PIC 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 ; Check W to see if message is waiting goto handle_incoming_exit ; W=0 if NO MESSAGE pagesel receive_byte ; W=1, so message exists. 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,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again goto handle_incoming_exit ; Exit look_at_incoming ; See if it's a Heartbeat message movlw 0x3A ; See if msg is subwf comm_in,w ; a heartbeat (from INT) btfss STATUS,Z ; Look at Zero flag goto look_not_heartbeat ; Zero NOT set so not heartbeat. Continue call handle_heartbeat ; Zero is set so it's heartbeat. Go handle goto handle_incoming_exit ; and exit ; look_not_heartbeat ; Not heartbeat. Continue checking ; See if it's an Encoder_message (MS bit set) ; btfss comm_in,ENCMSG ; Is encoder_message bit set? goto look_not_encmsg ; No, look_not_encmsg call handle_encoder_msg ; Yes, go handle encoder message goto handle_incoming_exit ; and exit look_not_encmsg ; ; Check for legal Interface message. ; Legal I->D pushbutton messages will be numbers 0 - 35 ; (No more valid messages yet) ; movlw 0x36 ; Highest legal 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_invalid ; C set, so msg >= 36, so it's bad call handle_pbs ; Handle the pushbutton goto handle_incoming_exit ; and exit ; incoming_invalid ; ; Interface-messages above 35 are invalid. ; (Encoder messages already handled) ; bsf PORTC,LEDERRD ; It's invalid, so turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again ; handle_incoming_exit return ; Return ; ;***************************************************************************** ; * Name: handle_encoder_msg * ; * * ; * All in Bank 0 * ; * * ; * Purpose: This routine will handle the incoming encoder message. * ; * It will determine the direction and amount of encoder movement * ; * based on the message number: * ; * Bit 7 indicates it's an Encoder message * ; * Bit 6 indicates direction (1 = clockwise) * ; * Bit 5 MS bit of tick counter * ; * - * ; * Bit 0 LS bit of tick counter * ; * * ; * This scheme gives 6 bits of tick count magnitude, which is 63. * ; * However, we also use a value of 0 to indicate one tick. * ; * (No message gets sent if no ticks are found.) * ; * * ; * This routine uses an acceleration mechanism to spread out the * ; * 64 possible values into a range of frequency change values. * ; * * ; * * ; * Input: comm_in set up with incoming encoder message * ; * (Shared cell - Access in bank0 via comm_in) * ; * (Shared cell - Access in bank1 via comm_in_bank1) * ; * * ; * Output: fstep_3:fstep_0 set up * ; * workfreq0:workfreq3 set up (via call to add_step or sub_step) * ; * Afreq or Bfreq set up (via call to VFO_freq_save) * ; * NEEDUPDATE flag set in VFOcontrol to trigger the DDS/LCD update * ; * * ;***************************************************************************** ; handle_encoder_msg movf comm_in,w ; Get encoder message in W movwf tick_count0 ; Set up tick_count LS Byte ; btfss VFOaction,VFOCALIBRATE ; Is Calibrate active? goto enc_not_calibrate ; No, continue handling msg pagesel calibrate_action ; Switch to PAGE 1 call calibrate_action ; Yes, go perform Calibrate action pagesel start ; Back to PAGE 0 goto enclook_ret ; Go to exit. ; It's not in calibrate mode. enc_not_calibrate btfss VFOaction,TONE_SET_ACTIVE ; Is tone set active? goto enc_not_tone_set ; No, continue handling msg pagesel tone_set_action ; Switch to PAGE 1 call tone_set_action ; Yes, go perform tone set action pagesel start ; Back to PAGE 0 goto enclook_ret ; Go to exit. ; It's not in tone_set mode. enc_not_tone_set bcf tick_count0,ENCMSG; Clear encoder message indicator bsf VFOcontrol,DIR_UP ; Set direction to go UP (default) btfss tick_count0,ENCDIR; Is direction bit set? (indicating UP) bcf VFOcontrol,DIR_UP ; No, clear UP direction bit (go DN) bcf tick_count0,ENCDIR; Clear dir flag from encoder message ; ; Now calculate fstep amount based on tick_count clrf tick_count2 ; Initialize clrf tick_count1 ; Initialize ; btfss tick_count0,5 ; Is bit 5 set? goto enc_check_4 ; movlw TUNE6 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE6 ; Yes, add a factor goto enc_shift_it enc_check_4 btfss tick_count0,4 ; Is bit 4 set? goto enc_check_3 ; movlw TUNE5 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE5 ; Yes, add a factor goto enc_shift_it enc_check_3 btfss tick_count0,3 ; Is bit 3 set? goto enc_check_2 movlw TUNE4 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE4 ; Yes, add a factor goto enc_shift_it enc_check_2 btfss tick_count0,2 ; Is bit 2 set? goto enc_check_1 movlw TUNE3 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE3 ; Yes, add a factor goto enc_shift_it enc_check_1 btfss tick_count0,1 ; Is bit 1 set? goto enc_check_0 movlw TUNE2 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE2 ; Yes, add a factor goto enc_shift_it enc_check_0 btfss tick_count0,0 ; Is bit 0 set? goto enc_check_none movlw TUNE1 ; Yes, put shift count in W btfsc VFOcontrol,FAST_TUNE ; Is fast_tune active? addlw FASTTUNE1 ; Yes, add a factor goto enc_shift_it enc_check_none movlw 0x01 ; Set up registers with 1 movwf tick_count0 ; Jam into low byte goto enc_shift_done ; No shifting this time ; enc_shift_it movwf enc_shift_count enc_shift_it_loop bcf STATUS,C ; Clear carry rlf tick_count0,f ; Rotate 0 into count0, MS bit goes to Carry rlf tick_count1,f ; Rotate C into count1, MS bit goes to Carry rlf tick_count2,f ; Rotate C into count2, MS bit goes to Carry decfsz enc_shift_count,f ; Done? goto enc_shift_it_loop ; No, go back and shift again ; enc_shift_done clrf fstep_3 ; Never use this digit here movf tick_count2,w ; Get MS digit movwf fstep_2 ; and move to fstep_2 movf tick_count1,w ; Get next digit movwf fstep_1 ; and move to fstep_1 movf tick_count0,w ; Get LS digit movwf fstep_0 ; and move to fstep_0 ; ; Get current frequency to be updated from selected VFO (Afreq or Bfreq) ; and load into ; call VFO_freq_load ; Load with Afreq or Bfreq btfss VFOcontrol,DIR_UP ; Is it UP direction? goto enc_need_sub ; No, go subtract the step call add_step ; Yes, add step and update call check_add ; See if band limits reached. If so, adjust, ; and update goto enc_step_done ; Back to common code enc_need_sub call sub_step ; Subtract a step and update enc_step_done 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 ; ;***************************************************************************** ; * Name: handle_heartbeat * ; * * ; * All in Bank 0 * ; * * ; * Purpose: This routine will handle the incoming heartbeat message and * ; * send the heartbeat_response message back to the INT PIC. * ; * * ; * Input: comm_in_bank1 set up with incoming heartbeat message * ; * (Shared cell - Access in bank1 via comm_in_bank1) * ; * ( - Access in bank0 via comm_in) * ; * * ; * Output: None. * ;***************************************************************************** ; handle_heartbeat movlw 0x3B ; Get heartbeat_response 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 btfsc STATUS,Z ; Good result? goto handle_heartbeat_exit ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again handle_heartbeat_exit return ; ; ;***************************************************************************** ; * Name: handle_pbs * ; * NOTE: DOES NOT RETURN TO CALLER. * ; * JUMPS TO APPROPRIATE MESSAGE HANDLER, which returns to caller. * ; * * ; * Enter in Bank 0 * ; * SWITCH TO BANK 1 AT BEGINNING OF ROUTINE * ; * Exit in Bank 0 * ; * * ; * Purpose: This routine will handle all pushbuttons. * ; * * ; * Input: comm_in set up with incoming button-press message * ; * (Shared cell - Access in bank1 via comm_in_bank1) * ; * ( - Access in bank0 via comm_in) * ; * * ; * Output: * ;***************************************************************************** ; handle_pbs banksel comm_in_bank1 ; Switch to Bank 1 movlw 0xF ; Isolate column (lowest 4 bits) andwf comm_in_bank1,w ; from row/col movwf pb_col ; Column now set valid movf comm_in_bank1,w ; Get row/column again movwf pb_row ; swapf pb_row,f ; Move row to lowest 4 bits movlw 0xF ; Isolate row andwf pb_row,f ; and save result movf comm_in_bank1,w ; Get row/column again goto message_jump ; Go to proper message handler ; ;***************************************************************************** ; * Name: handle_error_msg * ; * * ; * Enter in Bank 1 * ; * Exit in Bank 0 * ; * * ; * Purpose: This routine will handle illegal error message numbers. * ; * It will simply light LEDERRD for one second and then exit. * ; * * ; * Input: * ; * * ; * Output: * ;***************************************************************************** ; handle_error_msg ; ; Not found yet, so error. ; Messages above 35 are invalid banksel PORTA ; Back to Bank 0 bsf PORTC,LEDERRD ; It's invalid, so turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again handle_pbs_exit return ; In Bank 0 ; ;***************************************************************************** ; * Name: handle_keypad * ; * * ; * Enter using Bank 1 * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: This routine will handle the pushbuttons which require an * ; * immediate VFO frequency change via direct keyboard input * ; * * ; * Input: W = decoded keypad digit * ; * comm_in set up with incoming button-press message * ; * * ; * Output: W=0 (good) * ; * W=1 (error) * ;***************************************************************************** ; handle_keypad ; See if it's a *-key movlw 0x30 ; Get *-key message number subwf comm_in_bank1,w ; Compare to input message (bank1) btfss STATUS,Z ; See if it matches goto keypad_look ; No, continue looking ; Yes, *-detected. Start keypad entry. banksel PORTA ; Switch to bank 0 btfsc VFOaction,KEYPAD_ACTIVE ; Keypad buffering in progress? (bank0) goto keypad_action ; Yes, it's now complete, so go take action ; No, we're just starting, so initialize banksel TRISA ; Back to Bank 1 ; Initialize the buffer pointer clrf buffer_count ; Initialize count (bank1) clrf keypad_buffer_0 ; Initialize (bank1) clrf keypad_buffer_1 ; Initialize (bank1) clrf keypad_buffer_2 ; Initialize (bank1) clrf keypad_buffer_3 ; Initialize (bank1) clrf keypad_buffer_4 ; Initialize (bank1) banksel PORTA ; Switch to bank 0 ; ; Write "-----" on second line of LCD ; movlw 0xC0 ; Point LCD at digit 0 of 2nd line call cmnd2LCD ; movlw '-' ; Clear char 0 of first line call data2LCD ; movlw '-' ; Char 1 of first line call data2LCD ; movlw '-' ; Char 1 of first line call data2LCD ; movlw '-' ; Char 1 of first line call data2LCD ; movlw '-' ; Char 1 of first line call data2LCD ; ; bsf VFOaction,KEYPAD_ACTIVE ; Say keypad buffering in progress goto keypad_exit ; ; keypad_look banksel PORTA ; Switch to bank 0 btfss VFOaction,KEYPAD_ACTIVE ; Keypad buffering in progress? (bank0) goto keypad_exit ; No, ignore the keypad message ; See if it's a #-key movlw 0x32 ; Get #-key message number banksel TRISA ; Switch to bank 0 subwf comm_in_bank1,w ; Compare to input message (bank1) btfsc STATUS,Z ; See if it matches goto keypad_exit ; Yes, #-detected. Ignore it and exit ; No, it must be a number key so buffer it buffer_it movlw keypad_buffer_0 ; Get ADDRESS OF start of buffer (bank1) addwf buffer_count,w ; add on the current buffer count (bank1) movwf FSR ; and put it in FSR (any bank) movf comm_in_bank1,w ; Get the input message byte (bank1) call translate_to_digit; Translate msg to digit (any bank) movwf INDF ; and store digit in buffer (any bank) banksel PORTA ; Bank 0 addlw 0x30 ; Convert to ASCII movwf comm_in_digit ; Save digit (bank0) banksel TRISA ; Bank 1 ; Write digit to second line of LCD movlw 0xC0 ; Point LCD at digit 0 of 2nd line addwf buffer_count,w ; Add on buffer count (position) banksel PORTA ; Switch to bank 0 call cmnd2LCD ; Bank 0 movf comm_in_digit,w ; Get character in W (bank0) call data2LCD ; Write the digit banksel TRISA ; Bank 1 incf buffer_count,f ; Point to location for NEXT digit (bank1) movlw 0x05 ; Check for max characters in buffer subwf buffer_count,w ; (bank1) btfss STATUS,Z ; Are we at max yet? (Z set if equal) goto keypad_exit ; No, exit and keep looking ; Yes, quit buffering and take action now ; keypad_action ; *-key found, so take action now ; First, check to see if in range movlw 0x03 ; See if greater than 30 MHz banksel TRISA ; Switch to Bank 1 subwf keypad_buffer_0,w ; MS digits (ten-thousands digit) (bank1) btfss STATUS,C ; Look at Carry (set if positive or zero) goto keypad_buffer_range_OK ; Yes, range is OK. movlw 0x03 ; Set up default (eventually 30 MHz) movwf keypad_buffer_0 ; Put in MS digit (bank1) clrf keypad_buffer_1 ; Clear the (bank1) clrf keypad_buffer_2 ; rest of (bank1) clrf keypad_buffer_3 ; the (bank1) clrf keypad_buffer_4 ; digits (bank1) keypad_buffer_range_OK ; Make a binary number from the individual digits pagesel bcd2packedbcd ; Switch to PAGE 1 call bcd2packedbcd ; Pack the characters (PAGE 1)(bank1) call bcd2bin16 ; Make a binary number in (PAGE 1)(bank1) ; keypad_binary_high:keypad_binary_low keypad_ready_mult call multiply_by_1k ; Convert kHz (assumed) to Hz (PAGE 1)(bank1) pagesel start ; Back to PAGE 0 ; ; Now set up to jump to that frequency on (RX VFO only) ; banksel VFOcontrol ; Back to Bank 0 btfss VFOcontrol,VFOA ; Is VFO A selected for rx ? (bank0) goto keypad_VFOB ; No, go update VFO-B instead banksel TRISA ; Select bank 1 movf keypad_binary_0,w ; Move final binary value to work freq (bank1) banksel PORTA ; Select bank 0 movwf Afreq_0 ; update VFO-A (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_1,w ; (bank1) banksel PORTA ; Select bank 0 movwf Afreq_1 ; update VFO-A (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_2,w ; (bank1) banksel PORTA ; Select bank 0 movwf Afreq_2 ; update VFO-A (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_3,w ; (bank1) banksel PORTA ; Select bank 0 movwf Afreq_3 ; update VFO-A (bank0) goto keypad_VFO_OK keypad_VFOB banksel TRISA ; Select bank 1 movf keypad_binary_0,w ; Move final binary value to work freq (bank1) banksel PORTA ; Select bank 0 movwf Bfreq_0 ; update VFO-B (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_1,w ; (bank1) banksel PORTA ; Select bank 0 movwf Bfreq_1 ; update VFO-B (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_2,w ; (bank1) banksel PORTA ; Select bank 0 movwf Bfreq_2 ; update VFO-B (bank0) banksel TRISA ; Select bank 1 movf keypad_binary_3,w ; (bank1) banksel PORTA ; Select bank 0 movwf Bfreq_3 ; update VFO-B (bank0) keypad_VFO_OK ; ; Set flag to get a DDS, LCD update on next pass through main loop ; (In Bank 0) bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD (bank0) bcf VFOaction,KEYPAD_ACTIVE ; Say keypad buffering done (bank0) ; ; Write current mode on second line of LCD ; pagesel display_mode ; Switch to PAGE 1 call display_mode ; Dsplay Mode on LCD pagesel start ; Back to PAGE 0 keypad_exit banksel PORTA ; Exit in Bank 0 return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg03 - MHz UP * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Decrease the frequency of the active VFO by 1 MHz. * ; * * ; * * ; * Input: * ; * * ; * Output: NEEDUPDATE flag set in VFOcontrol * ; * or set up * ;***************************************************************************** ; handle_msg03 ; (MHz UP) banksel PORTA ; Switch to Bank 0 clrf fstep_3 ; movlw 0x0F ; Set 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 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 return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg04 - Split Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: * ; * * ; * * ; * * ; * Input: SPLIT flag in VFOcontrol * ; * * ; * Output: SPLIT flag in VFOcontrol updated * ;***************************************************************************** ; handle_msg04 ; (Split Toggle) ; ; See if SPLIT already set. If so, this button press is to END SPLIT ; banksel PORTA ; Switch to Bank 0 btfss VFOcontrol,SPLIT ; Is SPLIT already set? goto SetSplit ; No, go to set SPLIT bcf VFOcontrol,SPLIT ; Yes, Clear SPLIT control flag 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 ; ; ; Send message to Interface PIC (SPLIT LED OFF) ; movlw 0x54 ; Get 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 btfsc STATUS,Z ; Good result? goto SplitLEDOffDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again SplitLEDOffDone goto msg04_done ; Done with button SetSplit bsf VFOcontrol,SPLIT ; Button SPLIT is pressed. Set control flag 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 ; ; ; Send message to Interface PIC (SPLIT LED ON) ; movlw 0x44 ; Get 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 btfsc STATUS,Z ; Good result? goto SplitLEDOnDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again SplitLEDOnDone ; msg04_done return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg05 - A/B Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Change the inactive VFO to active and the active VFO to * ; * inactive. * ; * * ; * * ; * Input: VFOA or VFOB in VFOcontrol indicates active VFO * ; * * ; * Output: NEEDUPDATE flag set in VFOcontrol * ; * Opposite VFOA or VFOB set in VFOcontrol * ;***************************************************************************** ; handle_msg05 ; (VFO A/B Toggle) ; ; Toggle pressed. Change VFO and update ; banksel PORTA ; Switch to Bank 0 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 bsf VFOcontrol,VFOCHANGED; Set flag to say LED update msg needed ; Msg is sent by update_band_relays_DDS_LCD goto toggle_done ; ; buttons_select_VFOA bsf VFOcontrol,VFOA ; Select VFO A bcf VFOcontrol,VFOB ; and clear VFO B bsf VFOcontrol,VFOCHANGED; Set flag to say LED update msg needed ; Msg is sent by update_band_relays_DDS_LCD ; toggle_done bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg13 - MHz DN * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Increase the frequency of the active VFO by 1 MHz. * ; * * ; * * ; * Input: * ; * * ; * Output: NEEDUPDATE flag set in VFOcontrol * ; * or set up * ;***************************************************************************** ; handle_msg13 ; (MHz DN) banksel PORTA ; Switch to Bank 0 clrf fstep_3 ; movlw 0x0F ; Set 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 ; Sub. 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 return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg14 - Mode Cycle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Toggle the ModeSelect mode to next mode * ; * * ; * Input: ModeSelect bit set * ; * * ; * Output: ModeSelect (next in cycle - LSB, USB, CW, RevCW * ; * LEDLSB and LEDUSB ON/OFF as appropriate * ;***************************************************************************** ; handle_msg14 ; (Mode Cycle) banksel PORTA ; Switch to Bank 0 btfsc ModeSelect,LSB ; Is it LSB? goto Msg14_ChangeToUSB ; Yes, go and change to USB btfsc ModeSelect,USB ; Is it LSB? goto Msg14_ChangeToCW ; Yes, go and change to USB btfsc ModeSelect,CW ; Is it CW? goto Msg14_ChangeToRevCW; Yes, go and change to Reverse CW ; fall through to Msg14_ChangeToLSB ; No, it must be RevCW so change to LSB ; Msg14_ChangeToLSB clrf ModeSelect ; Change ModeSelect bsf ModeSelect,LSB ; to USB ; Send message 40 to turn LED1 ON (USB/RevCW) and LED2 OFF movlw 0x40 ; Get message number for SSB LED 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 btfsc STATUS,Z ; Good result? goto Msg14_WriteLSB ; Yes, write LSB on LCD bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again goto Msg14_exit ; and exit Msg14_WriteLSB ; Now write Mode on LCD pagesel display_mode ; Switch to PAGE 1 call display_mode ; Display the mode on LCD pagesel start ; Back to PAGE 0 goto Msg14_SetIQRelay ; SET the IQ Relay ; Msg14_ChangeToUSB clrf ModeSelect ; Change ModeSelect bsf ModeSelect,USB ; to USB ; Send message 41 to turn LED2 ON (USB/RevCW) and LED1 OFF movlw 0x40 ; Get message number for SSB LED 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 btfsc STATUS,Z ; Good result? goto Msg14_WriteUSB ; Yes, go update LCD also bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again goto Msg14_exit ; and exit Msg14_WriteUSB ; Now write Mode on LCD pagesel display_mode ; Switch to PAGE 1 call display_mode ; Display the mode on LCD pagesel start ; Back to PAGE 0 goto Msg14_ResetIQRelay; RESET the IQ Relay ; Msg14_ChangeToCW clrf ModeSelect ; Change ModeSelect bsf ModeSelect,CW ; to CW ; Send message 40 to turn LED1 ON (LSB/CW) and LED2 OFF movlw 0x41 ; Get message number for CW LED 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 btfsc STATUS,Z ; Good result? goto Msg14_WriteCW ; Yes, write CW on LCD bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again goto Msg14_exit ; and exit Msg14_WriteCW ; Now write Mode on LCD pagesel display_mode ; Switch to PAGE 1 call display_mode ; Display the mode on LCD pagesel start ; Back to PAGE 0 goto Msg14_SetIQRelay ; SET the IQ Relay ; Msg14_ChangeToRevCW clrf ModeSelect ; Change ModeSelect bsf ModeSelect,RevCW ; to Reverse CW ; Send message 41 to turn LED2 ON (USB/RevCW) and LED1 OFF movlw 0x41 ; Get message number for CW LED 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 btfsc STATUS,Z ; Good result? goto Msg14_WriteRevCW ; Yes, go update LCD also bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again goto Msg14_exit ; and exit Msg14_WriteRevCW ; Now write Mode on LCD pagesel display_mode ; Switch to PAGE 1 call display_mode ; Display the mode on LCD pagesel start ; Back to PAGE 0 goto Msg14_ResetIQRelay; RESET the IQ Relay ; Msg14_SetIQRelay ; bsf PORTB,IQRelaySet ; No, raise SET bit, nop ; let it settle (Read-modify-write problem), bcf PORTB,IQRelayReset ; and then drop RESET bit goto IQSelectReady ; Go and set/reset the relay Msg14_ResetIQRelay ; bsf PORTB,IQRelayReset ; No, raise RESET bit, nop ; let it settle (Read-modify-write problem), bcf PORTB,IQRelaySet ; and then drop SET bit IQSelectReady call wait_8ms ; Wait for Relay to set/reset ;clrf PORTB ; (old) Clear all port bits (clears BANDSWITCH bits too!) ; ; Write mode to EEPROM ; banksel EEADR ; Select bank 2 movlw 0x06 ; Mode at EEPROM address 4 movwf EEADR ; Set the EEPROM read address banksel PORTA ; Select bank 0 movf ModeSelect,w ; Get the Mode banksel EEDATA ; Select bank 2 movwf EEDATA ; Set up mode byte as EEDATA call write_EEPROM ; Write it to EEPROM and increment address ; (Sets up Bank3, EEDATA to EEPROM, exits in Bank0) bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD Msg14_exit return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg15 - VFOA = VFOB * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Set frequency of the inactive VFO to the frequency of the * ; * active VFO. * ; * * ; * * ; * Input: * ; * * ; * * ; * Output: and are the equal * ;***************************************************************************** ; handle_msg15 ; (VFO A=B) ; ; First find active VFO and set this into other VFO. ; banksel PORTA ; Switch to Bank 0 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 msg15_exit ; and exit 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 msg15_exit return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg23 - Band UP * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Change frequency of active VFO to start of next higher amateur * ; * band (USA). The band table can easily be updated to * ; * accommodate band tables for other countries. * ; * * ; * * ; * Input: band contains the current base index (i.e. band number * 4) * ; * * ; * * ; * Output: NEEDUPDATE flag set in VFOcontrol * ; * new_band contains the base index (i.e. new band number * 4) * ; * or set up * ;***************************************************************************** ; handle_msg23 ; (Band UP) ; ; NOTE: DO NOT update band here. Just find base of next band and then ; update . Do the band update ONLY IN ; update_DDS_LCD, so band relays get updated properly also. ; banksel PORTA ; Switch to Bank 0 movf band_index,w ; Get current band pointer addlw 0x04 ; Increment the band pointer (4 entries apart) movwf new_band ; and save new band base movlw 0xFF-highest_band_base + 4 ; Check to see if we have gone over the addwf new_band,w ; top of the table (not including 30 MHz). btfss STATUS,C ; Did we go over the top of the table? goto msg32_update ; No, continue movlw 0x04 ; Get index for bottom band (not zero) movwf new_band ; and go to it. (wrap around) msg32_update call update_workfreq_newband ; Update using new_band call VFO_freq_save ; Save workfreq_ into AFreq_ or BFreq_ bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg24 - Tone Set Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Calibrate mode allows the user to adjust the constants * ; * ref_osc_3:ref_osc_0 (as stored in EEPROM). * ; * When CALIBRATE mode is started the VFO jumps to 10.0 MHz. * ; * Now, turning the encoder changes the frequency constants * ; * slightly such that the DDS output frequency changes. When * ; * leaving CALIBRATE mode, the new constants will be stored * ; * back in EEPROM. The new constants are "permanent" and will * ; * be in effect until the next CALIBRATE or until the 16F877 * ; * is reprogrammed, restoring the defaults. * ; * * ; * * ; * Input: VFOCALIBRATE flag in VFOaction * ; * osc_3:osc_0 in EEPROM * ; * * ; * Output: VFOCALIBRATE flag in VFOaction updated * ; * New osc_3:osc_0 stored in EEPROM * ;***************************************************************************** ; handle_msg24 ; (Tone Set Toggle) banksel PORTA ; Switch to Bank 0 btfsc VFOaction,TONE_SET_ACTIVE ; Is Tone Set already active? goto TS_active_need_turnoff ; Yes, go and turn it off ; bsf VFOaction,TONE_SET_ACTIVE ; Say Tone Set is now active ; ; Read the tone value from EEPROM. ; banksel EEADR ; Select bank 2 movlw 0x04 ; First byte of CW Offset is at EEPROM address 4 movwf EEADR ; Set the EEPROM read address call read_EEPROM ; Read EEPROM and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf CWOffset_0 ; Save offset value - LS byte movwf freq_0 ; Save here too - for bin2BCD call read_EEPROM ; Get next byte and increment address ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf CWOffset_1 ; Save Offset value - MS byte movwf freq_1 ; Save here too - for bin2BCD clrf freq_2 ; Clear unused byte clrf freq_3 ; Clear unused byte ; ; Note: it's OK to use freq bytes here. They do not retain actual frequency long term. ; pagesel bin2BCD ; Switch to PAGE 1 call bin2BCD ; Calculate BCD version of side tone value ; Uses freq regs, returns with BCD regs set up call show_tone ; Display the tone value on the LCD pagesel start ; Back to PAGE 0 ; goto msg24_exit ; Go to exit and return to caller TS_active_need_turnoff ; ; A message was received from Interface PIC, so terminate the Calibrate. ; ; Write final value to EEPROM banksel EEADR ; Select bank 2 movlw 0x04 ; First byte of Offset is at EEPROM address 4 movwf EEADR ; Set the EEPROM read address banksel PORTA ; Select bank 0 movf CWOffset_0,w ; Get the MS offset byte banksel EEDATA ; Select bank 2 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 CWOffset_1,w ; Get the LS offset byte banksel EEDATA ; Select bank 2 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 0x81 ; Point LCD at digit 1 of 1st line call cmnd2LCD ; movlw ' ' ; Clear it call data2LCD ; movlw ' ' ; Clear it call data2LCD ; movlw 0x8F ; Point LCD at last digit of 1st line call cmnd2LCD ; movlw ' ' ; Clear it call data2LCD ; ; bcf VFOaction,TONE_SET_ACTIVE ; Say Tone Set is NOT active bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD ; msg24_exit return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg25 - Fast Tune Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: This is a toggle. Routine is entered to turn fast tuning on * ; * and off. When fast-tune is enabled, the frequency change * ; * acceleration rate is increased. * ; * * ; * * ; * Input: FAST_TUNE flag in VFOcontrol * ; * * ; * Output: FAST_TUNE flag in VFOcontrol updated * ;***************************************************************************** ; handle_msg25 ; (FastTune toggle) ; ; See if fast tuning already set? ; banksel PORTA ; Switch to Bank 0 btfsc VFOcontrol,FAST_TUNE ; Is Fast Tuning already set? goto buttons_clear_fast_tuning ; Yes, go and clear it bsf VFOcontrol,FAST_TUNE ; No, set Fast Tuning movlw 0x8F ; Point LCD at digit 15 of 1st line call cmnd2LCD ; movlw '+' ; Set Fast Tuning indicator call data2LCD ; Update LCD ; ; Send message to Interface PIC to turn on LEDFASTTUNE ; movlw 0x45 ; Get 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 btfsc STATUS,Z ; Good result? goto buttonsFTOnDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again buttonsFTOnDone goto msg25_exit ; and exit ; buttons_clear_fast_tuning bcf VFOcontrol,FAST_TUNE ; Clear Fast tuning movlw 0x8F ; Point LCD at digit 15 of 1st line call cmnd2LCD ; movlw ' ' ; Clear Fast Tuning indicator call data2LCD ; Update LCD ; ; Send message to Interface PIC to turn off LEDFASTTUNE ; movlw 0x55 ; Get 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 btfsc STATUS,Z ; Good result? goto buttonsFTOffDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again buttonsFTOffDone msg25_exit return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg33 - Band DN * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Change frequency of active VFO to start of next lower * ; * amateur band (USA). The band table can be updated to * ; * accommodate band tables for other countries. * ; * * ; * Input: band contains the current base index (i.e. band number * 4) * ; * * ; * Output: NEEDUPDATE flag set in VFOcontrol * ; * new_band contains the base index (i.e. desired band number * 4) * ; * or set up * ;***************************************************************************** ; handle_msg33 ; (Band DN) ; ; NOTE: DO NOT update band here. Just find base of next band and then ; update . Do the band update ONLY IN ; update_DDS_LCD, so band relays get updated properly also. ; banksel PORTA ; Switch to Bank 0 movf band_index,w ; Get current band pointer movwf new_band ; and save new band base movlw 0x04 ; Move down in band list (4 entries apart) subwf new_band,f ; Store it back btfss STATUS,Z ; Did we hit Zero? goto msg33_update ; No, continue movlw highest_band_base ; Yes, go to highest band (wrap around) movwf new_band ; Store it movlw 0x04 ; Back down by 4 (don't use 30 MHz) subwf new_band,f ; Store it back msg33_update call update_workfreq_newband ; Update using new_band call VFO_freq_save ; Save workfreq_ into AFreq_ or BFreq_ bsf VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD return ; Return to caller ; ;***************************************************************************** ; * Name: handle_msg34 - LCD Backlight Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Toggle the LCD Backlight ON/OFF. * ; * * ; * Input: BacklightControl,LIGHT_ON = 1 if light on, 0 if light off * ; * * ; * Output: Backlight is toggled and BacklightControl,LIGHT_ON is toggled. * ;***************************************************************************** ; handle_msg34 banksel PORTA ; Switch to Bank 0 btfss BacklightControl,LIGHT_ON ; Is backlight ON? goto turn_backlight_on ; No, go to turn it ON bcf PORTC,BACKLIGHT ; Yes, turn it OFF bcf BacklightControl,LIGHT_ON ; Turn backlight flag OFF goto handle_msg34_exit ; and exit ; turn_backlight_on bsf PORTC,BACKLIGHT ; NO, turn it ON bsf BacklightControl,LIGHT_ON ; Turn backlight flag ON ; handle_msg34_exit return ; ;***************************************************************************** ; * Name: handle_msg35 - Calibrate Toggle * ; * * ; * Enter in Bank 1 * ; * Change to Bank 0 at start of the routine * ; * Exit in bank 0 <- NOTE * ; * * ; * Purpose: Calibrate mode allows the user to adjust the constants * ; * ref_osc_3:ref_osc_0 (as stored in EEPROM). * ; * When CALIBRATE mode is started the VFO jumps to 10.0 MHz. * ; * Now, turning the encoder changes the frequency constants * ; * slightly such that the DDS output frequency changes. When * ; * leaving CALIBRATE mode, the new constants will be stored * ; * back in EEPROM. The new constants are "permanent" and will * ; * be in effect until the next CALIBRATE or until the 16F877 * ; * is reprogrammed, restoring the defaults. * ; * * ; * * ; * Input: VFOCALIBRATE flag in VFOaction * ; * osc_3:osc_0 in EEPROM * ; * * ; * Output: VFOCALIBRATE flag in VFOaction updated * ; * New osc_3:osc_0 stored in EEPROM * ;***************************************************************************** ; handle_msg35 ; (Calibrate Toggle) banksel PORTA ; Switch to Bank 0 btfsc VFOaction,VFOCALIBRATE ; Is Calibrate already active? goto Cal_active_need_turnoff ; Yes, go and turn it off ; bsf VFOaction,VFOCALIBRATE ; Say Calibrate is now active ; ; Send message to Interface PIC (CALIBRATE ON) ; movlw 0x46 ; Get 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 btfsc STATUS,Z ; Good result? goto CalLEDOnDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again CalLEDOnDone ; bcf VFOcontrol,SPLIT ; Make sure SPLIT control flag is CLEAR ; ; Send message to turn off SPLIT LED ; (Split must be off to do calibrate!) ; movlw 0x54 ; Get 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 btfsc STATUS,Z ; Good result? goto CalSplitLEDOffDone; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again CalSplitLEDOffDone ; ; Initialize the calibrate ; Takes 8 ms from here to end of routine. ; (Read EEPROM locations, calc BCD, display 10 MHz and CAL on LCD) ; 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. ; banksel EEADR ; Select 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 pagesel bin2BCD ; Switch to PAGE 1 call bin2BCD ; Calculate BCD version of 10,000.00 call show_freq ; Display the frequency on the LCD pagesel start ; Back to PAGE 0 ; 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 ; ; goto msg35_exit ; Go to exit and return to caller Cal_active_need_turnoff ; ; A message was received from Interface PIC, so terminate the Calibrate. ; ; Write final value to EEPROM banksel EEADR ; Select bank 2 clrf EEADR ; Set the EEPROM write address to zero banksel PORTA ; Select bank 0 movf osc_0,w ; Record the first osc byte banksel EEDATA ; Select bank 2 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 banksel EEDATA ; Select bank 2 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 banksel EEDATA ; Select bank 2 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 banksel EEDATA ; Select bank 2 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) pagesel display_mode ; Switch to PAGE 1 call display_mode ; Display mode on LCD pagesel start ; Back to PAGE 0 ; ; Send message to Interface PIC (CALIBRATE OFF) ; movlw 0x56 ; Get 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 btfsc STATUS,Z ; Good result? goto CalLEDOffDone ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again CalLEDOffDone ; bcf VFOaction,VFOCALIBRATE ; Say Calibrate is NOT active bsf VFOcontrol,NEEDUPDATE ; We need an update of DDS and LCD ; msg35_exit return ; Return to caller ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>> FREQUENCY CHANGE ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ; ;***************************************************************************** ; * Name: transmitter_active_look * ; * * ; * 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 PORTC,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 0xC7 ; Point LCD at digit 8 of 2nd line call cmnd2LCD ; movlw 'X' ; Say "XMT " call data2LCD ; movlw 'M' ; 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 0xC7 ; Point LCD at digit 9 of 2nd line call cmnd2LCD ; movlw 'R' ; Say "RCV " call data2LCD ; movlw 'C' ; call data2LCD ; movlw 'V' ; call data2LCD ; ; transmit_setup_exit return ; Return to caller ; ; ;***************************************************************************** ; * Name: update_band_relays_DDS_LCD * ; * NOTE: This routine is called only by main. * ; * * ; * Purpose: Update the Band relays, DDS and LCD. * ; * This routine is called after a change has been detected, * ; * either via the encoder or a pushbutton. * ; * * ; * 1) Update . * ; * 2) Update band relays (if band changed) * ; * 3) Display updated frequency on the LCD * ; * 4) Send updated frequency to the DDS * ; * 5) Send Current VFO message to INT (if it changed) * ; * * ; * Input: VFOcontrol flag word set up * ; * or set up * ; * * ; * Output: set up * ; * LCD updated * ; * DDS updated * ; * NEEDUPDATE flag cleared * ;***************************************************************************** ; update_band_relays_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 btfss VFOcontrol,TRANSMIT ; Now transmitting? goto select_DDS_VFO_B ; Not transmitting, so set up DDS to use B bsf VFOcontrol,VFOCHANGED ; VFO changed goto select_DDS_VFO_A ; Yes, set up DDS to use A ; ;----- 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 ; Now set flag to add/subtract the offset ; goto select_DDS_VFO_A ; Go set DDS to use VFO A ; ; VFO A receive, SPLIT, so check for transmit ; ARsplit_check_transmit btfss VFOcontrol,TRANSMIT ; Now transmitting? goto select_DDS_VFO_A ; No, set up DDS to use A bsf VFOcontrol,VFOCHANGED ; VFO changed goto select_DDS_VFO_B ; Yes, set up DDS to use B ; ; ---- 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 ; ; btfss VFOcontrol,VFOCHANGED ; Did the VFO change since last update? goto select_VFO_exit ; No, go to exit movlw 0xCB ; Point LCD at digit 12 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 ; movlw 0x42 ; Msg number - Turn LEDVFOA ON and LEDVFOB OFF 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 btfsc STATUS,Z ; Good result? goto select_VFO_exit ; Yes, exit bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again ; 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 ; ; btfss VFOcontrol,VFOCHANGED ; Did the VFO change since last update? goto select_VFO_exit ; No, go to exit movlw 0xCB ; Point LCD at digit 12 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 ; movlw 0x43 ; Msg number - Turn LEDVFOB ON and LEDVFOA OFF 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 btfsc STATUS,Z ; Good result? goto select_VFO_exit ; Yes, exit bsf PORTC,LEDERRD ; Turn on LED9 call wait_a_sec ; Wait one second bcf PORTC,LEDERRD ; and then turn it off again ; Fall through to exit ; select_VFO_exit ; pagesel bin2BCD ; Switch to PAGE 1 call bin2BCD ; Convert the frequency to BCD call show_freq ; Display the frequency on the LCD pagesel start ; Back to PAGE 0 call VFO_freq_load ; Set up workfreq registers ; movf band_index,w ; Get current band index call detect_band_change ; Go see where we are now in freq table movf band_changed_flag,f ; See if the band changed btfss STATUS,Z ; Did band change? call set_one_band_relay ; Go set the band relays (v 1.110) call shift_for_offset ; Yes, go update freq for offset 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 ; ;***************************************************************************** ; * Name: add_step * ; * * ; * 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: * ; * * ; * * ; * Output: increased by * ; * When incrementing, this value may exceed the maximum. * ;***************************************************************************** ; 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 inc byte 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 ; ;***************************************************************************** ; * Name: sub_step * ; * * ; * Purpose: Subtract the increment step from freq, checking that it does * ; * not go below zero. * ; * * ; * Calls: add_step * ; * * ; * Input: * ; * * ; * * ; * Output: decreased by * ; * This workfreq value will not go negative. * ;***************************************************************************** ; 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 low order 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 ; ;***************************************************************************** ; * Name: check_add * ; * * ; * 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 ; ;***************************************************************************** ; * Name: VFO_freq_load * ; * * ; * Purpose: Load with with frequency of the * ; * selected 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 ; ;***************************************************************************** ; * Name: VFO_freq_save * ; * * ; * 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 ; ;***************************************************************************** ; * Name: shift_for_offset * ; * * ; * Purpose: Add or subtract a offset value so we can hear an audio tone * ; * in receiver. Add value if USB, subtract value if LSB mode. * ; * Note that this shift is only done for the receive frequency. * ; * The transmit frequency is NOT shifted. * ; * * ; * Calls: side_add, side_sub * ; * * ; * Input: * ; * (two byte value) * ; * SSBselect * ; * * ; * Output: adjusted * ;***************************************************************************** ; shift_for_offset btfsc VFOcontrol,TRANSMIT ; Are we transmitting? goto offset_exit ; Yes, don't shift btfsc VFOcontrol,SPLIT ; Are we in SPLIT mode? goto offset_exit ; Yes, don't shift btfsc ModeSelect,LSB ; Are we in LSB Mode ? goto offset_exit ; Yes, don't shift btfsc ModeSelect,USB ; Are we in USB Mode ? goto offset_exit ; Yes, don't shift ; movf CWOffset_0,w ; Move offset movwf workfreq_0 ; to movf CWOffset_1,w ; working movwf workfreq_1 ; registers clrf workfreq_2 ; Clear upper two bytes clrf workfreq_3 ; because offset is only 2 bytes ; btfsc ModeSelect,RevCW ; Are we in RevCW Mode ? goto offset_RevCW ; Yes, go to offset sub call offset_add ; Must be CW mode so add the offset value goto offset_exit ; Exit offset_RevCW call offset_sub ; LSB so go subtract the offset value offset_exit return ; ;***************************************************************************** ; * Name: offset_add * ; * * ; * Purpose: Add offset value to * ; * * ; * Input: * ; * * ; * * ; * Output: is adjusted * ;***************************************************************************** ; offset_add movf workfreq_0,w ; Get low byte of the offset working value addwf freq_0,f ; Add it to the low byte of freq btfss STATUS,C ; Any carry? goto side_add1 ; No, add next byte incfsz freq_1,f ; Ripple carry up to the next byte goto side_add1 ; No new carry, add next byte incfsz freq_2,f ; Ripple carry up to the next byte goto side_add1 ; No new carry, add next byte incf freq_3,f ; Ripple carry up to the highest byte side_add1 movf workfreq_1,w ; Get the next increment byte addwf freq_1,f ; Add it to the next higher byte btfss STATUS,C ; Any carry? goto side_add2 ; No, add next byte incfsz freq_2,f ; Ripple carry up to the next byte goto side_add2 ; No new carry, add next byte incf freq_3,f ; Ripple carry up to the highest byte side_add2 movf workfreq_2,w ; Get the next to most significant inc byte addwf freq_2,f ; Add it to the freq byte btfss STATUS,C ; Any carry? goto side_add3 ; No, add last byte incf freq_3,f ; Ripple carry up to the highest byte side_add3 movf workfreq_3,w ; Get the most significant increment byte addwf freq_3,f ; Add it to the most significant freq return ; Return ; ;***************************************************************************** ; * Name: offset_sub * ; * * ; * Purpose: Subtract offset value from * ; * * ; * Calls: offset_add * ; * * ; * Input: * ; * * ; * * ; * Output: is adjusted * ;***************************************************************************** ; offset_sub comf workfreq_0,f ; Subtraction of fstep from comf workfreq_1,f ; freq is done by adding the comf workfreq_2,f ; twos compliment of fstep to comf workfreq_3,f ; freq. incfsz workfreq_0,f ; Increment low order byte goto side_comp_done ; Non-zero, continue incfsz workfreq_1,f ; Increment next byte goto side_comp_done ; Non-zero, continue incfsz workfreq_2,f ; Increment next byte goto side_comp_done ; Non-zero, continue incf workfreq_3,f ; Increment the high byte side_comp_done call offset_add ; Add the compliment to do the subtraction ; ; If the frequency has gone negative, clear it to zero. ; btfss freq_3,7 ; Is high order frequency byte "negative"? goto shift_exit ; No, exit side_set_min clrf freq_0 ; Yes, set the frequency to zero clrf freq_1 ; clrf freq_2 ; clrf freq_3 ; shift_exit return ; ;***************************************************************************** ; * Name: detect_band_change * ; * NOTE: called ONLY by update_band_relays_DDS_LCD * ; * * ; * Purpose: This routine detects the current band that is pointed to by * ; * band table each time one of the band-change pushbuttons is * ; * pressed or the encoder moves. * ; * * ; * Input: band_index contains the base index of the "old" band * ; * contains the current frequency * ; * * ; * Output: band_index updated to contain current band base index * ; * band_changed_flag = 0 if band did not change * ; * band_changed_flag = 1 if band changed * ;***************************************************************************** ; detect_band_change movlw 0x00 ; Start at the ms digit of the lowest freq band movwf new_band_index ; Save as current index movwf new_band_base ; Save as base of current band also goto band_table_check ; Look to see if we are within limits detect_loop movlw 0x04 ; Increment to addwf new_band_base,f ; next band movf new_band_base,w ; Get updated base movwf new_band_index ; and save as new starting index movf new_band_base,w ; Load into W for index for band table call band_table_check call band_table ; Get the most significant byte into W subwf workfreq_3,w ; Compare to current frequency byte btfsc STATUS,Z ; See if byte matches goto band_check_2eq ; Yes, match, so look at next digit btfsc STATUS,C ; Carry is set if result is positive or zero goto detect_next_band ; C Set, so work>band so go to next band goto band_back_up ; C Clear, so work < band, so back up and exit band_check_2eq incf new_band_index,f ; Point to next byte of current band movf new_band_index,w ; Put index W for call call band_table ; Get the next most significant byte into W subwf workfreq_2,w ; Compare to current frequency byte btfsc STATUS,Z ; See if byte matches goto band_check_1eq ; Yes, match, so look at next digit btfsc STATUS,C ; Carry is set if result is positive or zero goto detect_next_band ; C Set, so work>band so go to next band goto band_back_up ; C Clear, so work < band, so back up and exit band_check_1eq incf new_band_index,f ; Point to next byte of current band movf new_band_index,w ; Put index W for call call band_table ; Get the next most significant byte into W subwf workfreq_1,w ; Compare to current frequency byte btfsc STATUS,Z ; See if byte matches goto band_check_0eq ; Yes, match, so look at next digit btfsc STATUS,C ; Carry is set if result is positive or zero goto detect_next_band ; C Set, so work>band so go to next band goto band_back_up ; C Clear, so work < band, so back up and exit band_check_0eq incf new_band_index,f ; Point to next byte of current band movf new_band_index,w ; Put index W for call call band_table ; Get the least significant byte into W subwf workfreq_0,w ; Compare to current frequency byte btfsc STATUS,Z ; See if byte matches goto detect_change ; Yes, match. All digits match. btfsc STATUS,C ; Carry is set if result is positive or zero goto detect_next_band ; C Set, so work>band so go to next band goto band_back_up ; ; band_back_up movlw 0x04 ; Move pointer back by 4 to use previous band subwf new_band_base,f ; Save updated base index goto detect_change ; and leave detect_next_band ; See if we are at highest band already. movlw highest_band_base ; See if we are at subwf new_band_base,w ; the highest band btfss STATUS,Z ; At hightest band? goto detect_loop ; No, loop back and keep looking ; Yes, quit looking detect_change ; ; We found the lowest valid band. Now see if it has changed. ; movf band_index,w ; Move current band index into W movwf old_band_index ; Save it movlw 0x01 ; Set up default for band changed flag movwf band_changed_flag ; Default - band changed movf new_band_base,w ; Get new band base index subwf band_index,w ; Compare to current band index btfsc STATUS,Z ; If band did not change clrf band_changed_flag ; then clear the band changed flag movf new_band_base,w ; Update the band to movwf band_index ; the current band ; ; NOTE: If band_index gets updated, it is done here! ; In this case, old_band_index has just been updated as well. ; (Will use old_band_index to reset the band relay.) ; detect_exit return ; Return ; ;***************************************************************************** ; * Name: update_band_relays UNUSED IN VERSION 1.110 * ; * NOTE: This is called only by update_band_relays_LCD_DDS * ; * * ; * Purpose: Send SET/RESET signals to band relays via header pins. * ; * * ; * Input: old_band_index has base index for old band * ; * band_index has base index for selected band in frequency table * ; * * ; * Output: band relays SET (old band relay RESET) * ;***************************************************************************** ; NOW UNUSED ;update_band_relays ; First RESET the old band relay ; movf old_band_index,w ; Set up W with old band index ; bcf STATUS,C ; Clear carry ; movwf band_number_temp ; Convert band_index to band_number by ; rrf band_number_temp,f; rotating it to the right twice ; rrf band_number_temp,w; to divide by 4. (This time leave in W) ; call reset_one_band_relay ; RESET this relay (using w = band number) ; New SET the new band relay ; movf band_index,w ; Set up W with NEW band index ; bcf STATUS,C ; Clear carry ; movwf band_number_temp ; Convert band_index to band_number by ; rrf band_number_temp,f; rotating it to the right twice ; rrf band_number_temp,w; to divide by 4. (This time leave in W) ; call set_one_band_relay ; SET new relay (using w = band number) ; return ; Exit ; ;***************************************************************************** ; * Name: reset_one_band_relay UNUSED IN VERSION 1.110 * ; * * ; * Purpose: Send RESET signals to one band relay via header pins. * ; * * ; * Input: W = band number of selected band * ; * * ; * Output: selected band relay is RESET * ;***************************************************************************** ; NOW UNUSED ;reset_one_band_relay ; call reset_band_relay_select; Convert band_number to band relay number ; ; NOTE: It is positioned in bits 5-2 ; movwf PORTB ; Set band header pin to set ; call wait_64ms ; Wait a while <<<<< CHANGE TO 8ms later ; clrf PORTB ; and then drop it ; return ; Exit ; ;***************************************************************************** ; * Name: set_one_band_relay (changed in version 1.110) * ; * * ; * Purpose: Send SET signals to one band relay via header pins. * ; * Leave set until this routine is executed again to change. * ; * * ; * Input: W = band number of selected band * ; * * ; * Output: LNA number set in PORTB bits RB7, RB6, RB5 (RB7 is LSB) * ; * LPF number set in PORTB bits RB4, RB3, RB2 (RB4 is LSB) * ; * (PORTB pins RB7-RB2 go to HDR5 which attaches to the LNA/LPF board) * ;***************************************************************************** ; set_one_band_relay movf band_index,w ; Set up W with NEW band index (1.110) bcf STATUS,C ; Clear carry (1.110) movwf band_number_temp ; Convert band_index to band_number by(1.110) rrf band_number_temp,f; rotating it to the right twice (1.110) rrf band_number_temp,w; to divide by 4. (Leave in W) (1.110) call set_band_relay_select; Convert band_number to LNA and LPF bits andlw 0xFC ; Mask off I/Q Relay selection bits (1.112) movwf PORTB ; Set LNA and LPF numbers on header ; Leave it on (Non-latching relay) (1.110) return ; Exit ; ;***************************************************************************** ; * Name: update_workfreq_newband * ; * * ; * Purpose: This routine reads the frequency value of a band table entry * ; * pointed to by new_band and returns it in * ; * * ; * Input: new_band contains the base index (i.e. desired band number * 4) * ; * * ; * Output: The band frequency in . * ;***************************************************************************** ; update_workfreq_newband movf new_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 new_band,f ; Increment index to next byte movf new_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 new_band,f ; Increment index to the next byte movf new_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 new_band,f ; Increment index to the low byte movf new_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 new_band,f ; Restore original value of band return ; Return to the caller ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>>> DDS ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Name: calc_dds_word * ; * * ; * 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. ; ;**************************************************************************** ; * Name: send_dds_freq * ; * * ; * Purpose: This routine sends the AD9854 control word to the DDS chip * ; * using a serial data transfer. * ; * * ; * Input: AD9854_3 ... AD9854_0 * ; * * ; * Output: The DDS chip 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_20_inst ; Wait 10 instructions call wait_10_inst ; Wait 10 instructions bcf PORTD,DDS_ioreset ; call wait_20_inst ; Wait 10 instructions call wait_10_inst ; Wait 10 instructions ; 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_20_inst ; Wait 10 instructions call wait_10_inst ; Wait 10 instructions bcf PORTD,DDS_ioud ; Drop the IO update call wait_20_inst ; Wait 10 instructions call wait_10_inst ; Wait 10 instructions ; return ; Return to the caller ; ; ;***************************************************************************** ; * Name: send_DDS_byte * ; * * ; * 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 call wait_20_inst ; Wait for 10 instructions call wait_10_inst ; Wait for 10 instructions bsf PORTC,DDS_clk ; Toggle write clock call wait_20_inst ; Wait for 10 instructions call wait_20_inst ; Wait for 10 instructions call wait_20_inst ; Wait for 10 instructions bcf PORTC,DDS_clk ; decfsz bit_count,f ; Has the whole byte been sent? goto next_DDS_bit ; No, keep going return ; ;***************************************************************************** ; * Name: read_DDS_byte * ; * * ; * Purpose: Read a CR byte from the AD9854 * ; * NOTE: Turned off because mechanism is UNRELIABLE per AD * ; * * ; * 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 PORTC,DDS_clk ; Toggle write clock ; nop ; Let it settle ; 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 ; Let it settle ; nop ; ; bcf PORTC,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 ; ;***************************************************************************** ; * * ; * 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 ; ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>>>>> LCD ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ; ***************************************************************************** ; * Name: busy_check * ; * * ; * 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 ; ; Now ready to clear the port and set up for busy check. ; clrf PORTA ; Clear all outputs on PORTA banksel TRISA ; Select bank 1 movlw b'00001111' ; Set as outputs, inputs movwf TRISA ; via Tristate banksel PORTA ; Select bank 0 bcf PORTE,LCD_rs ; Set up LCD for Read Busy Flag (RS = 0) bsf PORTA,LCD_rw ; Set up LCD for Read (RW = 1) movlw 0xFF ; Set up constant 255 movwf timeloop1 ; for timer loop counter LCD_is_busy bsf PORTA,LCD_e ; Set E high call wait_5_inst ; Wait movf PORTA,w ; Read PORTA into W movwf LCD_read ; Save W for later testing bcf PORTA,LCD_e ; Drop E again call wait_5_inst ; Wait at least .450 us ; (Each NOP instruction takes about .2 us ; when using 20 MHz clock) bsf PORTA,LCD_e ; Pulse E high (dummy read of lower nibble), call wait_5_inst ; Wait at least .450 us ; (Each NOP instruction takes about .2 us ; when using 20 MHz clock) bcf PORTA,LCD_e ; and drop E again decf timeloop1,f ; Decrement loop counter btfsc STATUS,Z ; Is loop counter down to zero? goto bailout ; If yes, bailout (end regardless) call wait_20_inst ; Wait a while btfsc LCD_read,LCD_BUSY ; Is Busy Flag (DB7) in save byte clear? goto LCD_is_busy ; If not, it is busy so jump back goto not_busy ; Not busy bailout ; bsf PORTE,DEBUG0 ; Set DEBUG bit - Interrupt bailout occurred not_busy clrf PORTA ; Clear all of Port A (inputs and outputs) banksel TRISA ; Select bank 1 movlw 0x00 ; Set up to enable PORTA data pins movwf TRISA ; All pins (RA5..RA0) are back to outputs banksel PORTA ; Select bank 0 ; return ; ; ; ***************************************************************************** ; * Name: cmnd2LCD * ; * data2LCD * ; * * ; * 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 call wait_20_inst ; Wait for LCD call wait_20_inst ; to be stable clrf rs_value ; Remember to clear RS (clear rs_value) bcf PORTE,LCD_rs ; Clear RS for Command to LCD goto write2LCD ; Go to common code data2LCD ; ****** Entry point ******** movwf LCD_char ; Save byte to write to LCD call wait_20_inst ; Wait for LCD call wait_20_inst ; to be stable bsf rs_value,0 ; Remember to set RS (set bit 0 of rs_value) bsf PORTE,LCD_rs ; Set RS for Data to LCD write2LCD call busy_check ; Check to see if LCD is ready for new data bcf PORTA,LCD_rw ; Set LCD back to Write mode (RW = 0) bcf PORTE,LCD_rs ; Guess RS should be clear btfsc rs_value,0 ; Should RS be clear? (is bit 0 == 0?) bsf PORTE,LCD_rs ; No, set RS ; ; Transfer Most Significant nibble (XXXX portion of XXXXYYYY) ; Now is sent via ; movlw 0xF0 ; Set up mask andwf PORTA,f ; Clear LS Nibble of PORTA swapf LCD_char,w ; Move MS nibble of data to LS position in W andlw 0x0F ; Mask to give 0000XXXX in W iorwf PORTA,f ; Send to without changing bsf PORTA,LCD_e ; Pulse the E line high, call wait_5_inst ; Wait at least .450 us ; (Each NOP instruction takes about .2 us ; when using 20 MHz clock) bcf PORTA,LCD_e ; Now drop it again ; ; Transfer Least Significant nibble (YYYY portion of XXXXYYYY) ; movlw 0xF0 ; Set up mask andwf PORTA,f ; Clear LS Nible of PORTA movf LCD_char,w ; Put byte of data into W andlw 0x0F ; Mask to give 0000YYYY in W iorwf PORTA,f ; Send to without changing bsf PORTA,LCD_e ; Pulse the E line high, call wait_5_inst ; Wait at least .450 us ; (Each NOP instruction takes about .2 us ; when using 20 MHz clock) bcf PORTA,LCD_e ; and drop it again return ; ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>>>> EEPROM ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ; ***************************************************************************** ; * Name: write_EEPROM * ; * * ; * 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 banksel EECON1 ; Select 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 banksel EEADR ; Select bank 2 incf EEADR,f ; Increment the EE write address banksel PORTA ; Select bank 0 return ; Return to the caller ; ;***************************************************************************** ; * Name: read_EEPROM * ; * * ; * 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 banksel EECON1 ; Select 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 banksel EEADR ; Select bank 2 incf EEADR,f ; Increment the read address movf EEDATA,w ; W = EEDATA banksel PORTA ; Select bank 0 return ; Return to the caller ; ;***************************************************************************** ;***************************************************************************** ;***************************************************************************** ; >>>>>>>>>>>>>>>>>>>> INTERRUPT HANDLER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Name: interrupt_handler_main * ; * * ; * 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) Perform some action (not define at this time) * ; * 3) Restore saved environment * ; * 4) Return to interrupted code * ;***************************************************************************** ; interrupt_handler_main ; ; NO INTERRUPTS NOW! ; bcf INTCON,INTF ; Clear RB0 interrupt ; #endif ; End DriverPIC only ; ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ####################### ###################### ; ####################### PAGE 0 COMMON FOR BOTH PICs ###################### ; ####################### ###################### ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ; **************************************************************************** ; * Name: input_message_detect * ; * * ; * Purpose: Make a quick check to see if a message is ready to be received.* ; * NOTE: This can be a common routine as long as both PICs use * ; * the same ports/pins. * ; * * ; * Input: PORT for CLOCKIN * ; * * ; * Output: W=0 (Z=1) - NO message * ; * W=1 (Z=0) - A message IS waiting to be received * ; **************************************************************************** input_message_detect clrw ; Default - no message waiting btfsc PORTD,CLOCKIN ; Is input clock signal LOW? movlw 0x01 ; No - clock is HIGH so message exists ; NOTE: don't ACK the CLOCKIN here! iorlw 0x00 ; Set up Z Flag (Don't change W) return ; ; **************************************************************************** ; * Name: wait_20_inst : Wait for 20 instructions * ; * wait_10_inst : Wait for 10 instructions * ; * wait_5_inst : Wait for 5 instructions * ; * wait_a_sec : Wait for 1 second * ; * wait_256ms : Wait for 256 msec * ; * wait_128ms : Wait for 128 msec * ; * wait_64ms : Wait for 64 msec * ; * wait_32ms : Wait for 32 msec * ; * wait_16ms : Wait for 16 msec * ; * wait_8ms : Wait for 8 msec * ; * * ; * Purpose: Wait for a specified number of instructions or milliseconds. * ; * * ; * Input: None * ; * * ; * Output: None * ; **************************************************************************** ; wait_20_inst ; ***** Entry point ****** (About 4 us with 20 MHz clock) nop nop nop nop nop nop nop nop nop nop ; fall through wait_10_inst ; ***** Entry point ****** (About 2 us with 20 MHz clock) nop nop nop nop nop wait_5_inst ; ***** Entry point ****** (About 1 us with 20 MHz clock) 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_setup ; Go to wait loops wait_32ms ; ****** Entry point ****** movlw 0x80 ; Set up outer loop to 64 goto outer_loop_setup ; Go to wait loops wait_16ms ; ****** Entry point ****** movlw 0x40 ; Set up outer loop to 32 goto outer_loop_setup ; 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 timeloop1 value ; For example, 8ms / .255ms = 31.3; use 0x20 for timeloop1 in wait_8ms ; outer_loop_setup movwf timeloop1 ; Store outer loop count outer_loop movlw 0xFF ; Set up inner loop counter movwf timeloop2 ; to 255 inner_loop nop ; 1 instruction cycle nop ; 1 instruction cycle decfsz timeloop2,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 timeloop1,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 ; ; NOTE: MAKE SURE THIS ADDRESS IS STILL BELOW 0x800 !! ; ; ****************************************************************************** ; ****************************************************************************** ; ****************************************************************************** ; ****************************************************************************** ; ---------------------------------------------- ORG 0x0800 ; NOTE!!! NOW SWITCHING TO PAGE 1 NOTE!!!! ; ---------------------------------------------- ; ****************************************************************************** ; ****************************************************************************** ; ****************************************************************************** ; ****************************************************************************** ; ****************************************************************************** #ifdef DriverPIC ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ####################### ###################### ; ####################### PAGE 1 DRIVER PIC ONLY ###################### ; ####################### ###################### ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ;***************************************************************************** ; >>>>>>>>>>>>>> DRIVER PIC INITIALIZATION ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<< ;***************************************************************************** ;***************************************************************************** ; ;***************************************************************************** ; * Name: init_Driver_PIC >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: Power on initialization of 16F877. * ; * * ; * Input: None * ; * * ; * Output: None * ;***************************************************************************** ; init_Driver_PIC clrf INTCON ; Disable interrupts until we are ready banksel TRISA ; Select bank 1 ; bsf OPTION_REG,NOT_RBPU ; Disable weak pullups (LOW ACTIVE) movlw 00000000b ; (5,4,3,2,1,0) = outputs movwf TRISA ; to Port A (bits 7,6 don't exist) movlw 00000000b ; (7,6,5,4,3,2,1,0) = outputs movwf TRISB ; to Port B movlw 01110000b ; (7,3,2,1,0) = outputs, (6,5,4) = input movwf TRISC ; to Port C movlw 00001000b ; (7,6,5,4,2,1,0) = outputs, (3) = input movwf TRISD ; to Port D movlw 00000000b ; (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 banksel PORTA ; Select bank 0 ; bcf VFOcontrol,NEEDUPDATE ; Clear needupdate flag bcf VFOcontrol,TRANSMIT ; Clear transmitting flag bcf VFOcontrol,SPLIT ; Start with split off ; Send message 54 to turn SPLIT OFF movlw 0x54 ; message number movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto InitPICSplitOK ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to page 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_Driver_PIC ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again InitPICSplitOK bcf VFOcontrol,FAST_TUNE ; Turn fast tune off ; Send message 52 to turn FASTTUNE LED OFF movlw 0x55 ; message number movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto InitPICFTUNEOK ; Yes, done bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to page 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_Driver_PIC ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again InitPICFTUNEOK clrf VFOaction ; Clear all action flags clrf PORTC clrf PORTD clrf comm_flags ; Initialize return ; return ; ;***************************************************************************** ; * Name: init_LCD >>>>> IN PAGE 1 <<<<<<< * ; * * ; * 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 pagesel wait_64ms ; Wait for LCD to power up call wait_64ms ; Wait for LCD to power up (PAGE 0) pagesel init_LCD ; Back to PAGE 1 movlw 0x03 ; LCD init instruction (First) (DB7 is RA3) movwf PORTA ; Send to LCD via bsf PORTA,LCD_e ; Set the LCD E line high, pagesel wait_64ms ; Wait for LCD to power up call wait_64ms ; Wait for LCD to power up (PAGE 0) pagesel init_LCD ; Back to PAGE 1 bcf PORTA,LCD_e ; and then Clear E movlw 0x03 ; LCD init instruction (Second) movwf PORTA ; Send to LCD via RA3..RA0 bsf PORTA,LCD_e ; Set E high, pagesel wait_32ms ; Wait a while call wait_32ms ; Wait (PAGE 0) pagesel init_LCD ; Back to PAGE 1 bcf PORTA,LCD_e ; and then Clear E movlw 0x03 ; LCD init instruction (Third) movwf PORTA ; Send to LCD via bsf PORTA,LCD_e ; Set E high, pagesel wait_32ms ; Wait a while call wait_32ms ; Wait (PAGE 0) pagesel init_LCD ; Back to PAGE 1 bcf PORTA,LCD_e ; and then Clear E movlw 0x02 ; 4-bit mode instruction movwf PORTA ; Send to LCD via bsf PORTA,LCD_e ; Set E high, pagesel wait_16ms ; Wait a while call wait_16ms ; Wait (PAGE 0) pagesel init_LCD ; Back to PAGE 1 bcf PORTA,LCD_e ; and then Clear E movlw 0x28 ; 1/16 duty cycle, 5x8 matrix pagesel cmnd2LCD ; Switch to PAGE 0 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 pagesel init_LCD ; Back to PAGE 1 ; clrf BacklightControl ; Clear Backlight ; return ; ; ;***************************************************************************** ; * Name: display_mcode_version >>>>> IN PAGE 1 <<<<<<< * ; * * ; * 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 pagesel cmnd2LCD ; Switch to PAGE 0 movlw 0x80 ; Point LCD at char 0 of first line call cmnd2LCD ; movlw ' ' ; Clear char 0 of first line call data2LCD ; movlw 'I' ; Char 1 of first line call data2LCD ; movlw 'Q' ; Char 2 of first line call data2LCD ; movlw 'P' ; Char 3 of first line call data2LCD ; movlw 'r' ; 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' ; 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 ; 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 ; 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 ; movlw ' ' ; Clear char 6 of second line call data2LCD ; ; movlw 0xCF ; Point LCD at char 16 of second line call cmnd2LCD ; movlw ' ' ; Clear it second line call data2LCD ; ; pagesel display_mcode_version ; Back to PAGE 1 ; return ;***************************************************************************** ; * Name: init_DDS >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: Initialize the AD9854 by sending the CR register bit settings. * ; * * ; * Input: None * ; * * ; * Output: None * ;***************************************************************************** ; init_DDS pagesel wait_8ms ; Switch to PAGE 0 call wait_8ms ; (PAGE 0) ; ; Pulse the Master Reset to initialize the DDS chip. ; bsf PORTD,DDS_mreset ; Raise the master reset call wait_8ms ; Wait a long time (PAGE 0) bcf PORTD,DDS_mreset ; Drop the master reset call wait_8ms ; Wait a long time (PAGE 0) ; ; Now pulse the IO reset ; bsf PORTD,DDS_ioreset ; Raise the IO reset call wait_8ms ; Wait a long time (PAGE 0) bcf PORTD,DDS_ioreset ; Drop the IO reset call wait_8ms ; Wait a long time (PAGE 0) ; ; 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 (PAGE 0) ; ; 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 (PAGE 0) movlw CR_byte_1 ; Get the second DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) movlw CR_byte_2 ; Get the third DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) movlw CR_byte_3 ; Get the 4th DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) ; ; 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 (PAGE 0) bcf PORTD,DDS_ioreset ; Drop the IO reset call wait_8ms ; Wait a long time (PAGE 0) ; ; 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 (PAGE 0) ; ; 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 (PAGE 0) movlw CR_byte_1 ; Get the second DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) movlw CR_byte_2 ; Get the third DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) movlw CR_byte_3 ; Get the 4th DDS CR byte movwf byte2send ; Pass it call send_DDS_byte ; Send it (PAGE 0) ; ; Now we need to send external update to make the entire Init "take" ; bsf PORTD,DDS_ioud ; Raise the IO update call wait_20_inst ; Wait 10 instructions (PAGE 0) call wait_10_inst ; Wait 10 instructions (PAGE 0) bcf PORTD,DDS_ioud ; Drop the IO update call wait_20_inst ; Wait 10 instructions (PAGE 0) call wait_10_inst ; Wait 10 instructions (PAGE 0) pagesel init_DDS ; Back to PAGE 1 ; btfss PORTC,XMIT_Keyed_Low_Active ; Is it low (keyed = special debug) goto init_DDS ; Loop back to top if init_DDS return ; Return to the caller ; ;***************************************************************************** ; * Name: init_freq >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: Initialize default frequency and constants. * ; * Get the reference oscillator constant from the EEPROM. * ; * * ; * Input: None * ; * * ; * Output: None * ;***************************************************************************** ; init_freq bcf VFOcontrol,FAST_TUNE ; Start by fast tuning off (Normal tuning) ; banksel EEADR ; Select bank 2 clrf EEADR ; Set the EEPROM read address to zero ; pagesel read_EEPROM ; Switch to PAGE 0 call read_EEPROM ; Read EEPROM and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_0 ; Save osc frequency ; call read_EEPROM ; Get next byte and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_1 ; Save it ; call read_EEPROM ; Get the third byte and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf osc_2 ; Save it ; call read_EEPROM ; Get the fourth byte and increment address (PAGE 0) ; (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 RCV bcf VFOcontrol,VFOB ; and VFOB off movlw 0x42 ; Msg number - Turn LEDVFOA ON and LEDVFOB OFF movwf comm_out ; Put in output message box pagesel send_byte ; Back to PAGE 1 call send_byte ; Send the message byte (PAGE 1) ; Stay in PAGE 1 btfsc STATUS,Z ; Good result? goto init_VFOA_OK ; Yes, exit bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to PAGE 0 call wait_a_sec ; Wait one second (PAGE 0) bcf PORTC,LEDERRD ; and then turn it off again init_VFOA_OK ; ; Write to LCD ; pagesel cmnd2LCD ; Switch to PAGE 0 movlw 0xC7 ; Point LCD at digit 8 of 2nd line call cmnd2LCD ; (PAGE 0) movlw 'R' ; Say "RCV" call data2LCD ; (PAGE 0) movlw 'C' ; call data2LCD ; (PAGE 0) movlw 'V' ; call data2LCD ; (PAGE 0) movlw ' ' ; call data2LCD ; (PAGE 0) movlw 'V' ; Say "VFOA" call data2LCD ; (PAGE 0) movlw 'F' ; call data2LCD ; (PAGE 0) movlw 'O' ; call data2LCD ; (PAGE 0) movlw '-' ; call data2LCD ; (PAGE 0) movlw 'A' ; call data2LCD ; (PAGE 0) pagesel init_freq ; Back to PAGE 1 ; return ; Return ;***************************************************************************** ; * Name: init_mode >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: Initialize the sideband selection to latest mode * ; * * ; * Input: None * ; * * ; * Output: ModeSelect set up as last saved in EEPROM * ; * LCD updated with Mode * ;***************************************************************************** ; init_mode banksel EEADR ; Select bank 2 movlw 0x06 ; Location of Mode value movwf EEADR ; Set the EEPROM read address to zero ; pagesel read_EEPROM ; Switch to PAGE 0 call read_EEPROM ; Read EEPROM and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) pagesel init_mode ; Back to PAGE 1 movwf ModeSelect ; Save last mode ; btfsc ModeSelect,LSB ; Is it LSB? goto init_setLSB ; Yes, go set LSB btfsc ModeSelect,USB ; Is it USB? goto init_setUSB ; Yes, go set LSB btfsc ModeSelect,CW ; Is it CW? goto init_setCW ; Yes, go set LSB goto init_setRevCW ; No, it must be RevCW ; init_setLSB clrf ModeSelect ; Change ModeSelect bsf ModeSelect,LSB ; to LSB ; Send message 40 to turn LED1 ON (USB/RevCW) and LED2 OFF movlw 0x40 ; Get message number for LSB LED movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto init_WriteL ; Yes, write L on LCD bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to PAGE 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_mode ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again goto init_mode_exit ; and exit init_WriteL ; Now display Mode on LCD call display_mode ; (PAGE 1) goto init_SetIQRelay ; ; init_setUSB clrf ModeSelect ; Change ModeSelect bsf ModeSelect,USB ; to USB ; Send message 41 to turn LED2 ON (USB/RevCW) and LED1 OFF movlw 0x41 ; Get message number for USB/RevCW LED movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto init_WriteU ; Yes, go update LCD also bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to PAGE 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_mode ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again goto init_mode_exit ; and exit init_WriteU ; Now display Mode on LCD call display_mode ; (PAGE 1) goto init_ResetIQRelay ; ; init_setCW clrf ModeSelect ; Change ModeSelect bsf ModeSelect,CW ; to CW ; Send message 40 to turn LED1 ON (LSB/CW) and LED2 OFF movlw 0x40 ; Get message number for LSB LED movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto init_WriteC ; Yes, write C on LCD bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to PAGE 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_mode ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again goto init_mode_exit ; and exit init_WriteC ; Now display Mode on LCD call display_mode ; (PAGE 1) goto init_SetIQRelay ; ; init_setRevCW clrf ModeSelect ; Change ModeSelect bsf ModeSelect,RevCW ; to Reverse CW ; Send message 41 to turn LED2 ON (USB/RevCW) and LED1 OFF movlw 0x41 ; Get message number for USB/RevCW LED movwf comm_out ; Put in output message box call send_byte ; Send the message byte (PAGE 1) btfsc STATUS,Z ; Good result? goto init_WriteR ; Yes, go update LCD also bsf PORTC,LEDERRD ; Turn on LED9 pagesel wait_a_sec ; Switch to PAGE 0 call wait_a_sec ; Wait one second (PAGE 0) pagesel init_mode ; Back to PAGE 1 bcf PORTC,LEDERRD ; and then turn it off again goto init_mode_exit ; and exit init_WriteR ; Now display Mode on LCD call display_mode ; (PAGE 1) goto init_ResetIQRelay ; ; init_SetIQRelay ; bsf PORTB,IQRelaySet ; No, raise SET bit, nop ; let it settle (Read-modify-write problem), bcf PORTB,IQRelayReset ; and then drop RESET bit goto init_IQSelectReady ; Go and set/reset the relay init_ResetIQRelay ; bsf PORTB,IQRelayReset ; No, raise RESET bit, nop ; let it settle (Read-modify-write problem), bcf PORTB,IQRelaySet ; and then drop SET bit init_IQSelectReady movwf PORTB ; Raise the Relay Set or Reset bit pagesel wait_8ms ; Switch to PAGE 0 call wait_8ms ; Wait for Relay to set/reset (PAGE 0) pagesel init_mode ; Back to PAGE 1 clrf PORTB ; Clear all port bits ; ; Now retrieve CW offset from EEPROM and set up ; banksel EEADR ; Select bank 2 movlw 0x04 ; Location of CWOffset values movwf EEADR ; Set the EEPROM read address to zero ; pagesel read_EEPROM ; Switch to PAGE 0 call read_EEPROM ; Read EEPROM and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf CWOffset_0 ; Save LS byte of offset ; call read_EEPROM ; Get next byte and increment address (PAGE 0) ; (Sets up Bank3, EEDATA into W, exits in Bank0) movwf CWOffset_1 ; Save MS byte of offset init_mode_exit ; pagesel init_mode ; Back to PAGE 1 return ; Return ; ;***************************************************************************** ; * Name: init_band_relays >>>>> IN PAGE 1 <<<<<<< UNUSED v1.110* ; * * ; * Purpose: Initialize all band relays * ; * Note: DO NOT set band relay here. Set it only in * ; * set_band_relays when called from set_band_relays_DDS_LCD * ; * * ; * Input: None * ; * * ; * Output: All relays are reset. * ; * old_band_index set to zero * ;***************************************************************************** ; ;init_band_relays ; First reset all relays, since we don't know their state at last power-up. ; movlw HIGHESTBAND ; Start with band 10 ; movwf band_number_temp ; Save in temp location ;next_relay ; movf band_number_temp,w ; Set up W with band number ; decfsz band_number_temp,f ; Is the count down to zero yet? ; goto next_relay ; No, do another ; ; clrf band_index ; Initialize this to force update ; ; Will set the relay for default band in update_band_relays_DDS_LCD ; pagesel start ; Back to PAGE 0 ; return ; Return ; ;***************************************************************************** ; * Name: allow_interrupts >>>>> IN PAGE 1 <<<<<<< * ; * * ; * 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 ; ;***************************************************************************** ; * Name: calibrate_action >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: This routine is entered if CALIBRATE mode is activated by * ; * a push button press (message 35) and an encoder message is * ; * received. * ; * Upon entering CALIBRATE mode, "10,000.00 CAL" is displayed on * ; * the LCD, and the DDS chip is programmed to produce an output * ; * at 10 MHz, based on the osc value stored in the EEPROM. As * ; * long as CALIBRATE mode is active, the osc value is slowly * ; * altered (by turning the encoder) to allow the output frequency * ; * 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. The new osc value is * ; * in effect until CALIBRATE is done again or until the 16F877 * ; * is reprogrammed (restoring the defaults). * ; * * ; * Input: The original osc constant in * ; * comm_in has input message (for DIRECTION only) * ; * * ; * Output: The corrected osc constant in * ; * (will be saved back to EEPROM upon return from this routine) * ;***************************************************************************** ; calibrate_action clrf fstep_3 ; Clear the three most significant clrf fstep_2 ; bytes of fstep clrf fstep_1 ; ; ; NOTE: We don't use the encoder tick-count here. Instead, we just change by ; 2 Hz every time we enter this routine (which could be every 25 ms). ; This insures that movments are always small and controlled. ; movlw 0x2 ; Assume that we are adjusting slowly movwf fstep_0 ; Always use small increment btfsc comm_in,ENCDIR ; 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 ; ; DO NOT call shift_for_offset here. Want to calibrate with actual 10 MHz ; signal, not the offset signal. Will be listening "off the side" of WWV ; anyway, so the tones should be zero beat when calibration is complete. ; pagesel start ; Switch to PAGE 0 call calc_dds_word ; Calculate DDS value based on current osc call send_dds_freq ; Update the DDS pagesel calibrate_action ; Back to PAGE 1 ; return ; Return to the caller ; ;***************************************************************************** ; * Name: tone_set_action >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: This routine is entered if TONESET mode is activated by * ; * a push button press (message 24) and an encoder message is * ; * received. * ; * Upon entering TONESET mode, "10,000.00 CAL" is displayed on * ; * to be trimmed to exactly 10 MHz. Once the Tone Set push * ; * button is pressed again, the new tone value is stored in * ; * the EEPROM and normal operation begins. The new tone value is * ; * in effect until TONESET is done again or until the 16F877 * ; * is reprogrammed (restoring the defaults). * ; * * ; * Input: The original CWOffset constant in * ; * * ; * Output: The corrected CWOffset constant in EEPROM * ;***************************************************************************** tone_set_action btfsc ModeSelect,LSB ; Are we in LSB Mode ? goto tone_set_exit ; Yes, don't adjust btfsc ModeSelect,USB ; Are we in USB Mode ? goto tone_set_exit ; Yes, don't adjust ; clrf fstep_3 ; Clear the three most significant clrf fstep_2 ; bytes of fstep clrf fstep_1 ; ; ; NOTE: We don't use the encoder tick-count here. Instead, we just change by ; 5 Hz every time we enter this routine (which could be every 25 ms). ; This insures that movments are always small and controlled. ; movlw 0x5 ; Assume that we are adjusting by 5 Hz per tick movwf fstep_0 ; Always use small increment btfsc comm_in,ENCDIR ; Is direction bit clear? (indicating DN) goto tone_set_increase ; No, go and increase the offset 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 tone_set_increase ; Non-zero, continue incfsz fstep_1,f ; Increment next byte goto tone_set_increase ; Non-zero, continue incfsz fstep_2,f ; Increment next byte goto tone_set_increase ; Non-zero, continue incf fstep_3,f ; Increment the high byte tone_set_increase movf fstep_0,w ; Get the low byte increment addwf CWOffset_0,f ; Add it to the low offset byte btfss STATUS,C ; Was there a carry? goto tone_add4 ; No, add the next bytes incf CWOffset_1,f ; Ripple carry up to the next byte tone_add4 movf fstep_1,w ; Get the second byte increment addwf CWOffset_1,f ; Add it to the second offset byte ; See if it's gone negative. If so, wrap it back to zero. rlf CWOffset_1,w ; Rotate to set up Carry btfss STATUS,C ; Is Carry set? goto tone_incdec_ok ; No, it's OK clrf CWOffset_0 ; Yes, wrap back clrf CWOffset_1 ; to zero tone_incdec_ok ; ; Set up with offset value, for bin2BCD and show_tune ; tone_continue movf CWOffset_0,w ; Get LS CWOffset byte movwf freq_0 ; and save it here for bin2BCD movf CWOffset_1,w ; Get MS CWOffset byte movwf freq_1 ; and save it here for bin2BCD clrf freq_2 ; Clear unused clrf freq_3 ; Clear unused ; call bin2BCD ; Calculate BCD version of side tone value ; Uses freq regs, returns with BCD regs set up call show_tone ; Display the tone value on the LCD ; ; Now set up with current receive VFO frequency ; pagesel VFO_freq_load ; Switch to PAGE 1 call VFO_freq_load ; Set up with current VFO pagesel tone_set_action ; Back to page 1 movf workfreq_3,w ; Get workfreq register movwf freq_3 ; and save it in freq movf workfreq_2,w ; Get workfreq register movwf freq_2 ; and save it in freq movf workfreq_1,w ; Get workfreq register movwf freq_1 ; and save it in freq movf workfreq_0,w ; Get workfreq register movwf freq_0 ; and save it in freq ; pagesel start ; Switch to PAGE 0 call shift_for_offset ; Always update freq for offset call calc_dds_word ; Calculate DDS value call send_dds_freq ; Update the DDS pagesel tone_set_action ; Back to PAGE 1 tone_set_exit return ; Return to the caller ; ; **************************************************************************** ; * Name: bcd2packedbcd >>>>> IN PAGE 1 <<<<<<< * ; * * ; * ALL IN BANK 1 * ; * * ; * Input: BCD in buffer - keypad_buffer_0 (first character - MS) * ; * keypad_buffer_1 * ; * keypad_buffer_2 * ; * keypad_buffer_3 * ; * keypad_buffer_4 (least significant) * ; * buffer_count - number of valid characters in buffer * ; * * ; * Output: packed BCD in * ; * where keypad_BCD_2 = 0:ten-thousand * ; * keypad_BCD_1 = thousand:hundred * ; * keypad_BCD_0 = ten:one * ; * * ; **************************************************************************** ; bcd2packedbcd swapf keypad_buffer_3,w ; Get "tens" digit (bank1) iorwf keypad_buffer_4,w ; Add to "ones" digit (bank1) movwf keypad_BCD_0 ; Save in BCD LS register (bank1) swapf keypad_buffer_1,w ; Get "thousands" digit (bank1) iorwf keypad_buffer_2,w ; Add to "hundreds" digit (bank1) movwf keypad_BCD_1 ; Save in BCD register (bank1) movf keypad_buffer_0,w ; Get "ten-thousands" digit (bank1) movwf keypad_BCD_2 ; Save in BCD MS register (bank1) return ; ; **************************************************************************** ; * Name: bcd2bin16 >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Enter in Bank 1 * ; * ALL VARIABLES IN BANK 1 * ; * Exit in Bank 1 * ; * * ; * Input: packed BCD in bcd2:bcd1:bcd0 (modified!) * ; * bcd2 = 0:tenk * ; * bcd1 = thou:hund * ; * bcd0 = tens:ones * ; * Output: 16 bit binary in keypad_binary_high:keypad_binary_low * ; * Temporary: counter * ; * Size: 23 instructions * ; * Timing: 2+2+16*(6+12+3)-1+2=341 instruction cycles * ; * * ; * Notes: The routine uses BCD division by 2. Each iteration * ; * the LSB of BCD value (which coincides with correspondent * ; * binary bit) is shifted to output and BCD value is * ; * divided by 2. To do the division, BCD is shifted right * ; * once and corrected. Correction: if MSB of a nibble is set, * ; * subtract 3 from it. * ; * * ; * January 22, 2001 by Nikolai Golovchenko * ; * http://www.piclist.com/techref/microchip/math/radix/bcd2bin16.htm * ; **************************************************************************** bcd2bin16 movlw 0x10 ; movwf BCD_convert_counter ; bcd2bin16loop clrc ; rrf keypad_BCD_2,f ; (bank1) rrf keypad_BCD_1,f ; (bank1) rrf keypad_BCD_0,f ; (bank1) rrf keypad_binary_high,f ; (bank1) rrf keypad_binary_low,f ; (bank1) clrw ; btfsc keypad_BCD_0,3 ; (bank1) iorlw 0x03 ; btfsc keypad_BCD_0,7 ; (bank1) iorlw 0x30 ; subwf keypad_BCD_0,f ; (bank1) clrw ; btfsc keypad_BCD_1,3 ; (bank1) iorlw 0x03 ; btfsc keypad_BCD_1,7 ; (bank1) iorlw 0x30 ; subwf keypad_BCD_1,f ; (bank1) decfsz BCD_convert_counter,f ; goto bcd2bin16loop ; return ; ; **************************************************************************** ; * Name: multiply_by_1k >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Enter in Bank 1 * ; * ALL VARIABLES IN BANK 1 * ; * Exit in Bank 1 * ; * * ; * Input: Binary number in keypad_binary_high (MS) * ; * keypad_binary_low (LS) * ; * Other number is constant 1000 (0x3E8) * ; * * ; * Output: binary in keypad_binary_0 (LS) * ; * keypad_binary_1 * ; * keypad_binary_2 * ; * keypad_binary_3 (MS) * ; * * ; * Multiply 16x16 bit from malin@onspec.co.uk * ; * Unsigned multiply of a2:a1 with b2:b1 leaving result * ; * in res4:res3:res2:res1 * ; * These 8 variables need to be defined * ; * * ; * Program length 32 line * ; * time 129 to 228 cycles * ; * This program looks at the lsb of a1 to decide whether to add b1 to res2 * ; * and b2 to res3, with appropriate carrys * ; * It then looks at the lsb of a2 to decide whether to add b1 to res3 and * ; * b2 to res4, again with appropriate carrys. * ; * The rotates then only have to be done 8 times * ; * * ; * This is uses slightly more program but takes a little less time than * ; * a routine that performs one 16 bit addition per rotate and 16 rotates * ; * * ; * Multiple byte addition routine from Microchip AN617 * ; * Result registers used as loop counter from Bob Fehrenbach & Scott Dattalo* ; ***************************************************************************** ; multiply_by_1k movlw 0x03 ; Set up constant for 1000 (0x03E8) movwf mult_temp_1 ; movlw 0xE8 ; movwf mult_temp_0 ; (bank1) clrf keypad_binary_3 ; res4 (bank1) clrf keypad_binary_2 ; res3 (bank1) clrf keypad_binary_1 ; res2 (bank1) movlw 0x80 ; movwf keypad_binary_0 ; res1 (bank1) mult_1k_nextbit rrf keypad_binary_high,f ; a2 (bank1) rrf keypad_binary_low,f ; a1 (bank1) btfss STATUS,C ; goto mult_1k_nobit_l ; movf mult_temp_0,w ; b1 (bank1) addwf keypad_binary_1,f ; res2 (bank1) movf mult_temp_1,w ; b2 (bank1) btfsc STATUS,C ; incfsz mult_temp_1,w ; b2 (bank1) addwf keypad_binary_2, f ; res3 (bank1) btfsc STATUS,C ; incf keypad_binary_3, f ; res4 (bank1) bcf STATUS,C ; mult_1k_nobit_l btfss keypad_binary_low, 7 ; a1 (bank1) goto mult_1k_nobit_h ; movf mult_temp_0,w ; b1 (bank1) addwf keypad_binary_2,f ; res3 movf mult_temp_1, w ; b2 (bank1) btfsc STATUS,C ; incfsz mult_temp_1,w ; b2 (bank1) addwf keypad_binary_3,f ; res4 mult_1k_nobit_h rrf keypad_binary_3,f ; res4 (bank1) rrf keypad_binary_2,f ; res3 (bank1) rrf keypad_binary_1,f ; res2 (bank1) rrf keypad_binary_0,f ; res1 (bank1) btfss STATUS,C ; goto mult_1k_nextbit ; return ; ;***************************************************************************** ; * Name: show_tone >>>>> IN PAGE 1 <<<<<<< * ; * * ; * Purpose: Display the Side Tone setting on the LCD. * ; * Value range is 600 (initial) to 65535 * ; * * ; * Input: The values in * ; * (Each packed BCD byte has two digits) * ; * * ; * Output: The number displayed on the LCD * ;***************************************************************************** ; show_tone ; ============================================================================ ; 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 pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; Send starting digit location to LCD movlw 'T' ; Send T call data2LCD ; to LCD movlw 'o' ; Send o call data2LCD ; to LCD movlw 'n' ; Send n call data2LCD ; to LCD movlw 'e' ; Send e call data2LCD ; to LCD movlw ':' ; Send semicolon call data2LCD ; to LCD pagesel show_tone ; Back to PAGE 1 ; ; 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) ; clrf scratch ; Clear scratch register swapf BCD_2,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_0_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_digit_0 ; and go send it digit_0_non_zero bsf scratch,0 ; Set scratch bit non-zero addlw 0x30 ; Add offset for ASCII char set (0030XXXX) send_digit_0 pagesel cmnd2LCD ; Switch to PAGE 0 call data2LCD ; Send byte in W to LCD pagesel show_tone ; Back to PAGE 1 ; ; 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_2,w ; Put 1MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) btfsc scratch,0 ; Have we detected first non-zero digit? goto digit_1_non_zero ; Yes, always send the character btfss STATUS,Z ; Is this digit a zero? goto digit_1_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_digit_1 ; and go send it digit_1_non_zero bsf scratch,0 ; Set scratch bit non-zero addlw 0x30 ; Add offset for ASCII char set (0030YYYY) send_digit_1 pagesel cmnd2LCD ; Switch to PAGE 0 call data2LCD ; Send byte in W to LCD pagesel show_tone ; Back to PAGE 1 ; swapf BCD_1,w ; Swap 10 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) btfsc scratch,0 ; Have we detected first non-zero digit? goto digit_2_non_zero ; Yes, always send the character btfss STATUS,Z ; Is this digit a zero? goto digit_2_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_digit_2 ; and go send it digit_2_non_zero bsf scratch,0 ; Set scratch bit non-zero addlw 0x30 ; Add offset for ASCII char set (0030XXXX) send_digit_2 pagesel cmnd2LCD ; Switch to PAGE 0 call data2LCD ; Send data byte in W to LCD pagesel show_tone ; Back to PAGE 1 ; movf BCD_1,w ; Put 1 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) btfsc scratch,0 ; Have we detected first non-zero digit? goto digit_3_non_zero ; Yes, always send the character btfss STATUS,Z ; Is this digit a zero? goto digit_3_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_digit_3 ; and go send it digit_3_non_zero bsf scratch,0 ; Set scratch bit non-zero addlw 0x30 ; Add offset for ASCII char set (0030YYYY) send_digit_3 pagesel cmnd2LCD ; Switch to PAGE 0 call data2LCD ; Send byte in W to LCD pagesel show_tone ; Back to PAGE 1 ; swapf BCD_0,w ; Swap 10 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) btfsc scratch,0 ; Have we detected first non-zero digit? goto digit_4_non_zero ; Yes, always send the character btfss STATUS,Z ; Is this digit a zero? goto digit_4_non_zero ; No, go and construct the non-zero digit movlw ' ' ; Yes, pick up a ASCII space character instead goto send_digit_4 ; and go send it digit_4_non_zero addlw 0x30 ; Add offset for ASCII char set (0030XXXX) send_digit_4 pagesel cmnd2LCD ; Switch to PAGE 0 call data2LCD ; Send data byte in W to LCD ; (Leave PAGE 0 selected) ; movf BCD_0,w ; Put 1 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) ; Always display this character, even if it's zero 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 'H' ; Send an "H" call data2LCD ; to LCD ; movlw 'z' ; Send a 'z' call data2LCD ; to LCD pagesel show_tone ; Back to PAGE 1 ; return ; ; ;***************************************************************************** ; * Name: bin2BCD >>>>> IN PAGE 1 <<<<<<< * ; * * ; * 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 * ; * NOTE: THIS CLOBBERS * ; * * ; * 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 ; ;***************************************************************************** ; * Name: show_freq >>>>> IN PAGE 1 <<<<<<< * ; * * ; * 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 pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; Send starting digit location to LCD pagesel show_freq ; Back to PAGE 1 ; ; 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 pagesel data2LCD ; Switch to PAGE 0 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 ; pagesel show_freq ; Back to PAGE 1 return ; ; ;***************************************************************************** ; * Name: display_mode <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * Purpose: Display the Mode on LCD * ; * * ; * Input: ModeSelect bit set * ; * * ; * Output: Mode (LSB, USB, CW, RevCW) displayed on LCD * ;***************************************************************************** display_mode btfsc ModeSelect,LSB ; Is it LSB? goto DisplayLSB ; Yes, display LSB on LCD btfsc ModeSelect,USB ; Is it LSB? goto DisplayUSB ; Yes, display USB on LCD btfsc ModeSelect,CW ; Is it CW? goto DisplayCW ; Yes, display CW on LCD ; Fall through to display RevCW on LCD DisplayRevCW movlw 0xC0 ; Point LCD at digit 2 of 2nd line pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; movlw 'C' ; Say 'C' (page 0) call data2LCD ; movlw 'W' ; Say 'W' (page 0) call data2LCD ; movlw '+' ; Say '+' for upper side CW (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; pagesel display_mode ; Back to PAGE 1 goto display_mode_exit ; Go to exit ; DisplayLSB movlw 0xC0 ; Point LCD at digit 2 of 2nd line pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; movlw 'L' ; Say 'L' for Lower Sideband (page 0) call data2LCD ; movlw 'S' ; Say 'S' for Lower Sideband (page 0) call data2LCD ; movlw 'B' ; Say 'B' for Lower Sideband (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; pagesel display_mode ; Back to PAGE 1 goto display_mode_exit ; Go to exit ; DisplayUSB movlw 0xC0 ; Point LCD at digit 2 of 2nd line pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; movlw 'U' ; Say 'U' for Upper Sideband (page 0) call data2LCD ; movlw 'S' ; Say 'S' for Upper Sideband (page 0) call data2LCD ; movlw 'B' ; Say 'B' for Upper Sideband (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; pagesel display_mode ; Back to PAGE 1 goto display_mode_exit ; Go to exit ; DisplayCW movlw 0xC0 ; Point LCD at digit 2 of 2nd line pagesel cmnd2LCD ; Switch to PAGE 0 call cmnd2LCD ; movlw 'C' ; Say 'C' for CW (page 0) call data2LCD ; movlw 'W' ; Say 'W' for CW (page 0) call data2LCD ; movlw '-' ; Say '-' for lower side CW (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; movlw ' ' ; Space fill (page 0) call data2LCD ; pagesel display_mode ; Back to PAGE 1 display_mode_exit return ; ; #endif ; End of DriverPIC only ; ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ####################### ###################### ; ####################### PAGE 1 COMMON FOR BOTH PICs ###################### ; ####################### ###################### ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ############################################################################ ; ============================================================================ ; /////////////// INTER-PIC COMMUNICATIONS ROUTINES/////////////////////////// ; ============================================================================ ; ============================================================================ ; ///////////////// SEND ROUTINES //////////////////////////////////////////// ; ============================================================================ ; ; **************************************************************************** ; * Name: send_byte <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Send the comm_out byte * ; * Start with most significant bit of byte. * ; * * ; * Input: comm_out has data byte to send * ; * * ; * Output: W=0 (Z=1) if OK * ; * W=1 (Z=0) if Bad * ; **************************************************************************** ; send_byte bsf comm_flags,SENDINPROGRESS ; Set flag bit call send_sync_sequence; Send synchronization sequence btfss STATUS,Z ; Error detected? (if W=1) goto s_byte_error ; Yes, return error status ; Good sync sequence sent out movf comm_out,w ; Get byte to send (bank0-shared) movwf comm_out_temp ; and move to temp storage (bank0-shared) bcf STATUS,C ; Clear carry bit rlf comm_out_temp,f ; Rotate MS Bit into Carry position (bank0-sh) movlw 0x08 ; Send 8 bits movwf s_bit_count ; into bit count (bank0-shared) s_byte_loop ; Get bit to send rlf comm_out_temp,f ; Rotate bit to send into LS Bit position (bank0-sh) movlw 0x01 ; Mask of bit to extract andwf comm_out_temp,w ; Extract single data bit (bank0-shared) movwf s_bit ; and set it up to send (bank0-shared) call send_bit ; btfss STATUS,Z ; Good result? (good if W = 0) goto s_byte_error ; decfsz s_bit_count,f ; Decrement, put back, and skip if now zero (bank0-sh) goto s_byte_loop ; movf comm_out,w ; Get byte to be sent (bank0-shared) movwf gen_parity_byte ; Store for parity generator (bank0-shared) call generate_parity_bit; Generate parity bit (in W) movwf s_bit ; Save bit to be sent (bank0-shared) call send_bit ; Send the parity bit btfss STATUS,Z ; Good result? (good if W = 0) goto s_byte_error ; ; All data bits are out with no error. clrw ; Set up good status (W=0) goto s_byte_exit ; and exit s_byte_error movlw 0x01 ; Set w=1 (error) s_byte_exit iorlw 0x00 ; Set up Z Flag from W bcf comm_flags,SENDINPROGRESS ; Clear flag bit return ; ; ; **************************************************************************** ; * Name: send_sync_sequence <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Send 9 "1-bits" in a row * ; * This scheme is used because it is impossible for any message * ; * plus parity to have this bit sequence. Even a message of 0xFF * ; * would mean 8 "1-bits" followed by a parity bit of "0" (since * ; * EVEN parity is used). * ; * * ; * Input: None * ; * * ; * Output: W=0 (Z=1) if OK * ; * W=1 (Z=0) if Bad * ; **************************************************************************** ; send_sync_sequence movlw 0x09 ; Send 9 1's movwf s_bit_count ; (bank0-shared) s_sync_loop movlw 0x01 ; Data bit movwf s_bit ; to send (bank0-shared) call send_bit ; Send the bit btfss STATUS,Z ; Good result? (good if w=0) goto s_sync_error ; decfsz s_bit_count,f ; Dec, put back, skip if now zero (bank0-sh) goto s_sync_loop ; ; All sync bits are out with no error. clrw ; set up good status goto s_sync_exit ; and exit s_sync_error movlw 0x01 ; Set w=1 (error) s_sync_exit iorlw 0x00 ; Set up Z Flag from W return ; ; ; **************************************************************************** ; * Name: send_bit <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Send a bit from the comm_out * ; * * ; * Input: s_bit = data bit to send * ; * * ; * Output: W=0 (Z=1) if OK * ; * W=1 (Z=0) if Bad * ; * * ; * INT-PIC messages can sent to the DR-PIC at ANY TIME without looking to * ; * see if the DR-PIC is ready. DR-PIC messages in response to INT-PIC * ; * initiated messages are common. DR-PIC initiated messages are possible, * ; * although they are rare compared to INT-PIC initiated messages. * ; * * ; **************************************************************************** ; send_bit ; As we start we must make sure ACKIN is down. ; ; What can cause ACKIN to remain UP? (Other side is holding ACKOUT UP) ; 1) A SEND collision. Eliminate this possibility by not allowing the ; the DR-PIC to generate unsolicited messages. ; 2) RECEIVE side is not running or is hung. ; Solve: INT-PIC will detect SEND error and reset the DR-PIC. ; 3) RECEIVE routine gets interrupted after raising ACKOUT ; Solve: Wait - potentially for long enough for it to handle an ; interrupt. Look often while waiting! ; clrf comm_count0 ; Initialize counter (bank0-shared) clrf comm_count1 ; Initialize counter (bank0-shared) #ifdef InterfacePIC ; movlw 0x99 ; Want DriverPIC to time out first! #else ; DriverPIC ; movlw 0x13 ; #endif movwf comm_count2 ; Initialize counter (bank0-shared) send_look btfss PORTC,ACKIN ; Get current ACKIN on port pin goto s_bit_continue ; It's down. Ready to go. ; INT: (256*256*153)loops * 4 inst/loop * .2us/inst = 8 sec max wait ; DR: (256*256*19)loops * 4 inst/loop * .2us/inst = 1 sec max wait decfsz comm_count0,f ; Decrement count0 (bank0-shared) goto send_look ; count0 not expired so look again decfsz comm_count1,f ; count0 expired, so look at count1 (bank0-sh) goto send_look ; count1 not expired so look again decfsz comm_count2,f ; count1 expired, so look at count2 (bank0-sh) goto send_look ; count2 not expired so look again goto s_bit_timeout ; All expired so return error status s_bit_continue ; ACKIN is down. Ready to send first bit bcf PORTD,DATAOUT ; Default - clear data bit on port pin movf s_bit,w ; Load data bit into w. (sets up zero flag) (bank0-sh) btfss STATUS,Z ; Is bit set (zero clear)? bsf PORTD,DATAOUT ; Yes, set data bit on port pin ; Data set up. Now set clock. bsf PORTD,CLOCKOUT ; Set clock bit on port pin ; Wait for ACKIN to come back ; This one should not take long, but use dual counters anyway clrf comm_count0 ; Initialize counter (bank0-sh) clrf comm_count1 ; Initialize counter (bank0-sh) #ifdef InterfacePIC ; movlw 0x99 ; Want DriverPIC to time out first! #else ; DriverPIC ; movlw 0x13 ; #endif movwf comm_count2 ; Initialize counter (bank0-sh) s_bit_wait_ack btfsc PORTC,ACKIN ; Get current ACKIN on port pin goto s_bit_good_exit ; It's UP so good exit ; It's possible for this to take a while if the other side gets ; interrupted. Must be patient. ; INT: (256*256*153)loops * 4 inst/loop * .2us/inst = 8 sec max wait ; DR: (256*256*19)loops * 4 inst/loop * .2us/inst = 1 sec max wait decfsz comm_count0,f ; Decrement count0 (bank0-sh) goto s_bit_wait_ack ; count0 not expired so look again decfsz comm_count1,f ; count0 expired, so look at count1 (bank0-sh) goto s_bit_wait_ack ; count1 not expired so look again decfsz comm_count2,f ; count1 expired, so look at count2 (bank0-sh) goto s_bit_wait_ack ; count2 not expired so look again ; Both expired so fall through to timeout s_bit_timeout movlw 0x01 ; Set up error status goto s_bit_exit ; and go to exit s_bit_good_exit clrw ; Clear W s_bit_exit bcf PORTD,CLOCKOUT ; Drop clock (always!) iorlw 0x00 ; Set up Z Flag from W return ; ; ; ============================================================================ ; ///////////////RECEIVE ROUTINES///////////////////////////////////////////// ; ============================================================================ ; ; **************************************************************************** ; * Name: receive_byte <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Receive a message byte * ; * * ; * Input: None * ; * * ; * Output W=0 (Z=1) if OK (comm_in has data byte received) * ; * W=1 (Z=0) if Bad * ; **************************************************************************** ; receive_byte bsf comm_flags,RCVINPROGRESS ; Set Receive-in-progress flag call detect_sync_sequence; Receive synchronization sequence btfss STATUS,Z ; Good result? (good if W=0) goto r_byte_exit ; exit with bad status (W=1) movlw 0x08 ; Get 8 bits of data movwf r_bit_count ; (bank0-sh) r_byte_loop call receive_bit ; Get a bit btfss STATUS,Z ; Good result? (good if W = 0) goto r_byte_exit ; No, exit with bad status (W=1) rrf r_bit,f ; Move new bit (LS bit) into carry (bank0-sh) rlf comm_in,f ; Shift from carry into input byte (LS bit) (bank0-sh) decfsz r_bit_count,f ; More bits to collect? (bank0-sh) goto r_byte_loop ; Yes, loop back ; Get one more bit - the parity bit call receive_bit ; Get the parity bit in r_bit btfss STATUS,Z ; Good result? (good if W = 0) goto r_byte_exit ; No, exit with bad status (W=1) movf comm_in,w ; Get newly constructed data byte (bank0-sh) movwf gen_parity_byte ; Store for generate parity routine (bank0-sh) call generate_parity_bit; Generate the parity for received byte ; Return with W = calculated parity (LS bit) subwf r_bit,w ; Subtract received parity from calculated (bank0-sh) btfsc STATUS,Z ; Result is zero if parity bits equal goto r_byte_exit ; Go to exit with W=0 if OK r_byte_parity_error ; bsf PORTC,LED_R_PAR_ERR; Set Parity Error LED (W is non-zero) r_byte_exit bcf comm_flags,RCVINPROGRESS ; Set Receive-in-progress flag return ; return with status in W ; **************************************************************************** ; * Name: detect_sync_sequence <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: look for 9 1's in a row * ; * * ; * Input: None * ; * * ; * Output: W=0 (Z=1) if OK * ; * W=1 (Z=0) if Bad * ; **************************************************************************** ; detect_sync_sequence movlw 0x09 ; Look for 9 in a row movwf r_bit_count ; (bank0-sh) d_sync_loop call receive_bit ; Get a bit btfss STATUS,Z ; Good result? (good if W = 0) goto d_sync_error ; No, Error exit movlw 0x01 ; Yes, set up to test the bit subwf r_bit,w ; Result will be zero if r_bit is a "1" (bank0-sh) btfss STATUS,Z ; Is zero flag set? goto d_sync_error ; No, error decfsz r_bit_count,f ; Dec, put back, skip if now zero (bank0-sh) goto d_sync_loop ; clrw ; Good status when W=0 goto d_sync_exit ; and exit d_sync_error movlw 0x01 ; Return error status (W=1) d_sync_exit iorlw 0x00 ; Set up Z flag from W return ; ; ; **************************************************************************** ; * Name: receive_bit <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Receive a bit for the message byte * ; * * ; * Input: None * ; * * ; * Output: W=0 (Z=1) if OK (r_bit set up with data bin in LS bit position)* ; * W=1 (Z=0) if Bad * ; **************************************************************************** ; receive_bit ; wait for CLOCKIN, indicating data is on the line clrf comm_count0 ; Initialize counter (bank0-sh) clrf comm_count1 ; Initialize counter (bank0-sh) #ifdef InterfacePIC ; movlw 0x99 ; Want DriverPIC to time out first! #else ; DriverPIC ; movlw 0x13 ; #endif movwf comm_count2 ; Initialize counter (bank0-sh) r_bit_clock_wait btfsc PORTD,CLOCKIN ; Get clock signal from port pin goto r_bit_continue ; Clock is now high. Ready to go. ; INT: (256*256*153)loops * 4 inst/loop * .2us/inst = 8 sec max wait ; DR: (256*256*19)loops * 4 inst/loop * .2us/inst = 1 sec max wait decfsz comm_count0,f ; Decrement count0 (bank0-sh) goto r_bit_clock_wait ; count0 not expired so look again decfsz comm_count1,f ; count0 expired, so look at count1 (bank0-sh) goto r_bit_clock_wait ; count1 not expired so look again decfsz comm_count2,f ; count1 expired, so look at count2 (bank0-sh) goto r_bit_clock_wait ; count2 not expired so look again goto r_bit_timeout ; All expired so return error status r_bit_continue ; CLOCKIN is up. Ready to take the bit off the line clrw ; default (good status) btfsc PORTC,DATAIN ; Get Data bit from Port pin movlw 0x01 ; It's a 1 movwf r_bit ; Save the bit (bank0-sh) bsf PORTD,ACKOUT ; Set the ACKOUT bit on Port pin ; Wait for CLOCKIN to drop again clrf comm_count0 ; Initialize counter (bank0-sh) clrf comm_count1 ; Initialize counter (bank0-sh) #ifdef InterfacePIC ; movlw 0x99 ; Want DriverPIC to time out first! #else ; DriverPIC ; movlw 0x13 ; #endif movwf comm_count2 ; Initialize counter r_bit_clock_wait2 btfss PORTD,CLOCKIN ; Get clock signal from port pin goto r_bit_good_exit ; Now it's down so good exit ; INT: (256*256*153)loops * 4 inst/loop * .2us/inst = 8 sec max wait ; DR: (256*256*19)loops * 4 inst/loop * .2us/inst = 1 sec max wait decfsz comm_count0,f ; Decrement count0 (bank0-sh) goto r_bit_clock_wait2 ; count0 not expired so look again decfsz comm_count1,f ; count0 expired, so look at count1 (bank0-sh) goto r_bit_clock_wait2 ; count1 not expired so look again decfsz comm_count2,f ; count1 expired, so look at count2 (bank0-sh) goto r_bit_clock_wait2 ; count2 not expired so look again ; fall through to timeout r_bit_timeout movlw 0x01 ; Set up error status goto r_bit_exit ; and go to exit r_bit_good_exit clrw ; Set up good status r_bit_exit bcf PORTD,ACKOUT ; Drop ACK (Always!) (Doesn't affect STATUS) iorlw 0x00 ; Set up Z flag from W return ; ; ; ============================================================================ ; ///////////////COMMON ROUTINES FOR SEND AND RECEIVE //////////////////////// ; ============================================================================ ; ; **************************************************************************** ; * Name: generate_parity_bit <<<<<<<< IN PAGE 1 <<<<<< * ; * * ; * All in Bank 0 * ; * * ; * Purpose: Calculate parity bit for comm_out * ; * * ; * Input: gen_parity_byte * ; * * ; * Output: W set up with calculated parity bit in LS position * ; **************************************************************************** ; generate_parity_bit ; ; ---------------------------------------------------------------------------- ; 8-bit EVEN parity (from PICLIST.com) ; This routine will leave the EVEN parity of X in X.0 ; while blenderizing most of the rest of X ; ; swapf X, W ;x = abcdefgh w = efghabcd ; xorwf X, F ;x = abcdefgh w = efghabcd ; ; xor efghabcd ; rrf X, W ;x = abcdefgh w = -abcdefg ; ; xor efghabcd xor -efghabc ; xorwf X, F ;x = abcdefgh w = -abcdefg ; ; xor efghabcd xor -efghabc ; ; xor -abcdefg ; ; xor -efghabc ; ; ; at this point, the parity for half the bits ; ; (a, b, e, and f) is in bit 2 of X, and the ; ; parity for the other half (bits c, d, g, and h) ; ; is in bit 0 of X. ; ; btfsc X, 2 ; if the parity of (a,b,e,f) is 0, ; ; then the parity of (a,b,c,d,e,f,g,h) ; ; is equal to the parity of (c,d,g,h)... ; ; which is already in bit 0, so skip ahead. ; ; incf X, F ; otherwise, the parity of (a,b,e,f) is 1, ; ; so the parity of (a,b,c,d,e,f,g,h) is ; ; NOT equal to the parity of (c,d,g,h). ; ; invert bit 0. ; ; ; at this point, bit 0 contains the parity of ; ; (a,b,c,d,e,f,g,h). ; ---------------------------------------------------------------------------- swapf gen_parity_byte,w ; (bank0-sh) xorwf gen_parity_byte,f ; (bank0-sh) rrf gen_parity_byte,w ; (bank0-sh) xorwf gen_parity_byte,f ; (bank0-sh) btfsc gen_parity_byte,2 ; (bank0-sh) incf gen_parity_byte,f ; Parity is in bit 0 of gen_parity_byte (bank0-sh) ; clrw ; Default btfsc gen_parity_byte,0 ; Is bit 0 clear? (bank0-sh) movlw 0x01 ; No, set to 1 return ; ; END