; **************************************************************************** ; * CEGen-DCPro - Variable Frequency Oscillator with Direct Digital * ; * Synthesis * ; * Version 3.q - R2Pro/T2Pro DIRECT CONVERSION - AD9912 * ; * * ; * By C. Buck Ewing, KD8RRI * ; * 3June2014 * ; * * ; **************************************************************************** ; ; **************************************************************************** ; ; CEGen programs are designed as "Stand Alone" Variable Frequency Oscillators's. ; In other words - NO Personnal Computer for operation. ; ; >>>>>>>>>> "Serial EPROM MUST BE INSTALLED AND OPERATIONAL" <<<<<<<<<<<< ; ; Description: ; ; CEGen-R2 is a VFO designed to be a local oscillator for the R2Pro Direct ; Conversion receiver and T2Pro Direct Conversion Transmitter. ; ; The VFO uses the AD9912-PIC PCB designed by David Brainerd (WB6DHW). This PCB ; contains a Microchip PIC18F2550 microcontroller, and an Analog Devices AD9912 ; Direct Digital Synthesis (DDS) IC. The PCB also contains an Avago MSA-1105 ; RF amplifier. Details of this PCB can be found at David's website, www.WB6DHW.com. ; ; AD9912 analog output is fed to a low pass filter and then applied to the AD9912 ; FDBK IN. The AD9912 DDS HSTL output is used and is a complementary, 1.2 Volt ; Peak-to-Peak square wave. The HSTL output feeds a Quadrature Generator to supply ; the I/Q frequency required by the R2/T2Pro. Frequency range of the VFO is 1.5 MHz ; to 200 MHz. Frequency range can possibly be extended to 400 MHz using the AD9912 ; HSTL Doubler and is programmed for use. However, I have no way of testing above ; 200 MHz. ; ; Quadrature Generator circuit uses PECL logic elements that have a maximum rating of ; 3 GHz. Quadrature Generator circuit has two divide sections. The Quadrature Generator ; uses two flip-flops to generate the I/Q signals and this circuit always divides the ; input frequency by 2. A divide by four can be invoked, prior to the divide by 2 circuit, ; when the Divide By 4 signal from the microcontroller is high (+5V). ; ; HSTL output of the AD9912 has a minimum frequency specification of 20 MHz. Testing ; has shown that an acceptable signal is available down to about 8 MHz. ; Multiple conditions exist that determine how the actual output frequency of the VFO ; is generated. ; Operator required frequency, program multiply, DDS analog output frequency, ; HSTL multiply, AD9912 HSTL output frequency and Quadrature Generator divide selection. ; They are summarized below: ; Displayed Program DDS Analog HSTL HSTL Output I/Q Gen I/Q Generator ; Frequency Multiply Output Freq. Multiply Frequency Divide Output Frequency ; 1.5-10 MHz 8 10-80 Mhz 1 10-80 MHz 8 1.5 - 10 MHz ; 10-200 MHZ 2 20-400 MHz 1 20-400 MHz 2 10 - 200 MHz ; 200-400 MHz 1 200-400 MHz 2 400-800 MHz 2 200 - 400 MHz ; ; The PIC 18F2550 microcontroller is also used for other purposes. R2Pro LNA selection. ; Sideband and CW selection relay control. Receive/Transmit control. Signal mode control. ; Bar Graph generation. Memory control. ; ; ***************************************************************************** ; ; Following are credits, Hardware and Software Configurations, explanation of ; Controls and various features of the Local Oscillator. ; ; Most critical are the Hardware and Software Configurations. Clock ; Frequency and Clock Multiplier for the AD9912 DDS must be properly defined. ; ;****************************************************************************** ; CREDITS --- ; The concepts of most of the code in this program was done by ; the following: ; ; Curtis W. Preuss - WB2V ; Bruce Stough - AA0ED ; Craig Johnson - AA0ZZ ; Bob Okas - W3CD ; Other's, I'm sure ; Thanks to John McDonough, WB8RCR, for his great Elmer 160 course ; ; Kudos to these men, they are Giants and Mentors. ; ; Math, and conversion routines from PIC18F2550 Specifications, ; Microchip AN526e and the contributors to the PIClist web site. ; Some changes/extensions have been made to math routines. ; ; C. Buck Ewing, KD8RRI ; **************************************************************************** ; ; Hardware And Software Configurations ; ;***************************************************************************** radix dec ;***************************************************************************** ; AD9912 DDS Clock Definitions * ; * ; Define the Input Clock to the AD9912 DDS. Input Clock value is defined at * ; INPUT_CLOCK and is specified in Hz. Maximum Input Clock frequency is * ; 1.2 GHz (INPUT_CLOCK * CLK_MULT). If the computed DDS System Clock is * ; greater than 1.2 GHz, a message will be displayed and firmware will halt. * ; Set INPUT_CLOCK to DDS input clock frequency. * ; * ; Define the AD9912 Clock Multiplier. The Input Clock supplied to AD9912 is * ; multiplied by the multiplier to supply DDS system clock. * ; Multiplier can be one, for no multiplication, or an EVEN integer between * ; 4 and 66 for actual multiplication. If the multiplier specified does not * ; meet the above requirements, a message will be displayed and firmware will * ; halt. * ; Set CLK_MULT to desired clock multiplier. * ; * ;***************************************************************************** INPUT_CLOCK set 1000014000 ; Hz ; ^^^^^^^^^^ ; GMMMKKKHHH CLK_MULT equ 1 ; Clock Multiplier - 4 to 66 (Even Integers) ; or 1 radix hex ;***************************************************************************** ; Frequency Display Options * ; * ; Multiple options are provided for displaying the frequency. * ; M = Megahertz, K = Kilohertz, and H = Hertz. * ; * ; MMM,KKK,HHH Hz - Use the #define Hz statement. * ; MMM,KKK.HHH KHz - Use the #define KHz statement * ; MMM.KKK,HHH MHz - Use the #define MHz statement * ; Pick the format desired and uncomment that statement. * ; * ;***************************************************************************** #define HZ ; MMM,KKK,HHH Hz ;#define KHZ ; MMM,KKK.HHH KHz ;#define MHZ ; MMM.KKK,HHH MHz ;***************************************************************************** ; BAR GRAPH OPTIONS * ; An optional Bar Graph can be displayed. The Bar Graph is on LCD line 2, * ; characters 12 to 16. Analog To Digital Convertor (ADC) AN0 in the PIC * ; 18F2550 is used to generate the Bar Graph. * ; The input signal is appled to H5-2 and sent to Pin 2 of The PIC 18F2550. * ; To enable the Bar Graph uncomment the define BAR_GRAPH statement. * ; * ; Default ADC sampling time is every 0.10 seconds. * ; The sampling time can be increased in 0.10 second intervals. To increase * ; the sampling time, change the ADC_COUNT below to X times 0.10 seconds. * ; * ; When in receive mode the Bar Graph is configured as an S-Meter. * * ; In Transmit mode the Bar Graph is a linear zero to ten. * ; The maximum input voltage for default Bar Graph is +5 Volts. * ; Voltage above this value must be scaled to 5 volts. The input voltage must * ; be defined using BG_MAX_VOLT_UN and BG_MAX_VOLT_DE. This allows using * ; voltage less than +5 volts. * ; * ; A Zero-Center Bar Graph is optional. To use the Zero-Center bar graph, * ; uncomment the #define BG_CENTER. * ; If the Zero-Center is defined, the center voltage must be specified at * ; BG_CTR_VOLT_UN and BG_CTR_VOLT_DE. * ; * ; Maximum voltage swing from center must also be defined at BG_MAX_VOLT_UN * ; and BG_MAX_VOLT_DE. * ; BG_CTR_VOLT_X + BG_MAX_VOLT_X MUST NOT exceed +5 volts. * ; BG_CTR_VOLT_X - BG_MAX_VOLT_X MUST NOT go below 0 volts. * ; * ;***************************************************************************** ;#define BAR_GRAPH ; Enable the Bar Graph IFDEF BAR_GRAPH ADC_COUNT equ 1 ; Sample every 0.1 seconds BG_MAX_VOLT_UN equ D'5' ; Maximum input voltage - whole units of voltage BG_MAX_VOLT_DE equ D'00' ; Maximum input voltage - decimal value ;#define BG_CENTER ; Bar Graph is a zero center graph BG_CTR_VOLT_UN equ D'3' ; Center input voltage - whole units of voltage BG_CTR_VOLT_DE equ D'50' ; Center input voltage - decimal value ENDIF radix dec ;***************************************************************************** ; The default startup band, after firmware burn, is 1. * ; * ; After the first power up, VFO registers will be periodically saved and * ; used on the next band selection or power up. The contents of both VFO's * ; are saved, in their respective locations, in the band table. If VFO-A is * ; on band 2 and VFO-B is on band 7, VFO-A contents is stored in band 2 VFO-A * ; section. Likewise VFO-B contents are stored in band 7 VFO-B section. The * ; initial band frequency selection will be overlayed. So, at power up or * ; when changing band, the VFO's will be loaded with the last frequency used * ; on the band(s) selected. * ; * ; count_of_ovfl is the number of 0.1 second intervals between saving. * ; * ;***************************************************************************** ; count_of_ovfl equ 100 ; Overflows before saving (10.0 seconds) radix hex ; **************************************************************************** ; ENCODER CONFIGURATION * ; Moving the Entry Marker and making Menu selections with some encoders is * ; difficult. To require more encoder movement a counter is implemented that * ; is counted down as the encoder is moved. If an optical encoder has 128 * ; pulses per revolution, setting the count to 16 would then require 1/8 of * ; a rotation before the action would take place. Simularly a count of 3 * ; would require a rotation of 1/8 for a 24 pulse mechanical encoder. * ; Set the count for desired rotation delay. This does not affect encoder * ; operation during tuning. Set MENU_COUNTER for number of pulses to wait * ; before taking action. MUST NOT BE ZERO!! * ; * ; Defining DETENT_ENCODER enables software to debounce mechanical encoders * ; and modifies the incrementing so that only one count changes at each * ; detent. Comment out the line below for optical or detent-less encoders. * ; **************************************************************************** MENU_COUNTER equ 8 ; NOT ZERO! ;#define DETENT_ENCODER ; Uncomment for Mechanical Tuning Encoder ;***************************************************************************** ; Following is the definition the Calibration Frequency * ; Calibration frequency can be modified. Convert desired new frequency * ; to Hexidecimal and replace the values listed below. * ; This frequency is divided by 2 or by 8 in the Quadrature Generator. * ;***************************************************************************** cal_freq_0 equ 0x00 ; LSByte - 400 MHz cal_freq_1 equ 0x84 ; cal_freq_2 equ 0xD7 ; cal_freq_3 equ 0x17 ; MSByte - 400 MHz ; **************************************************************************** ; Keep the Version number here * ; **************************************************************************** #define CODE_VERSION "3.q" ;***************************************************************************** ; * ; Control Operation * ; * ;***************************************************************************** ; There are five manual controls: ; 1. Frequency Rotary Encoder ; 2. Menu Pushbutton switch ; 3. VFO Pushbutton switch ; 4. RIT Rotary Encoder ; 5 RIT Encoder Push Switch ; ; Frequency Rotary Encoder ; Rotary encoder is used to change frequency at position of entry marker. ; When in menu, encoder is used to move through menu items. For instance, ; in the Select Band menu, encoder movement will scroll through the band table ; entries. Some menu selections do not have items but encoder must be moved ; to confirm that the action should be completed. ; The encoder is also used, in conjunction with Menu switch, to move ; entry marker. ; ; Menu Switch ; If depressed during power-on, enter Calibration mode. The display shows ; xxx,xxx,xxx Hz and CALIBRATE and the calibration signal is generated. This ; frequency can be changed by altering the constant at cal_freq_0..3. The ; output should be monitored with a Frequency Counter, or other means, for ; correct frequency. ; While pressing Menu Switch, the Frequency Encoder is used to ; adjust output frequency to the calibration frequency. It is not necessary ; to hold Menu Switch if the Frequency Encoder is not moved allowing ; entering calibrate mode and waiting for thermal stabalization before ; calibrating. Once output frequency is correct, release Menu ; Switch and turn Frequency Encoder. Normal operation then starts. ; ; Depressing the Menu switch and moving Frequency Encoder will ; move the entry marker through the frequency digits. ; ; Depressing Menu switch and holding for two seconds will enter the menu. ; MENU will display until Menu Switch is released and then first menu ; selection will display. The MENU is used to: ; Select Band ; Select Signal Mode ; Enable/Disable Split Mode ; Save/Retrieve/Erase Memory entry ; Change CW Offset ; Erase Memory ; If an item is selected, by moving Frequency Encoder, momentarily depressing ; Menu Switch will complete menu action and return to normal display. ; If no Frequency Encoder movement occurs, momentarily depressing the Menu ; Switch will cycle to the next menu selection. Repeatedly pressing the Menu ; Switch will cycle through menu selections and exit menu. ; ; VFO Switch ; There are two independent VFO's, VFO-A and VFO-B. Momentarily depressing ; the VFO switch will switch between the VFO's. ; Momentarily depressing VFO switch, while in MENU, will exit menu. ; Holding VFO Switch depressed for 2 seconds will copy active VFO registers ; to the inactive VFO registers. ; ; RIT Encoder ; This encoder is used to change the Receive Incremental Tuning frequency. ; Encoder is also used to adjust the RIT Step Size. ; ; RIT Encoder Switch ; Momentarily depressing the RIT Encoder Switch will turn the RIT On or OFF. ; Hold the RIT Encoder Switch for 1 seconds and "RIT Step Size XXX" will be ; displayed. This allows adjusting the size of the Hz increment added or ; subtracted from the RIT frequency. The startup default is 10 Hz. ; Turning the RIT Encoder will adust the Step Size in 10 Hz increments. ; Maximum Step Size is 250 Hz, minimum Step Size is 10 Hz. ; When desired Step Size is reached, momentarily press the RIT Encoder ; Switch will save the Step Size. ; ; R/T Input ; Input to switch VFO between receive and transmit. + 5 Volts for ; receive and 0 Volts for transmit. When R/T input is 0 Volts, Tx will ; be displayed on the display to the right of the VFO designator. ; Split mode operation, enabled by MENU, uses VFO "A" for receive and ; VFO "B" for transmit. Switching is controlled by R/T input. When in ; transmit and CW mode, the CW offset is not added to displayed frequency. ; If RIT is on, the RIT frequency is not added during transmit. ; ;***************************************************************************** ;***************************************************************************** ; ; Receive Incremental Tuning (RIT) ; ; The maximum frequency adjustment is +/- 5,000 Hz. ; RIT is controlled by a RIT Rotary Encoder with attached push switch. When the ; RIT Encoder Switch is depressed, both Menu and VFO switch inputs are pulled ; low and the program determines that RIT processing is needed. ; There are two ways that RIT is turned off. ; 1. RIT Encoder Switch ; 2. Changing bands ;***************************************************************************** ; ; Bar Graph ; ; Bar Graph has been implemented that can be used to display signal strength, ; power level or another DC voltage. The bar graph is implemented using ; one of the Analog To Digital (ADC) convertors in the PIC 18F2550. ; Input DC Voltage is fed through a 330 ohm Resistor. The input voltage is ; defined in the configuration section. ; The bar graph is displayed on the LCD bottom line, characters 12 through 16. ; Each block in the bar graph represents 20% of the input signal. ; There are also partial blocks that represent 20, 40, 60 or 80 percent of ; a block. A numerical value preceeds the bar graph that indicates the number ; of full blocks displayed. ; The reference voltages for the ADC is 0 Volts (Vss) and 5 Volts (Vdd). ;***************************************************************************** ;***************************************************************************** ; ; External Interface and Memory ; ; This VFO is designed to control the receiver/transmitter. That is band, ; signal mode, LNA selection, filter selection and Receive/Transmit sequencing ; is controlled by the VFO. ; To do this an external PCB has been added. Access to the PCB is via a ; Serial interface. The serial interface uses 4, Port C pins, and 1, Port ; B pin of the PIC 18F2550. ; PIC Pin and Serial Interface Signal: ; 28 RB7 EIDI Data to Serial Interface ; 11 RC0 EIDO Data from Serial Interface ; 13 RC2 EICS Chip Select of Serial Interface ; 17 RC6 EIPK Parallel Transfer Clock ; 18 RC7 EICK Serial Clock to Serial Interface ; ; The added PCB has two, 8 bit, shift registers in series. The serial interface ; is used to shift the required control signals into the two shift registers. ; When the control signals have been moved into the shift registers, they are ; then transferred into two, 8 bit, register IC's. The two registers buffer the ; control signals. This provides 16 external control lines. ; ; The serial interface also allows control of a Serial EPROM (SEPROM). ; The program was written for, and testing done, with a Microchip 25AA1024 ; serial EEPROM. This allows saving and retreiving VFO frequency, signal ; mode, entry marker and band select information. ; The 25AA1024 is a 1.024 megabit - 128 Kilobyte EEPROM. The first 256 bytes ; are reserved. ; Each entry saved is 16 bytes in length. The memory is limited to 4095 entries ; to be saved. Saving, retreiving and deleting Memory entries is done with ; the main menu. Format of a memory entry is: ; 1 byte - Band number ; 4 bytes - Frequency ; 1 byte - Entry marker location ; 1 byte - Mode control ; 1 byte - Band Index ; 1 byte - Band external control ; ; Start up information is also saved in SEPROM. ; ;***************************************************************************** ;***************************************************************************** ; ; LCD Display ; ; ------------------ ; | 14,227,865 Hz | ; |A LS | ; ------------------ ; ; In normal operation the LCD display will appear as above. ; The top Line is the frequency. Optionally it can be KHz ; or MHz. ; Far left, on bottom line, is the active VFO - "A". ; In the center of bottom line is signal mode. ; Signal Modes Are: ; LS - Lower Single Sideband ; US - Upper Single Sideband ; C+ - CW Plus ; C- - CW Minus ; AM - Amplitude Modulation ; DS - Double Sideband ; PM - Phase ; ; ------------------ ; | 14,227,865 Hz | ; |A LS + 250| ; ------------------ ; This display is the same as above with the RIT display shown ; at far right on bottom line. ; ; ------------------ ; | 7,295,000 Hz | ; |B-Tx C+ 8XXXX|| ; ------------------ ; In this example VFO-B is active and Receive/Transmit (R/T) input control ; is low (Ground). This display will also occur when R/T is low in Split ; Mode of operation. Far right on the bottom line is the Bar Graph. ; ;***************************************************************************** ;***************************************************************************** ; ; Controller - PIC18F2550 R2Pro Direct Conversion LO ; __________ ; MCLR/Vpp---->RE3 |1 28| RB7>----DDS_EIDI,LCD_RS(H4-13) ; (H5-2)AN0---->RA0 |2 27| RB6>----DDS_SCLK (H4-12) ; (H5-1)ENC_A---->RA1 |3 26| RB5>----DDS_IOUPDATE (H4-11) ; (H5-4)ENC_B---->RA2 |4 25| RB4>----LCD_E (H4-10) ; (H5-3)MENU-SW---->RA3 |5 24| RB3>----EIDI, LCD_14 (H4-9) ; (H5-6)VFO-SW---->RA4 |6 23| RB2>----LCD_13 (H4-8) ; (H5-5) R/T---->RA5 |7 22| RB1>----LCD_12 (H4-7) ; GROUND--------LCD_11 (H4-6) ; 20MHz> XTAL----OSC1 |9 20| VDD-----+5 V ; 20MHz> XTAL----OSC2 |10 19| VSS-----GROUND ; (H4-1) EIDO---->RC0 |11 18| RC7>----EICK (H4-3) ; DIV_BY4,HEART--------EIPK (H4-2) ; (H4-4) EICS---- restore_flag ;****************************************************************************** VCO_LOW equ 0 ; DDS VCO set for low range VCO_MID equ 1 ; DDS VCO set for middle range VCO_HI equ 2 ; DDS VCO set for high range NOT_1ST equ 6 ; Indicates not first time through startup, use ; saved registers POWER_UP equ 7 ; Indicates processing power up ;****************************************************************************** ; Operating Mode and Signal Mode Control flags - MODE_flags ; ;****************************************************************************** ; Signal mode control - Bits 0 to 2 AM equ .0 ; AM Mode SSBU equ .1 ; Upper SSB mode SSBL equ .2 ; Lower SSB mode CW_POS equ .3 ; CW positive offset CW_NEG equ .4 ; CW negative offset DS equ .5 ; Double Sideband PM equ .6 ; Phase SPLIT equ 7 ; Split - VFO-A receive, VFO-B transmit ;****************************************************************************** ; Receive Incremental Tuning (RIT) Flags - RIT_flags ; ;****************************************************************************** RIT_VFOA equ 0 ; VFO-A using RIT, 1= On, 0= Off RIT_VFOB equ 1 ; VFO-B using RIT RITENC equ 2 ; RIT encoder moved while in RIT Menu RIT_STEP equ 3 ; RIT Step Size being adjusted ;****************************************************************************** ; Serial EPROM Flags - SEEP_flags ; ;****************************************************************************** RA equ 6 ; Bit 4 - Processing must be done during menu exit EA equ 7 ; Bit 5 - Processing must be done during menu exit ; **************************************************************************** ; * Assign names to PIC 18F2550 IO pins. * ; **************************************************************************** ; ; A register bits: ; RA0 is ADC, RA1 & RA2 are Tuning Encoder ; MENU_SW equ 0x03 ; Menu Switch VFO_SW equ 0x04 ; Vfo Switch RT equ 0x05 ; Receive/Transmit Input. Low=Transmit, High=Receive ; ; B register bits: ; RB0 thru RB3 are LCD Data ; EIDI equ 0x03 ; Data To the External Interface (Also LCD Pin 14) LCD_E equ 0x04 ; LCD Enable DDS_UPDATE equ 0x05 ; I/O Update to DDS DDS_SCLK equ 0x06 ; DDS serial clock DDS_SDIO equ 0x07 ; DDS serial data LCD_RS equ 0x07 ; LCD RS 0=Command 1=Data ; ; C Register bits: ; RC4 and RC5 are RIT Encoder ; EIDO equ 0x00 ; Data From External Interface DIV_BY4 equ 0x01 ; Controls Divide by 4 in I/Q Generator. 1 = Divide by 4 EICS equ 0x02 ; External Interface Chip Select EIPK equ 0x06 ; External Interface Parallel Clock EICK equ 0x07 ; External Interface Serial Clock ; ; **************************************************************************** ; * * ; * Allocate variables in Access Bank General Purpose RAM * ; * * ; **************************************************************************** ; CBLOCK ACCESS_BASE ; Start Access Bank - 96 locations (0x5F) freq_0 ; Frequency working storage (hex)(4 bytes)-0x00 freq_1 ; Calc_dds_ftw - actual output frequency freq_2 ; Bin2BCD displayed frequency freq_3 fstep_0 ; Frequency inc/dec - 0x04 fstep_1 ; (4 bytes) fstep_2 ; Update_decade fills these in fstep_3 ; with values from decade_tbl clk_rat_0 ; DDS Clock Ratio (6 bytes) LSByte - 0x08 clk_rat_1 ; clk_rat_2 clk_rat_3 clk_rat_4 clk_rat_5 ; MSByte band_low_0 ; Low frequency limit byte 0, Inited by Get_band 0x0E band_low_1 ; Low frequency limit byte 1 band_low_2 ; Low frequency limit byte 2 band_low_3 ; Low frequency limit byte 3 band_high_0 ; High frequency limit byte 0, Inited by Get_band 0x12 band_high_1 ; High frequency limit byte 1 band_high_2 ; High frequency limit byte 2 band_high_3 ; High frequency limit byte 3 BCD_0 ; Display frequency (BCD) - 0x16 BCD_1 ; (5 bytes) BCD_2 BCD_3 BCD_4 DDS_0 ; LSByte of Frequency Tuning Word (FTW) - 0x1B DDS_1 ; FTW DDS_2 ; FTW DDS_3 ; " DDS_4 ; " DDS_5 ; MSByte of FTW DDS_6 ; Required for FTW calculation, then used as DDS_7 ; DDS register address - MSByte of register address cur_pos ; Cursor (decade position) - 0x23 byte2send ; Byte being sent to DDS, External control, etc. - 0x24 timer1 ; Used in delay routines - 0x25 timer2 ; " timer3 ; " en_new ; New value of Tuning encoder pins A and B - 0x28 en_old ; Old value of Tuning encoder pins A and B en_read ; Tuning Encoder pins A and B and switches last_dir ; Indicates last direction of encoder next_dir ; Indicates expected direction count ; Multipurpose counter - 0x2D MISC_flags ; Miscellaneous flags - 0x2E PB_wait_count ; Push Button wait control timer - 0x2F temp ; Miscellaneous variable - 0x30 temp1 ; Working storage decade ; Frequency decade subject to updating - 0x32 enc_counter ; Divide by 4 counter for mechanical encoder - 0x33 vfo_select ; A = 0x00, B = 0x01 - 0x34 ; DO NOT disturb the order of the VFO ; registers as their adjacency is required. vfo_a_band ; Stores the current VFO A band number - 0x35 vfo_a_0 ; Used to store the frequency setting vfo_a_1 ; of VFO A vfo_a_2 ; " vfo_a_3 ; " MSByte decade_a ; Stores VFO A decade mode_a ; VFO-A mode controls vfo_b_band ; Current VFO B band number storage - 0x3C vfo_b_0 ; Used to store the frequency setting vfo_b_1 ; of VFO B. vfo_b_2 ; " vfo_b_3 ; " MSByte decade_b ; Stores VFO B decade mode_b ; VFO-B mode controls vfo_pointer ; Pointer to active vfo_X_0. - 0x43 band_index ; Index into band table for band in use. Set by Get_band - 0x44 MODE_flags ; Mode control flags, working - 0x45 MENU_flags ; Menu flags and control, working - 0x46 menu_enc_ctr ; Counter used in Menu operations - 0x47 menu_ctr_val ; Value used to load menu counter (encoder pulses) - 0x48 T0_ofl_ctr ; Counts number of Timer 0 overflows - 0x49 PWR_flags ; Flags for power up handling - 0x4A send_dds_stop ; Pointer - last byte to send to AD9912 - 0x4B DDS_flags ; DDS flags - 0x4C dds_pdr_reg ; AD9912 active Power-Down and Reset register - 0x4D decade_max ; Maximum decade position on LCD - 0x4E ext_ctl_0 ; External control byte 0, 0x4F ext_ctl_1 ; External control byte 1 RIT_flags ; Flags used for Receive Incremental Tuning - 0x51 SEEP_flags ; Flags for Serial EPROM access - 0x52 se_count ; Number of bytes to read or written to Serial EEPROM - 0x53 adc_counter ; Counter to control ADC sampling time - 0x54 smtr_mult_0 ; MSByte, Multiplier used to adjust ADC count for input voltage - 0x55 smtr_mult_1 ; LSByte, bg_cnts_block ; ADC counts per block - 0x57 bg_cnts_bar ; ADC counts per bar - 0x58 bg_1tenth_bar ; Ten percent of ADC counts per bar - 0x59 bg_ctr_cnt_hi ; ADC count for Zero-Center zero voltage - 0x5A bg_ctr_cnt_lo ; " bg_ctr_cnts_blk ; ADC counts per block for Zero-Center bar graph - 0x5C bg_ctr_cnts_bar ; ADC counts per bar for Zero-Center bar graph - 0x5D ; >>>>>>>>>>>>>>> Last location in Access Bank GPR is 0x5F <<<<<<<<<<<<<<< ENDC ; ; **************************************************************************** ; * * ; * Allocate variables in Ram Bank 1 * ; * * ; **************************************************************************** ; CBLOCK RB1_BASE cwoffs_pos_0 ; Working value of positive Offset added - 0x00 cwoffs_pos_1 ; to displayed frequency cwoffs_neg_0 ; Working value of negative Offset added - 0x02 cwoffs_neg_1 ; to displayed frequency rit_new ; New value of RIT encoder pins A and B - 0x04 rit_old ; Old value of RIT encoder pins A and B rit_read ; Contents of PORT C rit_last ; Indicates last direction of RIT encoder rit_next ; Indicates expected direction rit_counter ; Counter for RIT Encoder rit_vfoa_0 ; VFO-A RIT frequency byte 0 - 0x0A rit_vfoa_1 ; VFO-A RIT frequency byte 1 rit_vfob_0 ; VFO-B RIT frequency byte 0 - 0x0C rit_vfob_1 ; vfo-B RIT frequency byte 1 rit_step_0 ; RIT step size - 0x0E rit_step_1 seprom_a_0 ; Serial EPROM address LSByte. Used to address SEEPROM - 0x10 seprom_a_1 ; " seprom_a_2 ; Serial EPROM address MSByte sewrite_a_0 ; Serial EPROM next write address LSByte - 0x13 sewrite_a_1 ; " sewrite_a_2 ; Serial EPROM next write address MSByte se_buf_0 ; 16 byte Serial EPROM buffer - LSByte - 0x16 se_buf_1 ; " se_buf_2 ; " se_buf_3 ; " se_buf_4 ; " se_buf_5 ; " se_buf_6 ; " se_buf_7 ; " se_buf_8 ; " se_buf_9 ; " se_buf_10 ; " se_buf_11 ; " se_buf_12 ; " se_buf_13 ; " se_buf_14 ; " se_buf_15 ; 16 Byte Serial EPROM buffer - MSByte ; >>>>>>>>>>>>>>> Last location in Ram Bank 1 GPR is 0xFF <<<<<<<<<<<<<<< ENDC ; ; **************************************************************************** ; Several banks of RAM are loaded with tables from EEPROM and Flash memory,* ; during program initialization, and become the working tables. Each bank * ; of ram has 256 locations and the tables are limited to that size. The * ; original tables are at the end of program listing. * ; * ; RAM Bank 2 (0x0200) will contain band table. BAND_BASE * ; RAM Bank 3 (0x0300) will contain decade table, cursor postioning table, * ; LNA table and S-Meter table. DECADE_BASE * ; RAM Bank 4 (0x0400) will contain low limit table. LOW_BASE * ; RAM Bank 5 (0x0500) will contain high limit table. HIGH_BASE * ; * ; **************************************************************************** ; ; Start of Flash Memory ; ; **************************************************************************** ; * The 18F2550 resets to 0x0000. * ; * * ; * The Interrupt vectors are at 0x0008 and 0x0018. * ; **************************************************************************** ; ORG 0x0000 ; Reset entry. reset_vector goto Start ; Jump around the vectors to ; initialization program high_vector ORG 0x0008 ; High Interrupt- No interrupt enabled retfie low_vector ORG 0x0018 ; Low interrupt - No interrupt enabled retfie ORG 0x0020 ; START OF PROGRAM ; ; ***************************************************************************** ; * * ; * Purpose: This is the start of the program. It initializes all of the * ; * hardware and program variables. * ; * Calibrate routine is called if required. Otherwise, it sets * ; * the power-on frequency and enters Main. * ; * * ; * Input: None. * ; * * ; * Output: Normal VFO operation. * ; * * ; ***************************************************************************** Start movlb D'1' ; Set BSR for Ram Bank 1 clrf RCON ; Disable priority interrupt clrf INTCON ; No interrupts, Port B weak pullup clrf INTCON2 ; " clrf INTCON3 ; " clrf RCSTA ; Disable USART Receiver clrf TXSTA ; Disable USART Transmitter clrf PIE1 ; Clear all interrupt enable bits clrf PIE2 ; " clrf PIR1 ; Clear all interrupt flag bits clrf PIR2 ; " clrf ADCON0 ; Disable A/D module clrf T1CON ; Disable Timer 1 movlw H'0F' ; RA0..4 are digital movwf ADCON1 ; " movlw H'07' ; Set all A/D ports to digital movwf CMCON ; Disable comparitors clrf CVRCON ; Disable comparitor reference clrf UCON ; Disable USB movlw B'00001000' ; Disable USB tranceiver movwf UCFG ; " clrf CCP1CON ; Disable CCP/PWM #1 clrf CCP2CON ; Disable CCP/PWM #2 clrf PORTA ; Clear Port A movlw PORTA_CONTROL ; Set Port A Control bits. movwf TRISA ; " clrf PORTB ; Clear Port B clrf TRISB ; Set port B to all outputs clrf PORTC ; Clear Port C movlw PORTC_CONTROL ; Set Port C Control bits movwf TRISC ; " ; Preset the External Interface Chip Select control. bsf PORTC,EICS ; Raise External Interface Chip Select ; Presest the Divide by 4 control to Quadrature Generator. bcf PORTC,DIV_BY4 ; Lower the divide by four control. ; Get restore power flags from EEPROM movlw restore_flag ; Point to restore_flag in EEPROM movwf EEADR ; Set address for access call Read_EEPROM ; Read the power up flags movff EEDATA,PWR_flags ; Move flags to working register bsf PWR_flags,POWER_UP ; Set power up flag bit ; Initialize the LCD and display startup banner. call Init_LCD ; Initialize the LCD call Display_version ; Display title and version number ; ; All access to the band table is from RAM, so move band table from ; EEPROM to Ram Bank 2. ; call Move_band_tbl ; Move the band table from EEPROM to Ram Bank 2 ; ; Move cursor positioning table, decade table, LNA boundry table and S-Meter table ; from flash memory to Ram Bank 3. ; movlw c_d_LNA_table_lgth ; Get length of table movwf count ; Save in counter lfsr 2,DECADE_BASE ; Point FSR2 at Ram Bank 3 movlw upper decade_tbl ; Init table pointer to point at decade table movwf TBLPTRU ; in flash - Upper movlw high decade_tbl movwf TBLPTRH ; High movlw low decade_tbl movwf TBLPTRL ; Low and load the TBLPTR call Move_flash2ram ; Go move the table to ram ; ; Move Band Low check table from flash memory to Ram Bank 4. ; movlw low_tbl_lgth movwf count lfsr 2,LOW_BASE movlw upper low_tbl movwf TBLPTRU movlw high low_tbl movwf TBLPTRH movlw low low_tbl movwf TBLPTRL call Move_flash2ram ; ; Move Band High check table from flash memory to Ram Bank 5 ; movlw high_tbl_lgth movwf count lfsr 2,HIGH_BASE movlw upper high_tbl movwf TBLPTRU movlw high high_tbl movwf TBLPTRH movlw low high_tbl movwf TBLPTRL call Move_flash2ram ; ; Initialize other controls. ; clrf FSR0H ; Clear upper part of File Select Register 0 clrf last_dir ; Clear the tuning encoder direction indicator clrf MISC_flags ; Clear the flags bsf MISC_flags,RTF ; Set R/T flag - start up in receive state clrf MENU_flags ; Clear encoder and menu flags clrf PB_wait_count ; Clear Menu switch wait counter clrf MODE_flags ; Clear mode controls and flags clrf DDS_flags ; Clear DDS control flags clrf dds_pdr_reg ; Clear the DDS Power-Down and Reset clrf SEEP_flags ; Clear the Serial EPROM flags movlw MENU_COUNTER ; Get menu encoder counter value movwf menu_enc_ctr ; Set value into menu encoder counter movwf menu_ctr_val ; and save for later use clrf ext_ctl_0 ; Clear external control bytes clrf ext_ctl_1 ; " bsf ext_ctl_0,5 ; Set R/T external control line for receive ; ; Get the power on Tuning encoder value. ; movff PORTA,en_read ; Read port A movlw ENCODER_BITS ; Get encoder mask (RA0 and RA1) andwf en_read,w ; Get encoder bits movwf en_old ; Save in en_old clrf enc_counter ; Clear encoder modulo counter ; Initialize RIT encoder values, clear RIT frequency and clear flag movff PORTC,rit_read ; Read port C movlw RIT_BITS ; Get encoder mask (RC4 and RC5) andwf rit_read,w,1 ; Save just encoder bits movwf rit_old,1 ; Save in rit_old clrf rit_counter,1 ; Clear the encoder modulo counter clrf rit_last,1 ; Clear last direction clrf rit_vfoa_0,1 ; Clear the RIT frequency bytes clrf rit_vfoa_1,1 ; Clear the RIT frequency bytes clrf rit_vfob_0,1 ; Clear the RIT frequency bytes clrf rit_vfob_1,1 ; Clear the RIT frequency bytes clrf rit_step_1,1 clrf RIT_flags ; Clear RIT control flags IFDEF BAR_GRAPH ; Analog to Digital Convertor (ADC) AN0 is used to display an input voltage ; as a bar graph on the LCD. IFNDEF BG_CENTER ; S-Meter initialization for ADC. ; There is a table that relates ADC count to bars that are displayed in the Bar ; Graph with an ADC reference voltage of +5 Volts. The object of the following ; computations is to take the defined maximum input voltage and develop a multiplier ; which is the ratio of 5 Volts to the defined maximum input voltage. This ; multiplier is used to multiply the ADC count to a 5 Volt reference value that is ; then used for the look up in the table. ; NOTE: The S-Meter table byte positions are reversed when moved from Flash memory to ; RAM. movlw BG_MAX_VOLT_UN ; Get defined maximum input voltage units movwf DDS_5 ; " clrf DDS_4 ; " clrf DDS_6 ; Set multiplier to 100 movlw D'100' ; " movwf DDS_7 ; " call Mpy16X16 ; Mutilply Defined_Maximum_Voltage units by 100 movlw BG_MAX_VOLT_DE ; Get the decimal part of defined maximum voltage addwf DDS_3,f ; Add the decimal part to unit*100 movlw D'0' addwfc DDS_2,f ; Add any carry addwfc DDS_1,f ; DDS_0..3 now contains the defined maximum input addwfc DDS_0,f ; voltage times 100 movff DDS_2,BCD_0 ; Save the Defined_Maximum_Voltage times 100 movff DDS_3,BCD_1 movff DDS_3,freq_1 ; Now move Defined_Maximum_Voltage*100 for divide movff DDS_2,freq_0 ; into 5 volts*100 clrf DDS_5 ; Set the numerator to 5*100 movlw H'01' ; " movwf DDS_6 ; " movlw H'F4' ; " movwf DDS_7 ; " call Div24_16 ; Divide 5*100 Volts/Defined _Maximum_Voltage*100 movff DDS_1,BCD_2 ; Save Remainder clrf DDS_4 ; Set multiplier of 100 movlw D'100' ; " movwf DDS_5 ; " call Mpy16X16 ; Calculate the S-Meter multiplier*100 movff DDS_2,smtr_mult_0 ; Save results movff DDS_3,smtr_mult_1 ; " movff BCD_2,DDS_5 ; Get the remainder from divide clrf DDS_4 movlw D'100' ; and multiply by 100 movwf DDS_7 clrf DDS_6 call Mpy16X16 ; movff DDS_2,DDS_6 ; Move remainder * 100 for divide movff DDS_3,DDS_7 ; " clrf DDS_5 ; " movff BCD_0,freq_0 ; Move Defined_Maximum_Voltage for divide movff BCD_1,freq_1 ; " call Div24_16 ; Divide remainder * 100 by Defined_Maximum_Voltage movf DDS_7,w ; Get divide result -> remainder times 100 addwf smtr_mult_1,1 ; and add to Defined_Maximum_Voltage units movf DDS_6,w ; smtr_mult_0.1 now contains the ADC count addwfc smtr_mult_0,1 ; multiplier times 100 ELSE ; Zero-Center Bar Graph Initializing. ; There are nine blocks in the Zero Center display. Determine counts per block. ; Counts Per Block = Max ADC Count/9 ; There are 5 bars per block. Determine ADC counts per bar. ; Counts per bar = Counts per Block/5. ; Determine ADC Count for Zero voltage. ; ADC Count at Zero = 204.8 Counts per Volt * Center Voltage movlw BG_MAX_VOLT_UN ; Get the maximum input voltage for ADC addlw BG_CTR_VOLT_UN ; Add the center voltage movwf DDS_5 ; Move for multiply by 100 clrf DDS_4 ; " movlw D'100' ; Get multiplier of 100 movwf DDS_7 ; and prime multiply clrf DDS_6 ; ' call Mpy16X16 ; Multiply whole units of Maximum Input Voltage ; By 100. movlw BG_MAX_VOLT_DC ; Get Deciaml units of Maximum Input Voltage addwf DDS_3,f ; Add decimal units to whole units clrf WREG addwfc DDS_2,f addwfc DDS_1,f addwfc DDS_0,f movlw BG_CTR_VOLT_DC ; Get Deciaml units of Center Input Voltage addwf DDS_3,f ; Add decimal units to whole units clrf WREG addwfc DDS_2,f addwfc DDS_1,f addwfc DDS_0,f ; DDS_0..DDS_3 have Max-Input-Voltage + ; Center-Voltage*100 movff DDS_2,DDS_4 ; Multiply Max-Input+Center-Voltage voltage*100 movff DDS_3,DDS_5 ; " movlw H'50' ; By 204.8 * 100. A fixed value determined by movwf DDS_6 ; ADC Vref and ADC Maximum count of 1024 clrf DDS_7 ; i.e. 5000 Hex call Mpy16X16 ; Multply 20480 * Max-Input_Voltage*100 ; giving Maximum-ADC-Count*10,000 movff DDS_1,DDS_5 ; Move multiply results for divide movff DDS_2,DDS_6 ; Will now divide Maximum ADC Count * 10,000 movff DDS_3,DDS_7 ; by 10,000 to get the Maximum ADC Count movlw H'27' ; Set fixed count of 10,000 into the movwf freq_0 ; denominator movlw H'10' ; " movwf freq_1 ; " call Div24_16 ; Do The divide. Obtain Maximum ADC Count movlw D'9' ; There are 9 blocks. Determine counts per block movwf freq_1 ; Set denominator to 9 clrf freq_0 ; " call Div24_16 ; Do the divide movlw D'5' ; Check the remainder for rounding subwf DDS_1,w ; Maximum count is 113 so only one hex bn $+4 ; digit needs to be incremented for incf DDS_7,f ; rounding movff DDS_7,bg_ctr_cnts_blk ; Save the ADC counts per block clrf DDS_6 clrf DDS_5 movlw D'5' ; There are 5 bars per block. movwf freq_1 ; so divide by 5 clrf freq_0 ; " call Div24_16 ; Do it movff DDS_7,bg_ctr_cnts_bar ; and save ADC counts per bar movlw BG_CTR_VOLT_UN ; Get center input voltage units for ADC movwf DDS_5 ; Move for multiply by 100 clrf DDS_4 ; " movlw D'100' ; Get multiplier of 100 movwf DDS_7 ; and prime multiply clrf DDS_6 ; ' call Mpy16X16 ; Multiply whole units of Center Input Voltage ; By 100. movlw BG_CTR_VOLT_DC ; Get Decimal units of Center Input Voltage addwf DDS_3,f ; Add Decimal units to whole units ; DDS_0..DDS_3 have Center-Input-Voltage*100 movff DDS_2,DDS_4 ; Multiply Center-Input-voltage*100 movff DDS_3,DDS_5 ; " movlw H'50' ; By 204.8 * 100. A fixed value determined by movwf DDS_6 ; ADC Vref and ADC Maximum count of 1024 clrf DDS_7 ; i.e. 5000 Hex call Mpy16X16 ; Multply 20480 * Center-Input_Voltage*100 ; giving Maximum-ADC-Count*10,000 movff DDS_1,DDS_5 ; Move multiply results for divide movff DDS_2,DDS_6 ; Will now divide Maximum ADC Count * 10,000 movff DDS_3,DDS_7 ; by 10,000 to get the Maximum ADC Count movlw H'27' ; Set fixed count of 10,000 into the movwf freq_0 ; denominator movlw H'10' ; " movwf freq_1 ; " call Div24_16 ; Do The divide. Obtain Center Voltage ADC Count movlw H'88' ; 1/2 0f 10,000 subwf DDS_1,w ; Subtract from remainder movlw H'13' subwfb DDS_0,w ; Remainder-5000, if negative do not round up bn bg_ctr_done ; Do not round up incf DDS_7,f ; Yes, round up by adding 1 bnz bg_ctr_done ; Did not roll over to zero, continue incf DDS_6,f ; Add one bnz bg_ctr_done ; Did not roll over to zero incf DDS_5,f ; Add one ; Center-Voltage-ADC-Count in DDS_5, DDS_6, DDS_7 bg_ctr_done movff DDS_6,bg_ctr_cnt_hi ; Save the center voltage ADC count movff DDS_7,bg_ctr_cnt_lo ; " ENDIF ; Compute the Divisor's for linear bar graph. The Divisor's are: ; ADC counts per LCD block (a character space entirely filled in) ; ADC counts per LCD bar (vertical bar or bars with each bar representing ; 1/5 of a character). ; ADC Vref+ is 5 volts and Vref- is 0 volts. Maximum count for ADC is 1024. ; Therefore, the counts per volt is 1024counts/5volts = 204.8 counts-per-volt. ; Maximum ADC Count for user supplied maximum voltage is ; 204.8 * Maximum-Input-Voltage. ; Formula for counts-per-block is Maximum ADC Count/ 10 ; Formula for counts-per bar is counts-per-block/5 bars. movlw BG_MAX_VOLT_UN ; Get the maximum input voltage for ADC movwf DDS_5 ; Move for multiply by 100 clrf DDS_4 ; " movlw D'100' ; Get multiplier of 100 movwf DDS_7 ; and prime multiply clrf DDS_6 ; ' call Mpy16X16 ; Multiply whole units of Maximum Input Voltage ; By 100. movlw BG_MAX_VOLT_DE ; Get Deciaml units of Maximum Input Voltage addwf DDS_3,f ; Add decimal units to whole units ; DDS_0..DDS_3 have Max-Input-Voltage*100 adc_init_0 movff DDS_2,DDS_4 ; Multiply Max-Input-voltage*100 movff DDS_3,DDS_5 ; " movlw H'50' ; By 204.8 * 100. A fixed value determined by movwf DDS_6 ; ADC Vref and ADC Maximum count of 1024 clrf DDS_7 ; i.e. 5000 Hex call Mpy16X16 ; Multply 20480 * Max-Input_Voltage*100 ; giving Maximum-ADC-Count*10,000 movff DDS_1,DDS_5 ; Move multiply results for divide movff DDS_2,DDS_6 ; Will now divide Maximum ADC Count * 10,000 movff DDS_3,DDS_7 ; by 10,000 to get the Maximum ADC Count movlw H'27' ; Set fixed count of 10,000 into the movwf freq_0 ; denominator movlw H'10' ; " movwf freq_1 ; " call Div24_16 ; Do The divide. Obtain Maximum ADC Count movlw H'88' ; 1/2 0f 10,000 subwf DDS_1,w ; Subtract from remainder movlw H'13' subwfb DDS_0,w ; Remainder-5000, if negative do not round up bn adc_init_1 ; Do not round up incf DDS_7,f ; Yes, round up by adding 1 bnz adc_init_1 ; Did not roll over to zero, continue incf DDS_6,f ; Add one bnz adc_init_1 ; Did not roll over to zero incf DDS_5,f ; Add one ; Maximum-ADC-Count in DDS_5, DDS_6, DDS_7 adc_init_1 clrf freq_0 ; There are 5 LCD blocks. Determine ADC counts movlw D'5' ; per block movwf freq_1 ; Set denominator to 5 call Div24_16 ; Do the divide movlw D'5' ; Check the remainder for rounding subwf DDS_1,w ; Maximum count is 102 so only one hex bn $+4 ; digit needs to be incremented for incf DDS_7,f ; rounding movff DDS_7,bg_cnts_block ; Save the ADC counts per block movff bg_cnts_block,DDS_7 ; Calculate ADC counts per bar clrf DDS_6 ; Move counts per block to numerator clrf DDS_5 movlw D'5' ; 5 bars per block movwf freq_0 ; to the denominator clrf freq_1 ; " call Div24_16 ; Divide counts-per-block by 5 movff DDS_7,bg_cnts_bar ; Save the counts-per-bar movff bg_cnts_block,DDS_7 ; Move counts per block for divide clrf DDS_6 ; " clrf DDS_5 ; " movlw D'10' ; Set divisor of 10 movwf freq_0 ; " clrf freq_1 ; " call Div24_16 ; Divide counts-per-block by 10 movff DDS_7,bg_1tenth_bar ; and save 10% of counts per block ENDIF ; Initialize the AD9912. Calculate Clock Ratio if needed. call DDS_init ; Initialize the AD9912 ; Enter Calibrate Mode if Frequency Menu Switch is pressed while turning the ; power on. ; btfss en_read,MENU_SW ; Menu switch/Cal pushbutton pressed? call Calibrate ; Yes, calibrate bcf MISC_flags,PCAL_FLG ; Clear the calibrate flag ; Determine if this is very first time through start up. If it is, use the default ; band and frequency. If not, use the restore information saved in EEPROM and SEPROM. ; btfss PWR_flags,NOT_1ST ; Have we been through startup before? bra get_band_freq ; No, load the default startup band and frequency ; Been through startup before, so restore from PROM. movlw restore_vfo ; Get pointer to saved vfo_select movwf seprom_a_0,1 ; Set the address for SEPROM read clrf seprom_a_1,1 clrf seprom_a_2,1 movlw D'1' ; Count of 1 byte movwf se_count ; and set counter for read lfsr 1,vfo_select ; Point at data receive location call Se_read ; Get saved vfo_select incf seprom_a_0,f,1 ; Set address for vfo_a_band access movlw D'1' ; Count of 1 byte movwf se_count ; and set counter for read lfsr 1,vfo_a_band ; Point at store location call Se_read ; Get vfo_a_band incf seprom_a_0,f,1 ; Next SEPROM location movlw D'1' ; Count of 1 byte movwf se_count ; and set counter for read lfsr 1,vfo_b_band ; Point at store location call Se_read ; Get vfo_b_band lfsr 2,BAND_BASE ; Point at Ram band table movf vfo_a_band,w ; Get VFO-A band number mullw BAND_LGTH ; Calculate band index movf PRODL,w ; Get band index movff PRODL,band_index ; Save in band index in case we start in VFO-A addwf FSR2L,f ; Add to Ram band table pointer movlw FREQ_CT+CTL_CT ; Four bytes frequency & controls movwf count ; Set move counter lfsr 0,vfo_a_0 ; VFO-A destination registers vfoa_rest_loop movff POSTINC2,POSTINC0 ; Move byte to VFO-A registers decfsz count,f ; Decrement count and loop if not zero bra vfoa_rest_loop lfsr 2,BAND_BASE ; Point at Ram band table movf vfo_b_band,w ; Get VFO-B band number mullw BAND_LGTH ; Calculate band index movf PRODL,w ; Get band index addwf FSR2L,f ; Add to Ram band table pointer movlw FREQ_CT+CTL_CT ; Four bytes frquency & controls addwf FSR2L,f ; Add to FSR2 to jump over VFO-A in table movwf count ; Set move counter lfsr 0,vfo_b_0 ; VFO-B destination registers vfob_rest_loop movff POSTINC2,POSTINC0 ; Move byte to VFO-B registers decfsz count,f ; Decrement count and loop if not zero bra vfob_rest_loop ; Start in which VFO? btfss vfo_select,LSB ; Activate VFO-B? bra start_vfo_a ; No, activate VFO-A movlw vfo_b_0 ; Set vfo pointer to VFO-B movwf vfo_pointer ; " movf vfo_b_band,w ; Get VFO-B band number mullw BAND_LGTH ; Multiply band number by band entry length movff PRODL,band_index ; Move band index to working location movff decade_b,decade ; Set the working Entry Marker for VFO-B movff mode_b,MODE_flags ; Move VFO-B mode to working mode bra start_vfo start_vfo_a movlw vfo_a_0 ; Set vfo pointer to VFO-A movwf vfo_pointer ; " movff decade_a,decade ; Set the working decade for VFO-A movff mode_a,MODE_flags ; Set VFO-A working mode start_vfo call Set_limit_regs ; Set the high and low limits regs and external controls ; Retrieve CW Offset from SEPROM movlw saved_cwoffset ; Get the CW Offset in SEPROM. Set address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'2' ; # of bytes movwf se_count ; Into counter lfsr 1,cwoffs_pos_0 ; Point at data receive location call Se_read ; Read in the offset from SEPROM movff cwoffs_pos_0,cwoffs_neg_0 ; Copy positive offsets to negative offsets movff cwoffs_pos_1,cwoffs_neg_1 ; " comf cwoffs_neg_0,f,1 ; Need to negate the offset working values comf cwoffs_neg_1,f,1 ; " incfsz cwoffs_neg_0,f,1 ; Add one to complete 2's complement bra retr_rit_step ; Finished if no carry incfsz cwoffs_neg_1,f,1 ; Propogate carry - ; Retrieve the RIT Step Size from SEPROM retr_rit_step movlw saved_ritstep ; Location of RIT Step Size in SEPROM. Set read address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'1' ; # of bytes movwf se_count ; Into counter lfsr 1,rit_step_0 ; Point at data receive location call Se_read ; Read the step size from SEPROM bra init_continue ; Continue with startup get_band_freq ; First time through startup after firmware load. ; ; Initialize the offsets used for CW. cw_offset_1 equ (CW_FREQ/H'100')&H'FF' cw_offset_0 equ CW_FREQ&H'FF' movlw cw_offset_1 ; Get CW tone frequency high byte movwf cwoffs_pos_1,1 ; Initialize CW offset_1 movlw cw_offset_0 ; Get CW tone frequency low byte movwf cwoffs_pos_0,1 ; Initialize CW offset_0 movff cwoffs_pos_0,cwoffs_neg_0 ; Copy positive offsets movff cwoffs_pos_1,cwoffs_neg_1 ; " comf cwoffs_neg_0,f,1 ; Need to negate the offset working values comf cwoffs_neg_1,f,1 ; " incfsz cwoffs_neg_0,f,1 ; Add one to complete 2's complement bra cwoffset_done ; Finished if no carry incfsz cwoffs_neg_1,f,1 ; Propogate - cwoffset_done movlw saved_cwoffset ; Save the offset in SEPROM. Set write address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'2' ; # of bytes movwf se_count ; Into counter lfsr 1,cwoffs_pos_0 ; Point at data to save call Se_write ; Write the new offset to SEPROM ; Initialize RIT Step Size. movlw RIT_CHG ; Init the default RIT movwf rit_step_0,1 ; step size movlw saved_ritstep ; Save the RIT Step Size in SEPROM. Set write address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'1' ; # of bytes movwf se_count ; Into counter lfsr 1,rit_step_0 ; Point at data to save call Se_write ; Write the step size to SEPROM ; Startup with Band # 1 and set both VFO's for that band with the programmed ; startup frequency. Also set default Signal Mode mode for startup. ; Select VFO A at startup, establish VFO pointer and load registers. clrf vfo_select ; Start up with VFO A movlw vfo_a_0 ; Get address of VFO A registers movwf vfo_pointer ; save in pointer register movlw D'1' ; Start in band 1. movwf vfo_a_band ; Set band number in VFO-A call Get_band ; Establish the band, frequency, decade, ; set limits and control select call Copy_a_to_b ; Copy VFO-A registers to VFO-B registers ; Initialise SEPROM restore information. Initial values of VFO Select, ; VFO-A band number and VFO-B band number are saved to SEPROM. movlw restore_vfo ; Pointer to start of SEPROM save movwf seprom_a_0,1 ; Set SEPROM write address clrf seprom_a_1,1 clrf seprom_a_2,1 movlw D'3' ; Write 3 bytes movwf se_count movff vfo_select,se_buf_0 ; Move vfo_select to SEPROM buffer movff vfo_a_band,se_buf_1 ; Move vfo_a_band to SEPROM buffer movff vfo_b_band,se_buf_2 ; Move vfo_b_band to SEPROM buffer lfsr 1,se_buf_0 ; FSR1 points at SEPROM buffer call Se_write ; Save the data ; Continue here after loading VFO's with required frequency. init_continue btfsc PWR_flags,NOT_1ST ; Have we been through startup before? bra mem_clear_no ; Yes, continue ; ; The default startup band and frequency was used for startup. Now set flag ; so that subsequent start up uses saved band and frequency. bsf PWR_flags,NOT_1ST ; Set first-time flag bit in startup flags ; See if operator wants to clear Save/Restore (S/R) Memory on initial power up. call Clear_lcd ; No, clear the display movlw strt_msg1 ; Point at Memory Clear message call Display_message1 ; and display it mem_clear_loop movlw CMD_2ND_LINE ; Move to LCD second line call Cmnd2LCD movlw erase_msg ; Point at instruction message call Display_message1 ; and display it btfss PORTA,MENU_SW ; Menu switch pressed? bra mem_clear_no ; Yes, operator does not want to clear btfsc PORTA,VFO_SW ; No, check if VFO switch was pushed bra mem_clear_loop ; No, go through it again clrf sewrite_a_0,1 ; Yes, operator wants clear, reset write movlw SE_MEM_STRT ; address to start of write area movwf sewrite_a_1,1 ; " clrf sewrite_a_2,1 ; " bcf MISC_flags,SEEP_EN ; Clear the S/R entry flag clrf SEEP_flags ; Clear all S/R flags movlw D'1' ; Count of 1 movwf se_count ; To Serial EPROM read/write byte counter lfsr 1,SEEP_flags ; Point at data source - zero data movlw se_wriadr_save+D'3' ; SEPROM location of MISC flags (SEEP_EN) movwf seprom_a_0,1 ; Set SEPROM Write Address for MISC flags clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " call Se_write ; Zero out the SEEP_EN flag btfss PORTA,VFO_SW ; VFO Switch still pushed bra $-2 ; Yes, go back bra start_mem_init ; mem_clear_no ; Check the Serial EPROM for entries. Read in the SEPROM saved write address and flags. movlw D'4' ; Count of 4 movwf se_count ; To Serial EEPROM read/write byte counter lfsr 1,se_buf_0 ; Point at receiving location movlw se_wriadr_save ; SEPROM location of SEPROM saved write address movwf seprom_a_0,1 ; Read in the saved Write Address and flags clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " call Se_read ; Read in saved SEEPROM write address and flag ; If SEEP_EN flag set copy write address from read buffer to working write address btfss se_buf_3,SEEP_EN,1 ; Have entries been saved in SEPROM? bra start_mem_init ; No so just init the SEPROM write address movff se_buf_0,sewrite_a_0 ;Move write address from buffer to working location movff se_buf_1,sewrite_a_1 ;Move write address from buffer to working location movff se_buf_2,sewrite_a_2 ;Move write address from buffer to working location bsf MISC_flags,SEEP_EN ; Set flag that entries are in Serial Memory bra startup_set ; Continue with startup ; No entries in SEPROM. start_mem_init movlw SE_MEM_STRT ; Set starting SEPROM Memory write address movwf sewrite_a_1,1 ; or 0x00100 clrf sewrite_a_0,1 ; " clrf sewrite_a_2,1 ; " bcf MISC_flags,SEEP_EN ; Make sure entries present flag is reset startup_set ; Save the Power flags in EEPROM bcf PWR_flags,POWER_UP ; Reset power up flag movff PWR_flags,EEDATA ; Move data to be written to EEPROM movlw restore_flag ; EEPROM address of restore flags movwf EEADR ; Prime the EEPROM address call Write_EEPROM ; Write flags to EEPROM ; All set to go. call External_ctl_rt ; Set the Receive/Transmit controls call Clear_lcd ; Clear the display call Disp_vfo ; Display VFO call Disp_mode ; Display operating mode call Rit_display ; Display RIT frequency call Disp_funit ; Display frequency unit movlw D'0' ; Prepare W for decade update (don't move) call Update_decade ; Init fstep3..0, cur_pos and display frequency call Send_dds_freq ; Send the power-on frequency (FTW) to the ; DDS in serial mode ; LCD is displaying start up frequency and the VFO is generating that ; frequency. Now set up Timer0 to generate an overflow every 0.1 seconds. ; After number overflows, determined by count_of_ovfl, the registers of ; each VFO, is saved in EEPROM. The saved data will be used on next power up. movlw count_of_ovfl ; Get number of interrupts before saving movwf T0_ofl_ctr ; and initialize the interrupt counter movlw TIMER0_HI ; Count for timer 0 movwf TMR0H ; High clrf TMR0L ; Reload Timer 0 Low and High movlw T0CON_INIT ; Start Timer 0 with prescale = 128 movwf T0CON ; Timer 0 control IFDEF BAR_GRAPH ; Analog to Digital Convertor (ADC) AN0 is used to display an input voltage ; as a bar graph on the LCD. Input of the voltage is through a 330 Ohm resistor ; to PIC 18F2550 pin 2. ; Here the ADC, AN0, is started. Timer 0 processing routine will restart a new ; ADC aquisition and conversion. "Main" will check for the ADC Aquisition and ; Conversion to be complete and then transfer to routine to handle ADC display. movlw B'10111110' ; Conversion clock = Fosc/64, Acquistion movwf ADCON2 ; time = 20 TAD, right justify result movlw B'00001110' ; Enable AN0, Vdd and Vss are reference movwf ADCON1 ; " movlw B'00000001' ; Turn on ADC, Use module AN0 movwf ADCON0 ; " movlw ADC_COUNT ; Get Timer 0 sampling rate movwf adc_counter ; and put into counter bsf ADCON0,GO ; Set the Go/Done bit to start ADC bsf MISC_flags,AGD ; Set the ADC control flag ENDIF ; ; Done with initialization and startup. ; Enter the Main Program Loop ; ; ***************************************************************************** ; * * ; * Purpose: This is the Main Program Loop. * ; * The operator has five input controls for the program. * ; * 1. Frequency Rotary encoder * ; * 2. RIT Encoder * ; * 3. Menu Push Switch * ; * 4. VFO Push Switch * ; * 5. RIT Encoder Push Switch * ; * There is also Receive/Transmit Control, Timer 0 input and ADC * ; * input. * ; * * ; * Main successively checks Tune_encoder for Frequency Encoder * ; * rotation, RIT encoder for encoder rotation, Menu switch, VFO * ; * switch, Receive/Transmit input, Timer 0 overflow and ADC * ; * processing. * ; * Decisions are made and routines executed depending on the * ; * status of the inputs. * ; * * ; * Input: None. * ; * * ; * Output: None. * ; * * ; ***************************************************************************** ; Main call Tune_encoder ; Check for encoder movement btfss MISC_flags,ENC_MOV ; Is encoder moving? bra main_rit ; No btfsc MENU_flags,MENU ; Encoder moving, is menu flag set? goto MENU_process ; Yes, menu and moving encoder main_rit ; No call Rit_encoder ; Check the Receive Incremental Tuning btfss PORTA,MENU_SW ; Is Menu switch pressed goto Menu_sw ; Yes, process the Menu switch btfss PORTA,VFO_SW ; Is VFO switch pressed? goto Vfo_switch ; Yes, handle vfo or menu terminate call Rt_control ; Check Receive/Transmit and Split call Get_LNA ; Check for proper LNA selection btfsc INTCON,TMR0IF ; Did Timer 0 overflow (about 0.1 second) goto Proc_timer0 ; Yes, handle Timer 0 overflow IFDEF BAR_GRAPH btfss ADCON0,GO ; Has ADC finished? GO bit reset goto ADC_proc ; Yes, go process bar graph ENDIF bra Main ; Loop back to start of Main ;***************************************************************************** ; ; Purpose: This routine does the following ; 1. Reads the encoder bits until a change is detected, then ; determines the direction the knob was moved. ; 2. Performs mechanical shaft encoder debounce and ; detent processing if so enabled. ; 3. Sets flag ENC_MOV if encoder is moving. ; 4. MENU not active, change frequency, check limits, update ; display and send new FTW to the AD9912. ; 5. MENU active, return to Main. ; 6. MENU Switch pressed, return to Main for Entry Marker movement. ; ; Input: Knob input read from port A ; en_old -> the last encoder bits read ; last_dir -> the last direction moved ; ; Output: en_read: The contents of PORTA when the encoder was moved ; en_new : The current encoder bits ; last_dir: The last direction (0 = down, 2 = up [AKA UP_BIT]) ; ;***************************************************************************** Tune_encoder movf PORTA,W ; Get Port A contents andlw ENCODER_BITS ; Save just the encoder bits movwf en_read ; Save value IFDEF DETENT_ENCODER call Wait_.5ms ; Debounce time movf PORTA,W ; Read the port again andlw ENCODER_BITS ; Just encoder bits cpfseq en_read ; and compare with the previous value bra Tune_encoder ; Not the same, check again ENDIF movwf en_new ; Save new encoder value xorwf en_old,w ; Has it changed? bnz $+4 ; Encoder changed, continue return ; No change, go back to Main ; Else, Zero-flag is not set, so continue on ; Encoder changed. Now determine which direction the encoder turned. ;=============================================================================P ; Encoder bits are on RA1 and RA2. P ; A and B are "gray code" - 90 degrees out of phase (quadrature) P ; ___ ___ P ; | | | | P ; A ____| |___| |___ P ; ___ ___ P ; | | | | P ; B ___| |___| |___ P ; ^ ^ ^ ^ ^ ^ ^ ^ P ; a b c d a b c d P ; P ; A B P ; At point a: 0 0 P ; At point b: 1 0 P ; At point c: 1 1 P ; At point d: 0 1 P ; P ; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is: P ; 00, 10, 11, 01, 00, 10, 11, 01, etc. P ; P ; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is: P ; 01, 11, 10, 00, 01, 11, 10, 00, etc. P ; P ; To determine if the sequence is UP or DOWN: P ; 1) Take the "Right-Bit" of any pair. P ; 2) XOR it with the "Left-Bit" of the next pair in the sequence. P ; 3) If the result is 1 it is UP P ; If the result is 0 it is DOWN P ; P ; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning P ;=============================================================================P bcf STATUS,C ; Clear carry indicator rlncf en_old,f ; Rotate old bits left to align "Right-Bit" movf en_new,w ; Set up new bits in W xorwf en_old,f ; XOR old (left shifted) with new bits movf en_old,w ; Put XOR results into W also andlw UP_BIT ; 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. ; bz te_continue ; Zero flag set? (i.e, direction is same) movff next_dir,last_dir ; No Zero-flag, so direction changed ; update the direction indicator movff en_new,en_old ; Save the current encoder bits for next time bra Tune_encoder ; Try again te_continue clrf last_dir ; Clear last_dir (default is DN) btfss en_old,DIR_BIT ; Are we going UP? bra $+6 ; No down, go process it. movlw UP_BIT ; Get UP value movwf last_dir ; and set in last_dir movff en_new,en_old ; Get the current encoder bits and save for next time IFDEF DETENT_ENCODER decf enc_counter,f ; decrement the inter-detent counter btfsc enc_counter,0 ; Check if bit 0 is cleared bra Tune_encoder ; If not, then poll some more btfsc enc_counter,1 ; Else, check if bit 1 is cleared bra Tune_encoder ; If not, then poll some more ENDIF ; Arrive here when encoder is being turned bsf MISC_flags,ENC_MOV ; Set flag so that world can see encoder is moving btfss PORTA,MENU_SW ; Is Menu switch depressed? return ; Yes, operator wants to move Entry Marker btfsc MENU_flags,MENU ; Is Menu invoked? return ; Yes, go back to Main btfsc MISC_flags,PCAL_FLG ; Is this Tune Encoder check for Calibrate return ; Yes, return to Calibrate routine ; No, fall through to Step ;****************************************************************************** ; ; Purpose: Adjusts the frequency by examining the last_dir value and either ; adds or subtracts the value of fstep_0..3 from the current ; frequency setting. Checks that frequency is within the limits. ; Calls to display new frequency and send new FTW to the DDS. ; ; Input: Encoder direction flag. Frequency in freq_0..3. Increment/decrement ; value in fstep_0..3. ; ;******************************************************************************* Step ; Based on the knob direction, either add or ; subtract the increment, then update the ; LCD and DDS. movff vfo_pointer,FSR0L ; Get the pointer to vfo_X_0 into FSR0 btfsc last_dir,DIR_BIT ; Is the knob going up (clockwise)? bra Step_up ; Yes, then add the increment ; SUBTRCT fstep_0..3 from vfo_X_0..3 movf fstep_0,w ; Get fstep_0 subwf POSTINC0,f ; Subtract from vfo_X_0 movf fstep_1,w ; Get fstep_1 subwfb POSTINC0,f ; Subtract from vfo_X_1 movf fstep_2,w ; Get fstep_2 subwfb POSTINC0,f ; Subtract from vfo_X_2 movf fstep_3,w ; Get fstep_3 subwfb INDF0,f ; Subtract from vfo_X_3 ; ***************************************************************************** ; ; Purpose: Verify that the frequency is not below the band low limit. ; ; ***************************************************************************** Chk_low_limit btfsc INDF0,MSB ; Is frequency MSByte negative? bra set_low_limit ; Yes, set the low limit movf band_low_3,w ; No, get MSB of low limit subwf POSTDEC0 ,w ; vfo_X_3 - band_low_3 bz chk_low_2 ; They are equal, check next LSByte bc update ; vfo_X_3 > band_low_3 bra set_low_limit ; vfo_X_3 < band_low_3 ; ; Check the second most significant byte. ; chk_low_2 movf band_low_2,w ; Get next byte of low limit subwf POSTDEC0,w ; vfo_X_2 - band_low_2 bz chk_low_1 ; They are equal, check next LSByte bc update ; vfo_X_2 > band_low_2 bra set_low_limit ; vfo_X_2 < band_low_2 ; ; Check the third most significant byte. ; chk_low_1 movf band_low_1,w ; Get next byte of low limit subwf POSTDEC0,w ; vfo_X_1 - band_low_1 bz chk_low_0 ; They are equal, check next LSByte bc update ; vfo_X_1 > band_low_1 bra set_low_limit ; vfo_X_1 < band_low_1 ; ; Check LSByte ; chk_low_0 movf band_low_0,w ; Get last byte of low limit subwf POSTDEC0,w ; vfo_X_0 - band_low_0 bz update ; They are equal, exit bc update ; vfo_X_0 > band_low_0 set_low_limit movff vfo_pointer,FSR0L ; Get address of vfo_X_0 and put into FSR0 movff band_low_0,POSTINC0 ; band_low_0 -> vfo_X_0 movff band_low_1,POSTINC0 ; band_low_1 -> vfo_X_1 movff band_low_2,POSTINC0 ; band_low_2 -> vfo_X_2 movff band_low_3,INDF0 ; band_low_3 -> vfo_X_3 bra update ; Update LCD and DDS Step_up ; ADD fstep_0..3 to vfo_X_0..3 movf fstep_0,w ; Get fstep_0 addwf POSTINC0,f ; Add to vfo_X_0 movf fstep_1,w ; Get fstep_1 addwfc POSTINC0,f ; Add to vfo_X_1 movf fstep_2,w ; Get fstep_2 addwfc POSTINC0,f ; Add to vfo_X_2 movf fstep_3,w ; Get fstep_3 addwfc INDF0,f ; Add to vfo_X_3 ; ***************************************************************************** ; ; Purpose: Check if frequency exceeds the band upper limit. ; ; ***************************************************************************** Check_hi_limit ; ; Check the most significant byte. ; movf POSTDEC0,w ; Get vfo_X_3 subwf band_high_3,w ; Subtract band_high_3 - vfo_X_3 bz chk_limit2 ; vfo_X_3 = band_high_3 bc update ; vfo_X_3 < band_high_3 bn set_max ; vfo_X_3 > band_high_3 ; ; Check the second most significant byte when MSB equals band_high_3 ; chk_limit2 movf POSTDEC0,w ; Get vfo_X_2 subwf band_high_2,w ; Subtract band_high_2 - vfo_X_2 bz chk_limit1 ; vfo_X_2 = band_high_2 bc update ; vfo_X_2 < band_high_2 bra set_max ; vfo_X_2 > band_high_2 ; ; Check the third most significant byte. ; chk_limit1 movf POSTDEC0,w ; Get vfo_X_1 subwf band_high_1,w ; Subtract band_high_1 - vfo_X_1 bz chk_limit0 ; vfo_X_1 = band_high_1 bc update ; vfo_X_1 < band_high_1 bra set_max ; vfo_X_1 > band_high_1 ; ; Check the least significant byte. ; chk_limit0 movf INDF0,w ; Get vfo_X_0 subwf band_high_0,w ; Subtract band_high_0 - vfo_X_0 bz update ; vfo_X_0 = band_high_0 bc update ; vfo_X_0 < band_high_0 set_max ; Over limit, reset frequency to the high limit movff vfo_pointer,FSR0L ; Get active vfo pointer into FSR0 movff band_high_0,POSTINC0 ; High limit, Store in vfo_X_0 movff band_high_1,POSTINC0 ; Store in vfo_X_1 movff band_high_2,POSTINC0 ; Store in vfo_X_2 movff band_high_3,INDF0 ; Store in vfo_X_3 update call Disp_freq ; Display the new frequency on the LCD call Send_dds_freq ; Send the new FTW to the DDS chip bcf MISC_flags,ENC_MOV ; Clear encoder movement flag movlw D'4' ; Frequency update completed. Adjust stack return subwf TOSL,f ; pointer to return to "Main" - "call Tune_encoder". movlw D'0' ; This provides the highest priority for the subwfb TOSH,f ; Tuning Operation in case Operator is still subwfb TOSU,f ; tuning. return ; Back to Main ;**************************************************************************** ; ; Purpose: Menu switch is pressed. If VFO switch also pressed, ; go to RIT Control. If Frequency Encoder moving, move entry marker. ; If no encoder movement, time for 1 second then enter menu. If in ; menu, pressing Menu switch will advance menu or ; select the menu item. ; ; Inputs: Menu switch, Frequency Encoder ; ; Outputs: Menu is invoked, advanced to next selection or selected. ; Entry marker moved. ; ;**************************************************************************** Menu_sw ; Arrive here to process Menu switch btfss MISC_flags,RTF ; In receive mode? goto Main ; No, in transmit mode, ignore Menu switch movlw PB_RPT_WAIT ; Setup the delay timer for 1.0 seconds movwf PB_wait_count ; Menu_sw_release_wait btfss PORTA,VFO_SW ; Is the VFO Switch also pushed? goto Rit_control ; Yes, goto RIT enable/disable call Wait_25ms ; Wait a bit before checking again btfsc PORTA,MENU_SW ; Is Menu still pushed ? bra Menu_sw_release ; If it's released, check menu call Tune_encoder ; Check encoder btfsc MISC_flags,ENC_MOV ; Is it moving? goto Move_em ; Yes, move the entry marker ; Else, test if delay has expired decfsz PB_wait_count,f ; Decrement wait period bra Menu_sw_release_wait ; Keep looping until something happens ; 2.0 second delay has expired, display menu clrf PB_wait_count bsf MENU_flags,MENU ; Set flag that menu is in use incf MENU_flags,f ; Set for first menu selction (01) call Clear_lcd ; Clear display movlw CMD_MOV_CUR_DISP+D'6' ; Move entry marker - 1st line,char 7 call Cmnd2LCD ; Send to LCD movlw menu_msg ; Point to menu message call Display_message ; Display on LCD menu_wait call Wait_500ms ; and wait 1/2 second btfss PORTA,MENU_SW ; Is Menu switch still held? bra menu_wait ; Yes, wait for operator to release goto Menu_next Menu_sw_release btfss MENU_flags,MENU ; Is menu flag set? goto Main ; Nothing to do ; Menu is being displayed and the encoder ; switch was pressed. bcf MISC_flags,ENC_MOV ; Clear encoder movement flag movff menu_ctr_val,menu_enc_ctr ; Reset the menu encoder counter btfsc MENU_flags,SUB_MENU ; Are menu items displayed? bra Menu_sw_sub ; Yes, operator has made a selection, exit menu incf MENU_flags,f ; No, increment to next menu selection bcf MENU_flags,SUB_MENU ; Reset sub menu flag bra Menu_next ; Go to next menu selection Menu_sw_sub ; Menu selection was made movff MENU_flags,temp ; Move flags to work location movlw H'0F' ; Load mask andwf temp,f ; Save just the menu code movlw D'1' ; Menu code for band change cpfseq temp ; Is the code band change? bra Menu_sw_retr_chk ; No, check for memory retrieve btfsc vfo_select,LSB ; VFO-A or VFO-B? bra Menu_sw_vfob_act ; VFO-B is active movff temp1,vfo_a_band ; VFO-A is active, move new band number bra Menu_sw_get_band ; Continue Menu_sw_vfob_act movff temp1,vfo_b_band ; VFO-B is active, move new band number Menu_sw_get_band call Get_band ; Read the new band frequency from RAM bra Menu_sw_exit Menu_sw_retr_chk movlw D'3' ; Menu code for memory retrieve cpfseq temp ; Is the code memory retrieve? bra Menu_sw_erase_chk ; No, check for memory erase btfss SEEP_flags,RA ; Flag set saying Memory retrieve processing needed? bra Menu_sw_erase_chk ; No call Chg_mem_retr_proc ; Yes, go finish retrieve bra Menu_sw_exit ; Now exit Menu_sw_erase_chk movlw D'4' ; Menu code for memory erase cpfseq temp ; Is the code memory erase? bra Menu_sw_exit ; No, go exit btfss SEEP_flags,EA ; Flag set saying Erase processing needed? bra Menu_sw_exit ; No call Chg_mem_erase_proc ; Yes, go do the work Menu_sw_exit clrf SEEP_flags ; Clear Serial EEPROM (memory) flags clrf MENU_flags ; Get out of menu call Clear_lcd ; Clear the display call Disp_vfo ; Display VFO in use call Disp_mode ; Display mode in use call Rit_display ; Display RIT frequency call Disp_funit ; Display frequency unit ; Now display the frequency movlw D'0' ; Do not change decade call Update_decade ; Update the decade and display frequency call Send_dds_freq ; Send the frequency to the DDS chip goto Main ; ;**************************************************************************** ; ; Purpose: This function moves the entry marker depending on direction ; of Frequency Encoder movement. ; ; Inputs: Menu switch, Frequency Encoder direction ; ; Outputs: Entry marker is moved left or right with each encoder movement, ; fstep_3..0 updated with correct values. ; ;**************************************************************************** Move_em ; Encoder moving and Menu switch is pressed btfsc PORTA,MENU_SW ; Check again, is Menu switch pressed? goto Main ; No, do nothing decfsz menu_enc_ctr,f ; Menu counter zero? bra Em_finish_exit ; No, wait for more encoder movement btfss last_dir,DIR_BIT ; Is encoder moving clockwise (or UP)? bra Em_left ; No, movlw H'01' ; Yes, move entry marker to the right, add 1 bra Em_finish Em_left movlw H'FF' ; Move entry marker to the left, subtract 1 Em_finish call Update_decade ; Update decade and frequency display movff menu_ctr_val,menu_enc_ctr ; Reset value into menu encoder counter Em_finish_exit bcf MISC_flags,ENC_MOV ; Clear encoder movement flag goto Main ; Back to Main ;**************************************************************************** ; ; Purpose: Determines menu selection to display. ; ; Input: MENU_flags ; ; Output: The menu selection is displayed ; ;**************************************************************************** Menu_next bcf MISC_flags,ENC_MOV ; Clear encoder movement flag movff menu_ctr_val,menu_enc_ctr ; Set value into menu encoder counter movff MENU_flags,temp ; Move menu control to temp movlw LO_NYBBLE_MASK ; Mask for lower four bits andwf temp,f ; Save just the menu indicator number movlw D'1' ; Band select menu value cpfsgt temp ; Is the menu band selection (01) bra Change_band ; Display select band movlw D'2' ; Mode select value cpfsgt temp ; bra Change_mode ; Do mode select movlw D'3' ; Value for Memory retrieve operation cpfsgt temp bra Change_mem_retr movlw D'4' ; Value for Memory save operation cpfsgt temp bra Change_mem_save movlw D'5' ; Value for Memory erase operation cpfsgt temp bra Change_mem_erase movlw D'6' ; Split mode cpfsgt temp bra Change_split ; Do Split change movlw D'7' ; CW Offset cpfsgt temp bra Change_cwoff ; CW offset adjust menu movlw D'8' ; SEPROM Chip erase? cpfsgt temp bra Change_chip_erase ; Yes, go process goto Menu_sw_exit ; End of menu, exit menu ; ********************************** Change_band ; Select new band call Clear_lcd ; Clear display movlw CMD_MOV_CUR_DISP+D'2' ; Move cursor call Cmnd2LCD movlw band_msg ; Get offset to change band message call Display_message ; Display the message movlw CMD_2ND_LINE ; Move cusor to 2nd line of display call Cmnd2LCD ; Move Active band number to temp1. btfsc vfo_select,LSB ; Which vfo is active? bra Change_band_vfob ; VFO-B is active movff vfo_a_band,temp1 ; Save VFO-A band number in temp1 bra Change_band_cont Change_band_vfob movff vfo_b_band,temp1 ; VFO-B so save the band number Change_band_cont bsf MENU_flags,MENU_DISP ; Tell Disp_freq this is for menu call Disp_freq_menu ; Show present frequency bcf MENU_flags,MENU_DISP ; Remove the flag bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_mode call Clear_lcd ; Clear the display movlw CMD_MOV_CUR_DISP+D'2' ; Move the cursor call Cmnd2LCD ; Do it movlw mode_msg ; Get offset to mode select message call Display_message ; Display it movlw CMD_2ND_LINE+D'6' ; Move to second line call Cmnd2LCD ; Do it movlw AM ; AM mode bits cpfseq MODE_flags ; Is mode AM? bra mode_ssbu ; No movlw am_msg ; Get offset to AM mode message bra mode_exit mode_ssbu movlw SSBU ; Upper SSB mode bits cpfseq MODE_flags ; Is mode Upper SSB? bra mode_ssbl ; No movlw sbu_msg ; Get offset to SSB mode message bra mode_exit mode_ssbl movlw SSBL ; Lower SSB mode bits cpfseq MODE_flags ; Is mode Lower SSB? bra mode_cwplus ; No movlw sbl_msg ; Get offset to SSB mode message bra mode_exit mode_cwplus movlw CW_POS cpfseq MODE_flags ; Is mode CW Plus? bra mode_cwneg ; No, Check next movlw cwplus_msg ; Point at CW Plus Message bra mode_exit mode_cwneg movlw CW_NEG cpfseq MODE_flags ; Is mode CW Minus? bra mode_dsb ; No, Check next movlw cwmin_msg ; Point at CW Minus Message bra mode_exit mode_dsb movlw DS cpfseq MODE_flags ; Is mode Double Sideband? bra mode_phase ; No, Check next movlw dsbnd_msg ; Point at Double Sideband message bra mode_exit mode_phase ; Must be Phase signal mode movlw phase_msg ; Point at Phase Message mode_exit call Display_message ; Display correct message bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_mem_save call Clear_lcd ; Clear the display movlw mem_msg ; Point at "Memory" messages call Display_message1 ; Display message movlw mem_save ; Point at "Save" message call Display_message1 ; Display message movlw CMD_2ND_LINE ; Move cursor to beginning of second line. call Cmnd2LCD btfss sewrite_a_2,0,1 ; Is memory full - 4080 entries of 16 bytes ? bra Change_mem_save_ok ; No movlw full_buffer ; Yes, point at Memory Full message call Display_message1 ; and display it call Wait_a_sec ; Wait call Wait_a_sec ; wait goto Menu_sw_exit ; Get out Change_mem_save_ok movff sewrite_a_0,seprom_a_0 ; Move SEPROM write address to access address movff sewrite_a_1,seprom_a_1 ; " movff sewrite_a_2,seprom_a_2 ; " bsf MENU_flags,MENU_DISP ; Tell Disp_freq this is for menu call Disp_freq_menu ; Show present frequency bcf MENU_flags,MENU_DISP ; Remove the flag bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_mem_retr ; If an entry exits, set access address to start of Memory. Ask Operator what to do. btfsc MISC_flags,SEEP_EN ; Has an entry been saved in memory? bra $+4 ; Yes, continue retrive processing goto Menu_sw_release ; No, Simulate Menu Switch, go to next menu selection clrf seprom_a_0,1 ; Set EEPROM access address to start of Memory clrf seprom_a_2,1 ; " movlw SE_MEM_STRT ; " movwf seprom_a_1,1 ; " call Clear_lcd ; Clear the display movlw mem_msg ; Point at "Memory" messages call Display_message1 ; Display message movlw mem_retr ; Point at "Retrieve" message call Display_message1 ; Display it movlw CMD_2ND_LINE ; Move to LCD second line call Cmnd2LCD ; Do it movlw mem_encsw_msg ; Display instruction message call Display_message1 ; bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_mem_erase ; If an entry exits, set access address to 64. Ask Operator what to do. btfsc MISC_flags,SEEP_EN ; Has an entry been saved in memory? bra $+4 ; Yes, continue erase processing goto Menu_sw_release ; No, Simulate Menu Switch, go to next menu selection clrf seprom_a_0,1 ; Set EEPROM access address to start of Memory clrf seprom_a_2,1 ; " movlw SE_MEM_STRT ; " movwf seprom_a_1,1 ; " call Clear_lcd ; Clear the display movlw mem_msg ; Point at "Memory" messages call Display_message1 ; Display message movlw mem_erase ; Point at "Erase" message call Display_message1 ; Display message movlw CMD_2ND_LINE ; Move to LCD second line call Cmnd2LCD ; Do it movlw mem_encsw_msg ; Display move encoder message call Display_message1 ; bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_split call Clear_lcd movlw CMD_MOV_CUR_DISP ; Home the cursor call Cmnd2LCD ; Do it movlw split_msg ; Point at split message call Display_message ; and display split message movlw CMD_2ND_LINE+D'6' ; Move to middle of 2nd line of display call Cmnd2LCD ; and do it btfss MODE_flags,SPLIT ; Is split operation invoked? bra change_split ; No movlw yes_msg ; Yes, display the YES bra change_split_exit change_split movlw no_msg ; Point at NO message change_split_exit call Display_message ; and display the message bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_cwoff call Clear_lcd ; Wipe the display movlw CMD_MOV_CUR_DISP ; Home the cursor call Cmnd2LCD ; Send the command to LCD movlw cw_adj_msg ; Point at CW offset adjust message call Display_message ; Display CW adust message movlw CMD_2ND_LINE+D'5' ; Move cursor to second line, character 6 call Cmnd2LCD ; Move cursor call Disp_cwoffset ; Display offset movlw CMD_2ND_LINE+D'7' ; Position entry marker for update call Cmnd2LCD ; Send the command bra menu_wait_forop ; Reload menu counter and back to Main ; ********************************** Change_chip_erase call Clear_lcd ; Clear the display movlw seprom_erase ; Point at first message call Display_message1 ; and display it. movlw CMD_2ND_LINE ; Move LCD cursor to 2nd line call Cmnd2LCD ; Do it movlw mem_encsw_msg ; Point at next message call Display_message1 ; Display it ;*********************************** menu_wait_forop bcf MISC_flags,ENC_MOV ; Clear encoder movement flag movff menu_ctr_val,menu_enc_ctr ; Set value into counter goto Main ; Back to Main to wait for more encoder movement ;**************************************************************************** ; ; Purpose: MENU_process - This is where the menu action takes place ; In MENU and the Frequency Encoder has been turned. This ; function will either change item under a menu selection or ; preform the menu operation. ; ; Inputs: Frequency Encoder, Menu Switch, MENU_flags ; ; Outputs: None ; ;**************************************************************************** MENU_process bcf MISC_flags,ENC_MOV ; Clear encoder movement flag bsf MENU_flags,SUB_MENU ; Set flag - displaying menu items decfsz menu_enc_ctr,f ; Decrement the menu counter goto Main ; Not zero, wait for more encoder movement movff MENU_flags,temp ; Get menu control flags movlw LO_NYBBLE_MASK ; Mask to save just the selection andwf temp,f ; number (bits 0 thru 3) movlw D'1' ; Band change? cpfsgt temp ; Compare bra Chg_band ; Yes, go to band change movlw D'2' ; Mode change? cpfsgt temp ; Compare bra Chg_mode ; Yes movlw D'3' ; Memory retrieve? cpfsgt temp bra Chg_mem_retr ; Yes, go do Memory Retrieve Operation movlw D'4' ; Memory save? cpfsgt temp bra Chg_mem_save ; Yes, go do Memory Save Operation movlw D'5' ; Memory erase? cpfsgt temp bra Chg_mem_erase ; Yes, go do Memory Erase Operatiom movlw D'6' ; Split? cpfsgt temp ; Compare bra Chg_split ; Yes movlw D'7' ; Adjust CW offset? cpfsgt temp ; Compare bra Chg_cwoff ; Yes movlw D'8' ; SEEPROM Chip erase? cpfsgt temp bra Chg_chip_erase ; Yes, go process goto Menu_sw_exit ; End of menu, get out of menu ; ********************************** Chg_band movlw CMD_2ND_LINE ; Move cursor to 2nd line call Cmnd2LCD movlw blank_msg ; Blank out any message call Display_message btfsc last_dir,DIR_BIT ; Are we going up in the band list? bra Chg_band_up ; If so, increment the band number ; Else, we are going down in the band list decf temp1,f ; Decrement the band number bnn Chg_band_disp ; If still positive, then band number is OK ; Else wrap around to top movlw Max_band_no ; Get the top band number movwf temp1 ; Save it in temp storage bra Chg_band_disp ; Finish up Chg_band_up ; Arrive here when incrementing band number incf temp1,f ; Do it movf temp1,w ; Get new band number sublw Max_band_no ; Test if over the limit bnn Chg_band_disp ; Else, the band number is within range clrf temp1 ; If over, then reset the band number Chg_band_disp ; Arrive here with a valid band number movf temp1,w ; Get the band number to compute band index for display mullw BAND_LGTH ; Multiply by length of each band table entry movff PRODL,WREG ; Get lower part of product (band index). Must be < 256 lfsr 2,BAND_BASE ; Point FSR0 at band table addwf FSR2L,f ; Add index to correct band in table movlw FREQ_CT ; Number of bytes to move - 4 frequency movwf count ; Into counter lfsr 1,freq_0 ; Move the next band frequency to freq_0..3 for display Chg_get_band_loop movff POSTINC2,POSTINC1 ; Move byte from band table to freq_0..3 decfsz count,f ; Decrement counter bra Chg_get_band_loop ; Counter not zero, do next byte movlw CMD_2ND_LINE ; Move to LCD second line call Cmnd2LCD ; " bsf MENU_flags,MENU_DISP ; Set flag that display is for menu bsf MENU_flags,DSPY ; Set flag for Bin2BCD to not move data call Disp_freq_menu ; Display frequency bcf MENU_flags,MENU_DISP ; Remove the flag bcf MENU_flags,DSPY ; Remove the flag goto menu_proc_exit ; Go reload menu counter and then back to Main ; ********************************** Chg_mem_save ; Each memory entry is 16 bytes in length. The first 7 bytes are the VFO registers. ; Byte 0 - Band Number ; Byte 1 to Byte 4 - Frequency ; Byte 5 - Decade ; Byte 6 - Mode (Split flag not saved) ; Byte 7 - Band Index ; Byte 8 - Band Control Byte ; Byte 9 to 16 - Reserved for future use ; ; Save present VFO Registers to Serial EEPROM - Make a new entry movff vfo_pointer,FSR0L ; Pointer to active VFO registers movlw BAND_ADJUST ; Offset to vfo_X_band addwf FSR0L,f ; Prepare for indirect addressing movlw D'7' ; Count to moving seven VFO bytes movwf count ; into counter lfsr 2,se_buf_0 ; Point at Serial EEPROM buffer Chg_mem_save_loop movff POSTINC0,POSTINC2 ; Move a byte from vfo register to SEEPROM buffer decfsz count,f ; Decrement count bra Chg_mem_save_loop ; Not done, move next byte movlw H'7F' ; Get rid of Split flag in Mode flags andwf se_buf_6,f,1 ; Remove the Split flag movff band_index,POSTINC2 ; Move the band index to save buffer movf band_index,w ; Get VFO band index lfsr 1,BAND_BASE ; Get base of band table addwf FSR1L,f ; and add index to band in band table movlw BAND_OFS ; Offset to Band Control in band table addwf FSR1L,f ; Add to FSR1 that now points at control byte movff POSTINC1,POSTINC2 ; Move the control byte to buffer movff sewrite_a_0,seprom_a_0 ; Move write address to access address movff sewrite_a_1,seprom_a_1 ; " movff sewrite_a_2,seprom_a_2 ; " movlw SE_BUF_CNT ; Number of bytes to write movwf se_count ; into byte counter lfsr 1,se_buf_0 ; Point at location of write data call Se_write ; Write the data to Serial EEPROM bsf MISC_flags,SEEP_EN ; Save the fact that an entry is in Serial Memory movlw SE_BUF_CNT ; # of bytes in an entry addwf sewrite_a_0,f,1 ; Increment the Serial EEPROM write address for next write movlw D'0' addwfc sewrite_a_1,f,1 ; " addwfc sewrite_a_2,f,1 ; " movff sewrite_a_0,se_buf_0 ; Copy new write address to SEPROM buffer movff sewrite_a_1,se_buf_1 ; Copy new write address to buffer movff sewrite_a_2,se_buf_2 ; Copy new write address to buffer movff MISC_flags,se_buf_3 ; and the SEEP_EN flag movlw D'4' ; Count of four movwf se_count ; Into byte counter lfsr 1,se_buf_0 ; Point at write buffer location movlw se_wriadr_save ; Set address for saving Write Address in SEEPROM movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " call Se_write ; Save write address and Entry flag for recovery in SEEPROM clrf SEEP_flags ; All done, so clear the flags and simulate goto Menu_sw_release ; Menu switch being pressed -> Exit ; ********************************** Chg_mem_retr ; Check if access address exceeds memory capacity. Check if access address equal write address. ; If so, no more entries to display - exit. ; Read the next entry and display then increment access address to next entry. btfss seprom_a_2,0,1 ; Did the access address go above 4095 entries? bra Chg_mem_retr_nxchk ; No, movlw CMD_2ND_LINE ; Move to start of 2nd line of LCD call Cmnd2LCD ; Do it movlw end_of_mem_msg ; Point at end of message memory call Display_message1 ; Display the message "End of Memory" call Wait_a_sec ; Delay a little bra Chg_mem_retr_frcexit ; and get out Chg_mem_retr_nxchk movf sewrite_a_2,w,1 ; Get SEEPROM write address MSByte subwf seprom_a_2,w,1 ; Subtract SEPROM access address bz Chg_mem_retr_ck1 ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_retr_ok ; write > access Chg_mem_retr_ck1 movf sewrite_a_1,w,1 ; Get SEEPROM write address next MSByte subwf seprom_a_1,w,1 ; Subtract SEEPROM access address bz Chg_mem_retr_ck2 ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_retr_ok ; write > access Chg_mem_retr_ck2 movf sewrite_a_0,w,1 ; Get SEEPROM write address LSByte subwf seprom_a_0,w,1 ; Subtract SEEPROM access address bz Chg_mem_retr_laste ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_retr_ok ; write > access Chg_mem_retr_laste ; Write address = access address - quit. movlw CMD_2ND_LINE ; Move LCD cursor to start of 2nd line call Cmnd2LCD movlw end_mem_entry_msg ; Point at message "End of Entries" call Display_message1 ; Display it call Wait_a_sec ; Wait a bit Chg_mem_retr_frcexit call Wait_a_sec ; Wait some more clrf SEEP_flags ; Clear flags goto Menu_sw_release ; Exit the menu, return to normal operation Chg_mem_retr_ok bsf SEEP_flags,RA ; Tell Menu switch routine that processing needed movlw SE_BUF_CNT ; # of bytes in entry movwf se_count ; " lfsr 1,se_buf_0 ; Point at receiving buffer call Se_read ; Read the entry from Serial EEPROM movff se_buf_1,freq_0 ; Move four bytes of frequency from SEEPROM buffer to movff se_buf_2,freq_1 ; the working registers movff se_buf_3,freq_2 ; " movff se_buf_4,freq_3 ; " movlw CMD_2ND_LINE ; Move cursor to start of 2nd line call Cmnd2LCD ; and send command bsf MENU_flags,MENU_DISP ; Let display frequency know this is for menu bsf MENU_flags,DSPY ; Set flag for Bin2BCD to not move data call Disp_freq_menu ; Display this entry on 2nd line of LCD bcf MENU_flags,MENU_DISP ; Remove flag bcf MENU_flags,DSPY ; Remove flag movlw blank_rit call Display_message1 movlw SE_BUF_CNT ; Length of entry addwf seprom_a_0,f,1 ; Increment the access address by 16 to next entry movlw D'0' ; Zero W register addwfc seprom_a_1,f,1 addwfc seprom_a_2,f,1 goto menu_proc_exit ; Go wait for the operator ; Call from Menu Switch routine that Retrieve processing needed. ; Operator wants to retrieve displayed entry. The contents are in se_buf_0...15. Chg_mem_retr_proc movff vfo_pointer,FSR0L ; Pointer to vfo_X_0 movlw BAND_ADJUST ; Adjust to VFO band number in VFO registers addwf FSR0L,f ; Put this into FSR for indirect addressing lfsr 2,se_buf_0 ; Point at SEEPROM buffer movlw D'7' ; Count of seven bytes movwf count Chg_mem_retr_proc_loop movff POSTINC2,POSTINC0 ; Move a byte from buffer to VFO registers decfsz count,f ; Decrement count and if zero - continue bra Chg_mem_retr_proc_loop ; Count not zero movff se_buf_5,decade ; Move retrieved decade to working decade location movff se_buf_6,MODE_flags ; Move retrieved mode to working mode location movff se_buf_7,band_index ; Move band index from buffer to work location clrf SEEP_flags ; Clear all flags for SEEPROM processing call Set_limit_regs ; Go have all the band regs and controls setup return ; Return to Menu Switch routine ; ************************************* Chg_mem_erase ; Operator has turned Frequency Encoder so display next entry pointed to by seprom_a_0.2 ; Check if at the end of saved entries. Compare access address with write address. ; If the addresses are the same - end of saved entries movf sewrite_a_2,w,1 ; Get SEEPROM write address MSByt subwf seprom_a_2,w,1 ; Subtract SEPROM access address bz Chg_mem_erase_ck1 ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_erase_ok ; write > access Chg_mem_erase_ck1 movf sewrite_a_1,w,1 ; Get SEEPROM write address next MSByte subwf seprom_a_1,w,1 ; Subtract SEEPROM access address bz Chg_mem_erase_ck2 ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_erase_ok ; write > access Chg_mem_erase_ck2 movf sewrite_a_0,w,1 ; Get SEEPROM write address LSByte subwf seprom_a_0,w,1 ; Subtract SEEPROM access address bz Chg_mem_erase_laste ; write = access bc $ ; write < access - Should not happen - HANG bn Chg_mem_erase_ok ; write > access Chg_mem_erase_laste ; Write address = access address - quit. movlw CMD_2ND_LINE ; Move LCD cursor to start of 2nd line call Cmnd2LCD movlw end_mem_entry_msg ; Point at message "No More Entries" call Display_message1 ; Display it call Wait_a_sec ; Wait a bit Chg_mem_erase_frcexit call Wait_a_sec ; Wait some more clrf SEEP_flags ; Yes, clear flags goto Menu_sw_release ; Exit the menu, return to normal operation ; Read in the next entry in memory and display it. Then increment access address for ; next possible read. Chg_mem_erase_ok bsf SEEP_flags,EA ; Set flag that erase processing needed on exit movlw SE_BUF_CNT ; Set read count to 16 movwf se_count ; " lfsr 1,se_buf_0 ; Point at receiving buffer call Se_read ; Read the entry from Serial EEPROM movff se_buf_1,freq_0 ; Move four bytes of frequency from SEEPROM buffer to movff se_buf_2,freq_1 ; the working registers movff se_buf_3,freq_2 ; " movff se_buf_4,freq_3 ; " movlw CMD_2ND_LINE ; Move cursor to beginning of second line. call Cmnd2LCD bsf MENU_flags,DSPY ; Set flag for bin to BCD bsf MENU_flags,MENU_DISP ; Set flag that display is for menu call Disp_freq_menu ; Display this entry on 2nd line of LCD bcf MENU_flags,MENU_DISP ; Remove the flag bcf MENU_flags,DSPY ; " movlw blank_rit ; need to blank left over stuff call Display_message1 movlw SE_BUF_CNT ; Add 16 for next memory retrieve addwf seprom_a_0,f,1 ; Increment the access address by 16 to next entry clrf WREG ; in case operator wants to look at next entry addwfc seprom_a_1,f,1 addwfc seprom_a_2,f,1 goto menu_proc_exit ; Call from Menu Switch routine that Operator has selected entry is to be erased. ; SEEPROM address seprom_a_0.2 points one entry above entry to be deleted. Chg_mem_erase_proc movf seprom_a_2,w,1 ; Compare write address to access address cpfseq sewrite_a_2,1 ; If they are the same that means the last bra Chg_mem_erase_go ; entry in SEEPROM is to be erased movf seprom_a_1,w,1 ; " cpfseq sewrite_a_1,1 ; " bra Chg_mem_erase_go ; " movf seprom_a_0,w,1 ; " cpfseq sewrite_a_0,1 ; " bra Chg_mem_erase_go ; Addresses are not the same movlw SE_BUF_CNT ; Address same, so just back up write address one entry subwf sewrite_a_0,f,1 ; by subtracting 16 clrf WREG ; " subfwb sewrite_a_1,f,1 ; " subwfb sewrite_a_2,f,1 ; " bra Chg_mem_erase_done ; Go check for no entries and save write address Chg_mem_erase_go ; Erase is done by moving all entries, above entry to deleted, down one entry. ; Access address is pointing one address above entry to be deleted. ; Read in that entry, decrement address by 16 and save in entry to be deleted. ; Add 32 to memory address and compare to write address. No comparison, repeat ; the operation compacting memory. ; When new address equals write address, decrement write address by 16. It is done. Chg_mem_erase_go_loop movlw SE_BUF_CNT ; Move 16 bytes movwf se_count ; into counter lfsr 1,se_buf_0 ; FSR1 points at buffer call Se_read ; Read in the entry movlw SE_BUF_CNT ; 16 bytes subwf seprom_a_0,f,1 ; Decrement the access address by 16. The entry just read clrf WREG ; is moved down one entry in the memory subwfb seprom_a_1,f,1 ; " subwfb seprom_a_2,f,1 ; " movlw SE_BUF_CNT ; 16 bytes movwf se_count ; into read/write counter lfsr 1,se_buf_0 ; Point at data location call Se_write ; Write data in new entry, one entry lower movlw D'32' ; Now add 32 to the access address addwf seprom_a_0,f,1 ; " clrf WREG ; " addwfc seprom_a_1,f,1 ; " addwfc seprom_a_2,f,1 ; " movf seprom_a_2,w,1 ; Compare new access address with the original write address cpfseq sewrite_a_2,1 ; " bra Chg_mem_erase_go_loop ; Do not compare, loop back and move next movf seprom_a_1,w,1 ; Compare next byte cpfseq sewrite_a_1,1 ; " bra Chg_mem_erase_go_loop ; Do not compare, loop back movf seprom_a_0,w,1 ; Compare last byte cpfseq sewrite_a_0,1 ; " bra Chg_mem_erase_go_loop ; Do not compare, loop back ; When the new access address = the original write address the compaction is done. ; The write address is pointing one entry beyond last moved, so subtract entry length to ; have the write address point at next entry to be written movlw SE_BUF_CNT ; Get length of entry subwf sewrite_a_0,f,1 ; End of compaction, decrement the write address clrf WREG ; by one entry subwfb sewrite_a_1,f,1 ; " subwfb sewrite_a_2,f,1 ; " Chg_mem_erase_done movlw H'00' ; Compare the write address with address used cpfseq sewrite_a_2,1 ; when no entries exist in SEEPROM bra Chg_mem_erase_exit ; Do not compare movlw SE_MEM_STRT ; Check next lower address byte cpfseq sewrite_a_1,1 ; " bra Chg_mem_erase_exit ; Do not compare movlw H'00' ; Check lowest address byte cpfseq sewrite_a_0,1 ; " bra Chg_mem_erase_exit ; Address not equal bcf MISC_flags,SEEP_EN ; Address are equal, reset flag indicating no entries Chg_mem_erase_exit ; The write address and possibly the entries flag have been changed. ; Save this change to SEEPROM for restart. movff sewrite_a_0,se_buf_0 ; Copy new write address to buffer movff sewrite_a_1,se_buf_1 ; Copy new write address to buffer movff sewrite_a_2,se_buf_2 ; Copy new write address to buffer movff MISC_flags,se_buf_3 ; and the SEEP_EN flag movlw D'4' ; Count of four movwf se_count ; Into byte counter lfsr 1,se_buf_0 ; Point at write buffer location movlw se_wriadr_save ; movwf seprom_a_0,1 ; Set address for saving Write Address in SEEPROM clrf seprom_a_1,1 ; location 0x00000 clrf seprom_a_2,1 ; " call Se_write ; Save write address and Entry flag for recovery in SEEPROM return ; Finished erase, go back to Menu Switch processing ; ********************************** Chg_mode bcf DDS_flags,CW_PL ; Reset the CW flags bcf DDS_flags,CW_NG ; " movlw CMD_2ND_LINE+D'6' ; Move to second line, Signal Mode location call Cmnd2LCD ; Send command movff vfo_pointer,FSR0L ; Pointer to active vfo movlw MODE_OFS ; Offset to Mode in VFO registers addwf FSR0L,f ; Add in offset to Mode movlw AM ; Mode bits for AM cpfseq INDF0 ; Present mode AM? bra chg_mode_a ; No movlw SSBU ; Get SSB mode Upper bits movwf INDF0 ; Set new mode in vfo registers movwf MODE_flags ; Into working mode register movlw sbu_msg ; Point at message SU bra chg_mode_exit ; Go to display message and exit chg_mode_a movlw SSBU ; Mode bits for SSB Upper cpfseq INDF0 ; Present mode SSB Upper? bra chg_mode_1 ; No movlw SSBL ; Get SSB Lower mode bits movwf INDF0 ; Set new mode in vfo register movwf MODE_flags ; Into working mode register movlw sbl_msg ; Point at message SL bra chg_mode_exit ; Go to display message and exit chg_mode_1 movlw SSBL ; Mode bits for SSB Lower cpfseq INDF0 ; Present mode SSB Lower? bra chg_mode_2 ; No movlw CW_POS ; Get CW+ mode bits movwf INDF0 ; Set new mode in vfo register movwf MODE_flags ; Into working mode register bsf DDS_flags,CW_PL ; Set flag for DDS FTW calculation movlw cwplus_msg ; Point at message C+ bra chg_mode_exit ; Go to display message and exit chg_mode_2 movlw CW_POS ; Mode bits for CW+ cpfseq INDF0 ; Present mode CW+? bra chg_mode_3 ; No movlw CW_NEG ; Get CW- mode bits movwf INDF0 ; Set new mode in vfo registers movwf MODE_flags ; Into working mode register bsf DDS_flags,CW_NG ; Set flag for DDS FTW calculation movlw cwmin_msg ; Point at C- message bra chg_mode_exit ; Go to display mode and exit chg_mode_3 movlw CW_NEG ; Mode bits for CW Negative cpfseq INDF0 ; Present mode CW-? bra chg_mode_4 ; No movlw DS ; Get Double Sideband mode bits movwf INDF0 ; Set new mode in vfo register movwf MODE_flags ; Into working mode register movlw dsbnd_msg ; Point at message DS bra chg_mode_exit ; Go to display message and exit chg_mode_4 movlw DS ; Mode bits for Double Sideband cpfseq INDF0 ; Present mode Double sideband? bra chg_mode_5 ; No movlw PM ; Get Phase mode bits movwf INDF0 ; Set new mode in vfo register movwf MODE_flags ; Into working mode register movlw phase_msg ; Point at message PM bra chg_mode_exit ; Go to display message and exit chg_mode_5 ; Must be Phase mode, so go back to AM movlw AM ; Get AM mode bits movwf INDF0 ; Set new mode in vfo registers movwf MODE_flags ; Into working mode register movlw am_msg ; Point at message AM chg_mode_exit call Display_message ; Display the correct message and call External_ctl_mode ; Change external controls if needed call Send_dds_freq ; Send new frequency to DDS goto menu_proc_exit ; go back to Main ; ********************************** Chg_split btfss MODE_flags,SPLIT ; In split mode bra chg_split_on ; No bcf MODE_flags,SPLIT ; Yes, turn off split mode bcf mode_a,SPLIT ; Turn off split in VFO-A bcf mode_b,SPLIT ; and VFO-B movlw CMD_2ND_LINE+D'6' ; Move the cursor call Cmnd2LCD ; Do it movlw no_msg ; Pointer to the NO message call Display_message ; Display the message bra chg_split_exit chg_split_on bsf MODE_flags,SPLIT ; Turn on split mode bsf mode_a,SPLIT bsf mode_b,SPLIT movlw CMD_2ND_LINE+D'6' ; Move the cursor call Cmnd2LCD ; Do it movlw yes_msg ; Pointer to the Yes message call Display_message ; Display the message chg_split_exit goto menu_proc_exit ; Back to Main ; ********************************** Chg_cwoff btfsc last_dir,DIR_BIT ; Is the knob going up? bra cwoff_up ; Yes, then add the increment cwoff_down ; Else, shaft direction is CCW bcf STATUS,C ; Clear carry movlw D'10' ; Count of - 10 Hz subwf cwoffs_pos_0,f,1 ; Subtract 10 Hz from offset movlw D'0' subwfb cwoffs_pos_1,f,1 ; Add carry to offset MSByte btfss STATUS,N ; Is MSByte negative? bra cwoff_update ; No movlw D'10' ; movwf cwoffs_pos_0,1 ; Yes, went under, set offset to ten clrf cwoffs_pos_1,1 ; " bra cwoff_update ; Update LCD and DDS cwoff_up movlw D'10' ; Get count of 10 addwf cwoffs_pos_0,f,1 ; Add to Offset LSByte movlw D'0' ; Get a zero addwfc cwoffs_pos_1,f,1 ; and add to offset MSByte btfss cwoffs_pos_1,4,1 ; Did the add exceed maximum? bra cwoff_update ; No movlw H'FA' ; Yes, set maximum offset - 4090 Hz movwf cwoffs_pos_0,1 ; " movlw H'0F' ; " movwf cwoffs_pos_1,1 ; " cwoff_update movff cwoffs_pos_0,cwoffs_neg_0 movff cwoffs_pos_1,cwoffs_neg_1 comf cwoffs_neg_0,f,1 ; Need to negate the offset working values comf cwoffs_neg_1,f,1 ; " incfsz cwoffs_neg_0,f,1 ; Add one to complete 2's complement bra update_done ; Finished if no carry incfsz cwoffs_neg_1,f,1 update_done movlw saved_cwoffset ; Save the new offset in SEPROM. Set address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'2' ; # of bytes movwf se_count ; Into counter lfsr 1,cwoffs_pos_0 ; Point at data to save call Se_write ; Write the new offset to SEPROM movlw CMD_2ND_LINE+D'5' ; Reset cursor for display call Cmnd2LCD ; Send command to LCD call Disp_cwoffset ; Display the new offset movff MODE_flags,temp ; Get the mode, move to work movlw LO_NYBBLE_MASK ; Mask for signal mode only andwf temp,f ; Save just signal mode movlw CW_POS ; CW+ cpfseq temp ; Is signal mode CW+? bra $+4 ; No bra cwoff_send_ftw ; Yes update DDS frequency movlw CW_NEG ; CW- cpfseq temp ; Is signal mode CW-? bra cwoff_nosend_ftw ; Not a CW sigal mode so do not update DDS cwoff_send_ftw call Send_dds_freq ; Update the DDS cwoff_nosend_ftw movlw CMD_2ND_LINE+D'7' ; Move cursor for next update (if needed) call Cmnd2LCD ; Send the command goto menu_proc_exit ; Wait for operator action ; *********************************** Chg_chip_erase clrf sewrite_a_0,1 ; Reset the SEPROM write address to S/R Memory first entry clrf sewrite_a_2,1 ; " movlw SE_MEM_STRT ; " movwf sewrite_a_1,1 ; " bcf MISC_flags,SEEP_EN ; Reset flag that indicates and entry in S/R memory clrf SEEP_flags ; All done, so clear the flags and simulate goto Menu_sw_release ; Menu switch being pressed -> Exit ;****************************************************************************** menu_proc_exit movff menu_ctr_val,menu_enc_ctr ; Set value into menu encoder counter bcf MISC_flags,ENC_MOV ; Clear encoder movement flag goto Main ; Back to Main to wait for operator action ;****************************************************************************** ; ; Purpose: Arrive here when VFO Switch was pressed. One of four actions: ; 1. Menu switch also pressed, transfer to RIT control ; 2. Switch VFO's. ; 3. After 2 second delay, copy active vfo to inactive vfo. ; 4. Exit Main menu. ; ; Input: VFO Switch. ; Entry point - Vfo_switch determines if VFO copy, menu exit or ; vfo swap. ; ; Output: Operator requested operation takes place. ; ;****************************************************************************** Vfo_switch ; Arrive here to process VFO switch btfss MISC_flags,RTF ; In receive mode? goto Main ; No, transmit mode, ignore VFO Switch movlw PB_RPT_WAIT ; Setup the delay timer for 2 seconds movwf PB_wait_count ; Vfo_sw_release_wait btfss PORTA,MENU_SW ; Is the Menu switch also pushed? goto Rit_control ; Yes, goto the RIT enable/disable call Wait_25ms ; Wait a bit before checking again btfsc PORTA,VFO_SW ; Is VFO_SW still pushed ? bra Vfo_sw_release ; If it's released, continue processing ; Else, test if delay has expired decfsz PB_wait_count,f ; Decrement wait period bra Vfo_sw_release_wait ; Keep looping until something happens ; 2 second delay has expired, copy active ; VFO registers to inactive VFO registers btfss vfo_select,LSB ; Is VFO-B active bra a_active_copy ; No call Copy_b_to_a ; Yes, copy VFO-B to VFO-A bra vfo_copy_done a_active_copy call Copy_a_to_b ; Copy VFO-A to VFO-B vfo_copy_done bsf MENU_flags,MENU ; Fake out process timer routine movlw CMD_2ND_LINE+D'2' ; Postion cursor call Cmnd2LCD ; Send it movlw copy_msg ; Point at Copied message call Display_message ; Display it vfo_copy_wait btfss PORTA,VFO_SW ; Is VFO Switch released bra vfo_copy_wait ; No movlw CMD_2ND_LINE+D'2' ; Yes, reposition cursor call Cmnd2LCD ; Send command to LCD movlw D'10' ; Number spaces to send movwf temp ; into counter vfo_copy_blank movlw ' ' ; Space character call Data2LCD ; Send the space to blank message decfsz temp,f ; Count down bra vfo_copy_blank ; Not zero, do it again call Disp_mode ; Mode was wiped, display it bcf MENU_flags,MENU ; Release timer routine call Set_cursor_pos ; Reset Entry marker to frequency display return ; Back to Main Vfo_sw_release btfss MENU_flags,MENU ; Is menu active? call Swap_vfo ; No, go to switch vfo's clrf MENU_flags ; Yes, clear menu flags and get out of menu vfo_sw_cont call Clear_lcd ; Clear the display call Disp_vfo ; Display VFO in use call Disp_mode ; Display operating mode call Rit_display ; Display RIT frequency vfo_sw_norit call Disp_funit ; Display frequency unit movlw D'0' ; No decade change call Update_decade ; Display frequency, set entry marker position, ; and update fstep call Send_dds_freq ; Send the FTW to the DDS chip goto Main ; Back to Main ;***************************************************************************** ; ; Purpose: This routine reads the frequency value of a band table entry ; from Ram Bank 2 and stores it in the active VFO registers. It ; also sets decade, mode and control select lines from band table ; entry. The low limit and high limit registers are loaded. ; ; Input: vfo_pointer ; Two entry points: ; 1. Get_band loads VFO registers with new band data, sets ; external control lines and loads limit registers. ; 2. Set_limit_regs sets the external control lines and loads the ; limit registers. ; ; Output: The new band frequency in the current vfo frequency registers, ; external control lines and limit registers are set. ; ;***************************************************************************** Get_band btfss PWR_flags,POWER_UP ; Is processing for band change or power up bra get_band_clearrit ; Band change, get the frequency for new band btfsc PWR_flags,NOT_1ST ; Power up, is this the first time thru startup bra Set_limit_regs ; No, band and frequency has already been setup ; First time through startup or band change get_band_clearrit btfsc vfo_select,LSB ; VFO-A in use? bra get_band_clearritb ; No, it must be VFO-B bcf RIT_flags,RIT_VFOA ; Clear VFO-A RIT flag clrf rit_vfoa_0,1 ; Zero the frequency clrf rit_vfoa_1,1 ; " bra get_band_index ; Continue get_band_clearritb bcf RIT_flags,RIT_VFOB ; Clear VFO-B RIT flag clrf rit_vfob_0,1 ; Zero the frequency clrf rit_vfob_1,1 ; " get_band_index movff vfo_pointer,FSR0L ; Put pointer to active VFO registers in FSR0 movlw BAND_ADJUST ; Offset to vfo_X_band addwf FSR0L,f ; Prepare for indirect addressing movf POSTINC0,W ; Get the band number mullw BAND_LGTH ; Multiply by length of each band table entry movff PRODL,band_index ; Save lower part of product (band index). Must be < 256 lfsr 1,BAND_BASE ; Point FSR1 at band table movf band_index,w ; Get the band index addwf FSR1L,f ; Add index to correct band in table movlw FREQ_CT+CTL_CT ; Number of bytes to move - 4 frequency, decade, mode movwf count ; Into counter get_band_loop movff POSTINC1,POSTINC0 ; Move byte from band table to vfo_x_0..3, decade and mode decfsz count,f ; Decrement counter bra get_band_loop ; Counter not zero, do next byte decf FSR0L,f ; Back up to mode movff INDF0,MODE_flags ; Set the working mode decf FSR0L,f ; Back up to decade movff INDF0,decade ; and set working decade ; Initialize external control select lines and the lower and upper frequency band limits Set_limit_regs ; <<<<<<<<<<<<<<<<<<<<<<< ext_ctl_0 contains: ; Control 0 to 3 (1 to 4) are for LNA select ; Control 4 (5) is SSB Relay control ; Control 5 (6) is R/T control for Receiver Mute, LO Switching and Excitor Power ; Control 6 (7) is for AM Signal Mode ; Control 7 (8) is for CW Signal Mode ; External control Byte 1 -> ext_ctl_1 contains: ; Control 0 (9) is for Double Sideband Mode ; Control 1 (10) is for Phase Mode ; Control 2 (11) used to enbale power to T2Pro Excitor MMIC Amplifier External_ctl ; Band select control lines movlw HI_NYBBLE_MASK ; Mask to remove old band select lines andwf ext_ctl_0,f ; and save upper part of control byte 0 lfsr 2,BAND_BASE ; Point FSR2 at band table in ram movf band_index,w ; Get index into band table addlw BAND_OFS ; Add offset to band control in band table addwf FSR2L,f ; Point at Control byte of the new band bcf MISC_flags,NO_TX ; Reset no transmit flag btfsc INDF2,NO_TX ; Is No Transmit bit set for this band bsf MISC_flags,NO_TX ; Yes set flag that cannot go to transmit movff INDF2,WREG ; Get new band select byte into W andlw LO_NYBBLE_MASK ; Save just the new band select control lines iorwf ext_ctl_0,f ; Move new band select into external control_0 ; Now set the external signal mode control. External_ctl_mode ; <<<<<<<<<<<<<<<<<<< Entry point ;Signal mode control. Sideband select relay. External Signal Mode Control ; Lower Sideband - Relay Activated ; Upper Sideband - Relay Deactivated ; CW Positive - Relay Activated ; CW Negative - Relay Deactivated movwf ext_ctl_0,W ; Get external control byte 0 andlw LO_NYBBLE_MASK ; Save the LNA select, clear signal mode control clrf ext_ctl_1 ; Clear the signal mode bits in external control byte 1 movff MODE_flags,temp ; Get the signal mode movlw H'07' ; Mask for signal mode andwf temp,f ; Save just the signal mode movlw SSBL ; Lower Sideband mode cpfseq temp ; Is mode Lower sideband? bra $+6 ; No bsf ext_ctl_0,4 ; Yes, enable the SSB relay in I/Q generator bra External_ctl_rt ; Continue movlw SSBU ; Upper Sideband mode cpfseq temp ; Is signal mode Upper Sideband? bra $+4 ; NO bra External_ctl_rt ; Yes, leave SSB relay disabled movlw CW_POS ; CW Positive mode? cpfseq temp ; Is mode CW Positive? bra $+8 ; No bsf ext_ctl_0,4 ; Yes, enable the sideband relay bsf ext_ctl_0,7 ; Set external control for CW signal mode bra External_ctl_rt ; CW Negative? leave the sideband relay disabled. movlw CW_NEG ; Is mode CW Negative? cpfseq temp bra $+6 ; No bsf ext_ctl_0,7 ; Yes, set CW external control bra External_ctl_rt ; Send new Signal mode select lines movlw AM ; AM Mode? cpfseq temp ; Compare with signal mode bra $+6 bsf ext_ctl_0,6 ; Yes Set AM mode control bra External_ctl_rt ; Send new Signal mode select lines movlw DS ; Is Mode Double sideband? cpfseq temp bra $+6 ; No bsf ext_ctl_1,0 ; Yes, set external control for Double sideband bra External_ctl_rt ; Send new Signal mode select lines movlw PM ; Is Signal mode Phase? cpfseq temp bra $+4 ; No bsf ext_ctl_1,1 ; Yes, set external control for Phase bra $+4 ; Send external controls ext_ctl_halt_loop goto ext_ctl_halt_loop ; Something very worong -> HANG External_ctl_rt ; <<<<<<<<<<<<<<<<<< Entry point ; Receive/transmit (PTT) control btfss MISC_flags,RTF ; Test R/T flag bra $+6 ; Flag is low - transmit bsf ext_ctl_0,5 ; Receive, so set external control to +5V ; Unmute receiver, switch VFO to receiver bra $+4 ; Continue bcf ext_ctl_0,5 ; Transmit, so set external control to Gnd ; Mute receiver, switch VFO to transmitter ; Now send the controls to external interface. Ctl_ext_send ; <<<<<<<<<<<<<<<<<<<<<<< Entry Point movlw EXT_BYTE_CNT ; Number of bytes to transfer movwf count ; Save the count bsf PORTC,EICS ; Set Interface Chip Select to high, shift register enable movff ext_ctl_0,byte2send ; Get the external control byte 0 to send ctl_ext_next_byte bsf STATUS,C ; Set `end of loop' flag rrcf byte2send,f ; Place first bit into Carry ctl_ext_send_loop bcf PORTB,EIDI ; Precondition Shift register serial data to a zero bnc $+4 ; Check data - 0 or 1? bsf PORTB,EIDI ; Send one bsf PORTC,EICK ; Raise serial clock bcf PORTC,EICK ; and drop the clock bcf STATUS,C ; Clear data in Carry rrcf byte2send,f ; Place next bit into Carry tstfsz byte2send ; Test, is data byte all zeros bra ctl_ext_send_loop ; No, send next bit dcfsnz count,f ; All bytes been transferred? bra ctl_ext_exit ; Yes, exit movff ext_ctl_1,byte2send ; No, get the next byte bra ctl_ext_next_byte ; Send the next byte ctl_ext_exit bcf PORTB,EIDI ; Drop the data line bsf PORTC,EIPK ; Raise the Parallel transfer clock nop ; Delay bcf PORTC,EIPK ; Drop the Parallel transfer clock return ; All done ; ***************************************************************************** ; ; Purpose: Select the proper Low Noise Amplifier for the tuned frequency. ; ; Input: Frequency, LNA table ; ; Output: Correct LNA selected for tuned frequency. ; ; ***************************************************************************** ; A table resides in Ram Bank 3. The table contains two bytes of highest frequency ; for each LNA starting with the lowest frequency. LNA's are numbered starting at ; zero. The table is searched starting at the lowest frequency and is compared to ; the active VFO registers. When the table frequency exceeds the VFO registers ; the previous LNA is used. ; Get_LNA btfss MISC_flags,RTF ; In Transmit? return ; Yes, do nothing btfsc MENU_flags,MENU ; In main menu? return ; Yes, do nothing btfsc RIT_flags,RIT_STEP ; Adjusting RIT Step? return ; Yes, do nothing lfsr 2,DECADE_BASE ; Point at Ram Bank 3 movlw LNA_bnd_tbl ; Get offset to the LNA table addwf FSR2L,f ; FSR2 now points at LNA table movff vfo_pointer,FSR0L ; Point at active VFO registers movlw TABLE_ADJUST ; Move to MSByte of the VFO frequency addwf FSR0L,f ; " clrf count ; Clear counter for LNA# get_lna_search ; Check VFO frequency MSByte against the LNA table entry. f - W movf POSTINC2,w ; Get LNA table MSByte -> LNAentry+1 subwf POSTDEC0,w ; vfo_X_3 - LNA table MSByte -> vfo_X_2 bz get_lna_lsb ; LNA table MSByte = vfo_X_3 bn get_lna_ok ; vfo_X_3 < LNA table MSByte bc get_lna_next ; vfo_X_3 > LNA_table MSByte ; Check VFO frequency next MSByte against the LNA Table entry. get_lna_lsb movf INDF0,w ; Get vfo_X_2 subwf POSTINC2,w ; LNA table 2nd MSByte - vfo_X_2 bz get_lna_ok ; LNA table 2nd MSByte = vfo_X_2 bn get_lna_next_1 ; LNA table 2nd MSByte < vfo_X_2 bc get_lna_ok ; LNA table 2nd MSByte > vfo_X_2 bra get_lna_next_1 get_lna_next incf FSR2L,f ; Increment to next LNA table entry get_lna_next_1 incf FSR0L,f ; Decrement back to vfo_X_3 incf count,f ; Increment LNA# count bra get_lna_search ; Keep searching get_lna_ok ; Correct entry found in LNA table for VFO frequency movf ext_ctl_0,w ; Get external control byte zero andlw LO_NYBBLE_MASK ; Mask to remove other controls, save LNA number subwf count,w ; Subtract LNA number found in table search btfsc STATUS,Z ; Is the zero (same) indicator set? return ; Yes, LNA numbers same, do nothing movlw HI_NYBBLE_MASK ; NO, mask for bits 4-7 andwf ext_ctl_0,f ; and save upper part of control byte 0 movf count,w ; Get the LNA number from LNA table iorwf ext_ctl_0,f ; Move new LNA select into external control_0 call Ctl_ext_send ; Now send the new LNA select lines return IFDEF BAR_GRAPH ; *************************************************************************** ; ; Purpose: Handle the Analog to Digital Convertor and Bar Graph. ; ; Input: ADC flag, ADC result register ; ; Output: The bar graph display is updated. ADC flag is reset ; ; *************************************************************************** ADC_proc btfss MISC_flags,AGD ; Is processing required goto Main ; No, go back to Main bcf MISC_flags,AGD ; Reset flag that ADC needs processing btfsc RIT_flags,RIT_STEP ; Is RIT Step being adjusted? goto Main ; Yes, go back to Main btfsc MENU_flags,MENU ; Is main menu active? goto Main ; Yes, do nothing, back to Main btfss MISC_flags,RTF ; In Transmit? bra Adc_proc_xmt ; Yes, display the linear bar graph btfsc vfo_select,LSB ; No, is VFO-A active? bra ADC_proc_vfob ; No check VFO-B btfsc RIT_flags,RIT_VFOA ; Yes, is VFO-A RIT enabled? goto Main ; Yes, go back to Main bra ADC_proc_dograph ; No, do the bar graph ADC_proc_vfob btfsc RIT_flags,RIT_VFOB ; VFO-B is active is RIT enabled? goto Main ; Yes, go back to Main ADC_proc_dograph IFNDEF BG_CENTER ; ; Receive, no RIT, not in Menu so do the S-Meter Bar Graph. ; Transmit do the linear Bar Graph display. ; The ADC count is multiplied by multiplier calculated at startup. This new ; count is used to search a table to find how many bars to turn on in the bar ; graph display. The table format, in RAM, is two bytes per entry, with the bytes ; reversed from the defining table format. ; movff ADRESH,DDS_4 ; Get ADC results and move for multiply movff ADRESL,DDS_5 ; " movff smtr_mult_0,DDS_6 ; Move multiplier movff smtr_mult_1,DDS_7 call Mpy16X16 ; Multiply ADC count by S-Meter multiplier movff DDS_3,DDS_7 ; Divide the multiply result by 100 movff DDS_2,DDS_6 ; This is new ADC count referenced to 5-0 volt Vref movff DDS_1,DDS_5 ; The S-Meter lookup table is for 5-0 Volt Vref movlw D'100' movwf freq_1 clrf freq_0 call Div24_16 ; (New ADC count * 100)/100 ; The ADC count has been multiplied by the multiplier (calculated during startup) and then divided by 100. ; This gives the new ADC count referenced to maximum input voltage of + 5. movff DDS_6,BCD_0 ; Save Adc Count MSByte movff DDS_7,BCD_1 ; Save Adc Count LSByte btfss BCD_0,2 ; Is the new ADC count greater than 0x03FF (Maximum ADC count)? bra Adc_cnt_ok ; No, continue movlw H'03' ; Yes, correct new ADC count to maximum movwf BCD_0 ; Set ADC count MSByte to 0x03 movlw H'FF' ; movwf BCD_1 ; and set ADC count LSByte to 0xFF ; Have a valid ADC count. Now search the S-Meter table to find how many bars to display. ; The search is divided into four parts depending on the ADC count MSByte. Adc_cnt_ok clrf count ; Clear counter. Will count bars to display lfsr 2,DECADE_BASE ; Point to Ram Bank 3 movlw smeter_tbl ; Get offset to Smeter table in Ram - low order byte addwf FSR2L,f ; Add in the Smeter offset tstfsz BCD_0 ; Is ADC Count MSByte a zero? bra Adc_msb_nz ; No movlw D'10' ; Yes, get count of # table entries with MSByte = zero movwf temp ; Set the search limit counter ; This routine actually does the search. Adc_chk_next ; Search S-Meter Table to find ADC count movf POSTINC2,w ; Get S-Meter Table entry LSByte subwf BCD_1,w ; Subtract table entry from ADC count bz Adc_inc_disp_smeter ; Table = ADC count, go increment bar counter and do display bnc Adc_disp_smeter ; ADC count < Table entry, done scanning, do display incf count,f ; Increment to next display bar incf FSR2L,f ; Increment to next table entry dcfsnz temp,f ; ADC count > Table entry, decrement the limit counter bra Adc_disp_smeter ; Limit counter is zero, use last entry bra Adc_chk_next ; Check next table entry Adc_msb_nz ; ADC count MSByte is not zero movlw D'2' ; Is MSByte of ADC count 2 or 3? subwf BCD_0,w ; Subtract two from the ADC Count MSByte bc Adc_msb_two ; Result zero or positive so MSByte is => two movlw D'10' ; MSByte is a one, add additional bars to bar counter movwf count ; Set bar counter movlw D'20' ; Add additional offset for table to start of "1" entries addwf FSR2L ; Add offset to S-Meter table pointer movlw D'9' ; Maximum # entries with MSByte = one movwf temp ; Set the limit counter bra Adc_chk_next ; Start checking table entries Adc_msb_two ; ADC count MSByte is two or three bnz Adc_msb_three ; Result is positive so ADC count MSByte is three movlw D'20' ; ADC MSByte is 2, add additional bars to bar counter movwf count ; Set the bar counter movlw D'40' ; Generate offset to S-Meter table addwf FSR2L ; in File Select Register 2 (Table Pointer) movlw H'94' ; LSByte for start of +20 subwf BCD_1,w ; Subtract LSByte of count from "94" bc Adc_msb_two_20 ; The Adc count is +20 or higher movlw D'1' ; Set limit counter for # entries of S9 movwf temp ; Set the limit counter bra Adc_chk_next ; Start checking table entries Adc_msb_two_20 movlw D'1' ; Bump bar counter addwf count,f ; " movlw D'2' ; addwf FSR2L,f ; Bump the S-Meter table pointer movlw D'2' ; Maximum # entries for MSByte = two movwf temp ; Set the limit counter bra Adc_chk_next ; Start checking table entries Adc_msb_three movlw D'22' ; Add additional bars to bar counter movwf count ; Set bar counter movlw D'44' ; Generate offset to S-meter table addwf FSR2L ; Add to S-meter table pointer movlw D'3' ; Maximum # entries with MSByte = three movwf temp ; into limit counter bra Adc_chk_next ; The ADC count has been located in table. Now turn the bar count into an alpha-numeric ; display and a bar graph. Adc_inc_disp_smeter incf count,f ; Increment the bar count Adc_disp_smeter movff count,temp1 ; Save the bar display count movlw CMD_2ND_LINE+D'9' ; Move cursor to LCD second line, call Cmnd2LCD ; character 10 movlw D'21' ; First count for bars over "S" values subwf temp1,w ; Subtract from # bars to display bc Adc_notsu_disp ; Display is not S Units but is dB above S9 movff temp1,DDS_6 ; Move bar count - # of bars to display clrf count ; Clear S Unit counter movlw D'2' ; Constant to subtract from # of bars Adc_S_calc subwf DDS_6,f ; Subtract 2 from # of bars bn $+8 ; If negative, count has the S Unit value bz $+6 ; If zero, count has the S Unit value incf count,f ; Bar count is not negative or zero, increment S Unit value bra Adc_S_calc ; Try again Adc_S_disp movlw 'S' ; Display an "S" call Data2LCD ; Do it movf count,w ; Get S unit value addlw ASCII_NUM_BASE ; Convert result to ASCII and display call Data2LCD ; S unit value on LCD bra Adc_fin_smeter ; Go finish Adc_notsu_disp movf BCD_0,w ; Get ADC count MSByte sublw D'2' ; Subtract 2 from ADC count MSByte bnz Adc_msb_3 ; ADC MSBye is 3 Adc_disp_20 movlw '2' ; Adc count is in +20 range so get a two call Data2LCD ; and display bra Adc_fin_over ; Go finish Adc_msb_3 movlw H'44' ; Minimum ADC count for +40 subwf BCD_1,w ; Subtract minimum count from ADC count LSByte bnc Adc_disp_20 ; Count is below minimum for 40 movf BCD_1,w ; Get ADC count LSByte sublw H'F0' ; Subtract count for 60 over bc Adc_disp_40 ; Count less than "F0" movlw '6' ; Adc count is in +60 range call Data2LCD ; so display a 60 bra Adc_fin_over ; Go finish Adc_disp_40 movlw '4' ; Display the 4 call Data2LCD ; Do it Adc_fin_over movlw '0' ; Now display the zero call Data2LCD ; " Adc_fin_smeter clrf DDS_5 ; Calculate how many blocks to display movff temp1,DDS_6 ; Move bar count to numerator movlw D'5' ; Five bars per block into denominator movwf DDS_2 ; " clrf DDS_3 ; " call Div16by8_Fract ; Divide #bars / 5bars per block movlw D'5' ; Set the display blank counter to 5, movwf count ; used to blank out remaining characters movf DDS_6,w ; Get the # of blocks bz adc_smeter_remain ; Zero result, check remainder adc_smeter_blk_loop movlw H'04' ; Result > block, send block call Data2LCD ; " dcfsnz count,f ; Decrement the blank counter bra adc_smeter_exit ; Blank counter zero, all done decfsz DDS_6,f ; Decrement # blocks to display bra adc_smeter_blk_loop ; Do it again adc_smeter_remain movf DDS_2,w ; Get MSByte of the remainder bz adc_smeter_blank ; Remainder is zero, go to blank fill movlw D'1' ; Get constant to compare to 1 cpfseq DDS_2 ; Compare to remainder bra $+6 ; Remainder is not 1 movlw H'00' ; Send 1st bar to LCD bra Adc_disp_bar ; Go display the required number of bars movlw D'2' ; Get constant to compare to remainder of 2 cpfseq DDS_2 ; Compare with the remainder bra $+6 ; Remainder is not 2 movlw H'01' ; Display 2 bars bra Adc_disp_bar ; Send 2 bars to LCD movlw D'3' ; Get constant to compare to 3 cpfseq DDS_2 ; Compare to remainder bra $+6 ; Remainder is not 3 movlw H'02' ; Display 3 bars bra Adc_disp_bar ; Send 3 bars to LCD movlw H'03' ; Display 4 bars Adc_disp_bar call Data2LCD ; Do the bar display dcfsnz count,f ; Decrement the blank counter bra adc_smeter_exit ; Blank counter at zero, exit adc_smeter_blank movlw ' ' ; Get a space character call Data2LCD ; and send to display decfsz count,f ; Decrement the counter bra adc_smeter_blank ; Not done, do it again adc_smeter_exit call Set_cursor_pos ; Return entry marker to frequency display goto Main ; Done, back to Main ELSE ; ; Following code is for ZERO-CENTER BAR GRAPH ; movff ADRESH,BCD_0 ; Get the count from ADC movff ADRESL,BCD_1 ; " movlw CMD_2ND_LINE+D'7' ; Move cursor to start of graph call Cmnd2LCD ; and send it movlw space_offset ; Get spaces to blank graph area on LCD call Display_message ; and send it movlw space_offset ; Get spaces to blank graph area on LCD call Display_message ; and send it movlw CMD_2ND_LINE+D'11' ; Move cursor to center of bar graph call Cmnd2LCD movf bg_ctr_cnt_lo,w ; 16 bit subtract: ADC CT - Center CT subwf BCD_1,f ; This count determines bars and blocks movf bg_ctr_cnt_hi,w ; above or below center subwfb BCD_0,f ; Difference is in BCD_0 and BCD_1 bn bg_neg ; Result is negative, go process left bar graph btfsc STATUS,Z ; If result of MSByte subtract is zero -> tstfsz BCD_1 ; MSByte was zero, is LSByte zero bra $+4 ; No, result is positive, go process right bar graph bz bg_zero ; Result is zero, go display center bar ; Difference count is positive - right bar graph movff bg_ctr_cnts_bar,DDS_5 ; Move counts per bar to DDS_5 bcf STATUS,C ; Clear Carry rrcf DDS_5,f ; Divide by 2, now contains count for 1/2 bar movf DDS_5,w ; Subtract ADC difference count - 1/2 bar subwf BCD_1,w ; Subtract ADC Difference count - 1/2 bar count clrf WREG subwfb BCD_0,w bn bg_zero ; Less than 1/2 bar, display center bar movf DDS_5,w ; Get 1/2 bar count addwf bg_ctr_cnts_bar,w ; W contains count for 1 1/2 bars subwf BCD_1,w ; Subtract ADC-difference-count - 1 1/2 bar count clrf WREG subwfb BCD_0,w ; bnn bg_ctr_75 ; Over 1 1/2 bars, go check next bar movlw H'05' ; < 1 1/2 bars get two bars right character call Data2LCD ; Display it bra adc_zc_exit ; Go finish bg_ctr_75 movf BCD_5,w ; 1/2 bar count addwf bg_ctr_cnts_bar,w ; Make count 1 1/2 bars addwf bg_ctr_cnts_bar,w ; Make it 2 1/2 bars subwf BCD_1,f ; Subtract ADC-Difference-count - 2 1/2 bar count clrf WREG subwfb BCD_0,f bnn bg_block_fill ; Over 2 1/2 bars, go display blocks movlw H'06' ; No, get 3 bars right character call Data2LCD ; and display it bra adc_zc_exit ; Exit bg_block_fill movlw H'06' ; Get 3 bars right character call Data2LCD ; and display it bg_block_fill_loop movf bg_ctr_cnts_blk,w ; Number counts/block subwf BCD_1,f ; Subtract ADC diff. - counts/block clrf WREG subwfb BCD_0,f bn adc_zc_exit ; Went negative, finished movlw H'04' ; Send full block to LCD call Data2LCD ; bra bg_block_fill_loop ; Go again bg_zero movlw H'7C' ; Display center bar call Data2LCD ; and display it bra adc_zc_exit ; Go finish ; ; Display is to the left ; bg_neg ; The Bar Graph is to left of center comf BCD_1,f ; Make difference count positive comf BCD_0,f ; " infsnz BCD_1,f ; Add one incf BCD_0,f ; If add resulted in zero, add one to BCD_0 movlw CMD_2ND_LINE+D'11' ; Move to center of graph display call Cmnd2LCD ; Do it movff bg_ctr_cnts_bar,DDS_5 ; Get # counts-per-bar bcf STATUS,C ; Clear Carry rrcf DDS_5,f ; Divide by 2, BCD_5 = 1/2 bar count movf BCD_5,w ; Get 1/2 bar count subwf BCD_1,w ; Subtract ADC-difference-count - 1/2 bar count clrf WREG subwfb BCD_0,w bn bg_zero ; Less than 1/2 bar, display center bar movf DDS_5,w ; Get 1/2 bar count addwf bg_ctr_cnts_bar,w ; W contains count for 1 1/2 bars subwf BCD_1,w ; Subtract ADC-difference-count - 1 1/2 bar count clrf WREG subwfb BCD_0,w ; bnn bg_ctr_next_bar ; Over 1 1/2 bars, go check next bar movlw H'07' ; < 1 1/2 bars get two bars left character call Data2LCD ; Display it bra adc_zc_exit ; Go finish bg_ctr_next_bar movf BCD_5,w ; 1/2 bar count addwf bg_ctr_cnts_bar,w ; Make count 1 1/2 bars addwf bg_ctr_cnts_bar,w ; Make it 2 1/2 bars subwf BCD_1,f ; Subtract ADC-Difference-count - 2 1/2 bar count clrf WREG subwfb BCD_0,f bnn bg_cnt_#blocks ; Over 2 1/2 bars, go display blocks movlw H'02' ; No, get 3 bars right character call Data2LCD ; and display it bra adc_zc_exit ; Exit bg_cnt_#blocks clrf count ; Clear block display counter movlw D'11' ; LCD character number to start displaying movwf temp ; full blocks bg_ctr_loop movf bg_ctr_cnts_blk,w ; Counts per block subwf BCD_1,f ; Subtract from ADC difference count clrf WREG ; " subwfb BCD_0,f ; " bn bg_ctr_fill ; ADC difference count went negative decf temp,f ; Decrement back one LCD character incf count,f ; Increment counter, number of blocks to display bra bg_ctr_loop ; Check for more blocks to display bg_ctr_fill movf count,w ; Get count of number blocks to display bz bg_3bars ; Zero, so display 3 bars left and exit movf temp,w ; Get character position to start displaying addlw CMD_2ND_LINE ; Form command, move to starting LCD character call Cmnd2LCD ; to display and send it bg_ctr_fill_loop movlw H'04' ; Send the full block to LCD call Data2LCD ; " decfsz count,f ; Decrement count of blocks to send bra bg_ctr_fill_loop ; Not there yet, send another block bg_3bars movlw CMD_2ND_LINE+D'11' ; Move to center of graph display call Cmnd2LCD ; Do it movlw H'02' ; Get three bars left to display at center call Data2LCD ; of graph adc_zc_exit call Set_cursor_pos ; Return entry marker to frequency display goto Main ; Done, back to Main ENDIF Adc_proc_xmt ; ; Following code is for the ZERO-TO-10 BAR GRAPH ; ; Maximum count from ADC is 1024. The ADC count is divided by counts-per-block. ; This will determine how many full blocks are turned on in the LCD bar graph display. clrf DDS_5 movff ADRESH,DDS_6 ; Get ADC results and move for divide movff ADRESL,DDS_7 ; " movff bg_cnts_block,freq_1 ; Set denominator - counts per block clrf freq_0 ; " call Div24_16 ; Do the divide: ADC-counts/counts-per-block ; # blocks to display in DDS_7. Remainder to ; test for bar display in DDS_1 movff DDS_7,BCD_0 ; Move # blocks for conversion call Bin8BCD ; Convert result to BCD movlw CMD_2ND_LINE+D'9' ; Move cursor to LCD second line, call Cmnd2LCD ; character 10 movf BCD_1,w ; Get high order BCD result character bnz adc_res_z ; Character not a zero movlw ' ' ; Character is a zero call Data2LCD ; Send a space character to LCD char bra adc_res_next ; Go send lower order character adc_res_z addlw ASCII_NUM_BASE ; Convert high order character to ASCII call Data2LCD ; and send to display char 6 adc_res_next movf BCD_0,w ; Get BCD result - low order character addlw ASCII_NUM_BASE ; Convert result to ASCII call Data2LCD ; and display on LCD adc_disp_s movlw CMD_2ND_LINE+D'11' ; Move cursor to start of ADC display call Cmnd2LCD ; " movlw D'5' ; Set the display blank counter to 5, movwf count ; used to blank out remaining bar movlw D'1' ; Set the block number to compare movwf temp ; " movf DDS_7,w ; Get the result. bz adc_chk_remain ; Zero result, check remainder adc_chk_loop movf temp,w ; Get number to check subwf DDS_7,w ; Subtract (Result - block number) bz adc_remain ; Result = block, go test remainder movlw H'04' ; Result > block, send block call Data2LCD dcfsnz count,f ; Decrement the blank counter bra adc_exit ; Blank counter zero, all done incf temp,f ; Increment to next block number bra adc_chk_loop ; Do it again adc_remain movlw H'04' ; First must send a full block call Data2LCD ; to the LCD dcfsnz count,f ; and decrement blank counter bra adc_exit ; Display at last block, exit adc_chk_remain movf DDS_1,w ; Get MSByte of the remainder bz adc_blank ; Remainder is zero, go to blank fill movlw D'3' ; Test for 20% bar cpfslt DDS_1 ; Is remainder < 3? bra adc_chk_40 ; No movlw H'00' ; Yes, send 20% bar to LCD call Data2LCD ; " dcfsnz count,f ; Decrement the blank counter bra adc_exit ; Blank counter at zero, exit bra adc_blank ; Not zero, blank remainder of display adc_chk_40 movlw D'5' ; Test for 40% bar cpfslt DDS_1 ; Is remainder < 5? bra adc_chk_60 ; No movlw H'01' ; Yes, send 40% bar to LCD call Data2LCD ; " dcfsnz count,f ; Decrement blank counter bra adc_exit ; Blank counter at zero, exit bra adc_blank ; Not zero, blank remainder of display adc_chk_60 movlw D'7' ; Test for 60% bar cpfslt DDS_1 ; Is remainder < 7? bra adc_chk_80 ; No movlw H'02' ; Yes, send 60% bar to LCD call Data2LCD ; " dcfsnz count,f ; Decrement blank counter bra adc_exit ; Blank counter at zero, exit bra adc_blank ; Not zero, blank display adc_chk_80 movlw H'03' ; Must be 80% bar, send to LCD call Data2LCD ; " dcfsnz count,f ; Decrement blank counter bra adc_exit ; Blank counter at zero, exit adc_blank movlw ' ' ; Get a space character call Data2LCD ; and send to display decfsz count,f ; Decrement the counter bra adc_blank ; Not done, do it again adc_exit call Set_cursor_pos ; Return entry marker to frequency display goto Main ; Done, back to Main ENDIF ; >>>>>>>>>>>>>>>>>>>>>>>>>> Receive Incremental Tuning <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;**************************************************************************** ; ; Purpose: Turns the Receive Incremental Tuning On or Off. Changes RIT ; step size. ; ; Input: RIT Switch (Menu Switch + VFO Switch) ; ; Output: Changes RIT state (On/Off) or adjust RIT Step Size ; ;***************************************************************************** Rit_control btfss MISC_flags,RTF ; In receive mode? goto Main ; No, get out btfsc MENU_flags,MENU ; Is main menu invoked? goto Main ; Yes, will have to wait until menu gone movlw PB_RPT_WAIT ; Setup the delay timer for 1 second movwf PB_wait_count ; rit_sw_rlse_wait call Wait_25ms ; Wait a bit before checking again movff PORTA,temp ; Get Port A comf temp ; Complement Port A (Active push button now High) movlw H'18' ; Just The two pushbuttons andwf temp,f ; Is either VFO or MENU switch still depressed bz rit_cont_proc ; No ; Else, test if delay has expired decfsz PB_wait_count,f ; Decrement wait period bra rit_sw_rlse_wait ; Keep looping until something happens ; 0.5 second delay has expired, bra rit_enter_menu ; RIT switch held for 1 second, enter menu rit_cont_proc btfsc RIT_flags,RIT_STEP ; Adjusting the RIT Step Size? bra rit_menu_exit ; Yes, so get out, operator done adjusting btfsc vfo_select,LSB ; No, Turn an RIT On/Off, is VFO-A active bra rit_vfob ; No, process VFO-B btfss RIT_flags,RIT_VFOA ; Is RIT active for this VFO? bra rit_vfoa_on ; No, turn RIT on bcf RIT_flags,RIT_VFOA ; Yes, reset active flag for VFO-A clrf rit_vfoa_0,1 ; Reset VFO-A RIT frequency clrf rit_vfoa_1,1 ; " bra rit_blank_disp ; Now go blank RIT display rit_vfoa_on bsf RIT_flags,RIT_VFOA ; Turn on VFO-A RIT bra rit_menu_disp_freq ; Display the frequency and exit rit_vfob btfss RIT_flags,RIT_VFOB ; Is RIT active for this VFO? bra rit_vfob_on ; No, turn RIT on bcf RIT_flags,RIT_VFOB ; Yes, reset active flag for VFO-B clrf rit_vfob_0,1 ; Reset VFO-B RIT frequency clrf rit_vfob_1,1 ; " rit_blank_disp movlw CMD_2ND_LINE+D'11' ; Move cursor to 2nd line call Cmnd2LCD ; Do it movlw blank_rit ; Get the message call Display_message1 ; Blank RIT display bra rit_cont_exit ; Now exit rit_vfob_on bsf RIT_flags,RIT_VFOB ; Turn on VFO-B RIT bra rit_menu_disp_freq ; Display the frequency and exit ; Enter RIT Menu rit_enter_menu bsf RIT_flags,RIT_STEP ; Set flag adjusting RIT Step size movlw CMD_2ND_LINE+D'4' ; Operator want's to change step size call Cmnd2LCD ; Position cursor movlw rit_step_msg ; Point at step size message call Display_message1 ; and display the message lfsr 2,rit_step_0 ; Point at data to be converted call Bin16BCD ; Convert to BCD movlw CMD_2ND_LINE+D'13' ; Position cursor for step display call Cmnd2LCD ; and send it clrf temp ; Clear the zero suppress flag movf BCD_1,w ; Get 100's BCD digit call Send_digit ; Send to display swapf BCD_0,w ; Get 10's digit call Send_digit ; and send to display movlw '0' ; 1's digit is always a zero call Data2LCD ; Send the zero rit_menu_rlse_wait call Wait_25ms ; Wait a bit before checking again movff PORTA,temp ; Get Port A comf temp ; Complement Port A (Active push button now High) movlw H'18' ; Just The two pushbuttons andwf temp ; Is either VFO or MENU switch still depressed bnz rit_menu_rlse_wait ; Yes, wait goto Main ; Go back to Main and wait for operator action ; In menu and RIT encoder turned ; Change RIT Step Size Rit_chg_step ; <<<<<<< Enter from RIT Encoder routine movlw D'10' ; Step change of 10 Hz btfss rit_last,RIT_DIR_BIT,1 ; Is direction up? bra rit_chg_step_down ; No, down addwf rit_step_0,f,1 ; Add to the RIT step value bnc rit_chg_step_save ; Step =< 250, OK movlw RIT_MAX_STEP movwf rit_step_0,1 bra rit_chg_step_save ; Display new step size and wait for operator rit_chg_step_down subwf rit_step_0,f,1 ; Subtract the step change. Wreg has value of 10. bnz rit_chg_step_save ; Step value is not zero movlw D'10' movwf rit_step_0,1 ; Step value is zero, restore RIT minimum step value rit_chg_step_save movlw saved_ritstep ; Save the RIT Step Size in SEPROM. Set write address movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'1' ; # of bytes movwf se_count ; Into counter lfsr 1,rit_step_0 ; Point at data to save call Se_write ; Write the step size to SEPROM ; Display the new RIT Step Size lfsr 2,rit_step_0 ; Point at data to be converted call Bin16BCD ; Convert to BCD movlw CMD_2ND_LINE+D'13' ; Position cursor for step display call Cmnd2LCD ; and send it clrf temp ; Clear the zero suppress flag movf BCD_1,w ; Get 100's BCD digit call Send_digit ; Send to display swapf BCD_0,w ; Get 10's digit call Send_digit ; and send to display movlw '0' ; 1's digit is always a zero call Data2LCD ; Send the zero bcf RIT_flags,RITENC ; Clear RIT encoder movement flag return ; Go back to Main ; Exit RIT Control rit_menu_exit bcf RIT_flags,RIT_STEP ; Clear RIT Step size flag bcf RIT_flags,RITENC ; and the RIT encoder movement flag movlw CMD_2ND_LINE+D'4' ; Move to cursor to start of RIT menu call Cmnd2LCD ; on LCD second line movlw blank_msg ; Point at blank message call Display_message ; Send to LCD movf RIT_flags,W ; Get RIT flags andlw RIT_ENA_MASK ; Is RIT active for either VFO? bz rit_cont_exit ; Neither VFO is enabled rit_menu_disp_freq call Rit_display ; A VFO has RIT enabled, display the frequency rit_cont_exit call Wait_25ms ; Wait a bit before checking again movff PORTA,temp ; Get Port A comf temp ; Complement Port A (Active push button now High) movlw H'18' ; Just The two pushbuttons andwf temp ; Is either VFO or MENU switch still depressed bnz rit_cont_exit ; Yes, wait for release call Disp_mode ; Refresh Mode display call Set_cursor_pos ; Return Entry Marker to frequency display ; There may have been a change of RIT on/off so, call Send_dds_freq ; recalculate frequency and send to DDS goto Main ; Done ; **************************************************************************** ; ; Purpose: This routine does the following ; 1. Reads the RIT Encoder bits until a change is detected, then ; determines the direction the knob was moved. ; 2. When movement of RIT Encoder is detected, Adds or Subtracts ; rit_step_0 to the RIT frequency. ; 3. Performs mechanical shaft encoder debounce and ; detent processing if so enabled. ; ; Input: Encoder input read from port C ; rit_old -> the last encoder bits read ; rit_last_dir -> the last direction moved ; ; Output: Encoder moved - add/subtract rit_step_0 to RIT frequency ; rit_new The current encoder bits ; rit_last_dir: The last direction (0 = down, 5 = up [AKA UP_BIT]) ; ;***************************************************************************** Rit_encoder btfss MISC_flags,RTF ; Receiving? return ; Transmitting, do nothing movlw B'00001011' ; Is a VFO or Menu enabled in RIT flags andwf RIT_flags,W ; Test the important bits bnz $+4 ; One of the flags is set return ; No flags set, back to Main ; Check the encoder for movement rit_encoder_proc movf PORTC,W ; Get PORT C andlw RIT_BITS ; Just the RIT encoder bits movwf rit_read,1 ; and save call Wait_1ms ; Debounce time movf PORTC,W ; Read PORT C again andlw RIT_BITS ; Just the encoder output cpfseq rit_read,1 ; and compare with previous value bra rit_encoder_proc ; Not equal, still bouncing, poll again movwf rit_new,1 ; Save new encoder value xorwf rit_old,w,1 ; Has it changed? bnz $+4 ; Encoder changed, continue return ; No change, go back to Main ; Else, Zero-flag is not set, so continue on ; It changed. Now determine which direction the encoder turned. ;=============================================================================P ; RIT Encoder bits are on RC4 and RC5. P ; A and B are "gray code" - 90 degrees out of phase (quadrature) P ; ___ ___ P ; | | | | P ; A ____| |___| |___ P ; ___ ___ P ; | | | | P ; B ___| |___| |___ P ; ^ ^ ^ ^ ^ ^ ^ ^ P ; a b c d a b c d P ; P ; A B P ; At point a: 0 0 P ; At point b: 1 0 P ; At point c: 1 1 P ; At point d: 0 1 P ; P ; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is: P ; 00, 10, 11, 01, 00, 10, 11, 01, etc. P ; P ; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is: P ; 01, 11, 10, 00, 01, 11, 10, 00, etc. P ; P ; To determine if the sequence is UP or DOWN: P ; 1) Take the "Right-Bit" of any pair. P ; 2) XOR it with the "Left-Bit" of the next pair in the sequence. P ; 3) If the result is 1 it is UP P ; If the result is 0 it is DOWN P ; P ; The direction flag is 0 (DOWN) or 5 (UP) because of bit positioning P ;=============================================================================P bcf STATUS,C ; Clear the carry bit to prepare for rotate rlcf rit_old,f,1 ; Rotate old bits left to align "Right-Bit" movf rit_new,w,1 ; Set up new bits in W xorwf rit_old,f,1 ; XOR old (left shifted) with new bits movf rit_old,w,1 ; Put XOR results into W also andlw RIT_UP_BIT ; Mask to look at only "Left-Bit" of pair movwf rit_next,1 ; Save result (in W) as direction (bit=UP) xorwf rit_last,w,1 ; See if direction is same as before. ; ; Prevent encoder slip from giving a false change in direction. ; bz rit_continue ; Yes, same direction so no slip; keep going movff rit_next,rit_last ; No Zero-flag, so direction changed update ; the direction indicator movff rit_new,rit_old ; Save the current encoder bits for next time bra rit_encoder_proc ; Try again rit_continue clrf rit_last,1 ; Clear last direction (default is DN) btfss rit_old,RIT_DIR_BIT,1 ; Are we going DOWN? bra $+6 ; Yes, go process it. movlw RIT_UP_BIT ; No, get UP value movwf rit_last,1 ; and set in last direction movff rit_new,rit_old ; Get the current encoder bits and save for next time decf rit_counter,f,1 ; decrement the inter-detent counter btfsc rit_counter,0,1 ; Check if bit 0 is cleared bra rit_encoder_proc ; If not, then poll some more btfsc rit_counter,1,1 ; Check if bit 1 is cleared bra rit_encoder_proc ; If not, then poll some more btfss RIT_flags,RIT_STEP ; Is the RIT Step size being adjusted? bra $+4 ; No, continue processing encoder movement goto Rit_chg_step ; Go change the step size lfsr 2,rit_vfoa_0 ; Get pointer for VFO-A RIT freq in case A is active btfsc vfo_select,LSB ; VFO-A active? bra $+8 ; No, VFO-B is active btfsc RIT_flags,RIT_VFOA ; Is RIT on for VFO-A? bra rit_enc_cont ; Yes, continue return ; No, go back to Main btfss RIT_flags,RIT_VFOB ; VFO-B is active, is RIT enabled for "B" return ; No, go back to Main lfsr 2,rit_vfob_0 ; Yes, get the pointer to VFO-B RIT freq rit_enc_cont btfsc rit_last,RIT_DIR_BIT,1 ; Are we going DOWN? bra rit_up ; No, going UP, go precess ; Decrement the RIT frequency movf rit_step_0,w,1 ; Get RIT change value subwf POSTINC2,f ; Subtract from rit_freq_0 movlw H'00' ; Second byte to subtract subwfb INDF2,f ; Subtract from rit_freq_1 bra rit_math_done rit_up ; RIT Encoder noving CW, Up movf rit_step_0,w,1 ; Get RIT change value addwf POSTINC2,f ; Add to rit_freq_0 movlw D'0' ; W is zero addwfc INDF2,f ; Add to rit_freq_1 rit_math_done ; Check that 5,000 Hz has not been exceeded. btfsc INDF2,MSB ; Is the RIT frequency negative? bra rit_negative ; Yes, go check lower limit movf POSTDEC2,w ; Get rit_freq_1 sublw H'13' ; Subtract MSByte of 5,000 - rit_freq_1 bz rit_chk_next ; rit_freq_1 = MSByte of 5,000 bc rit_disp_freq ; rit_freq_1 < MSByte of 5,000 bn rit_set_maxp ; rit_freq_1 > MSByte of 5.000 rit_chk_next movf INDF2,w ; Lower byte of RIT frequency sublw H'88' ; Subtract LSByte of 5,000 - rit_freq_0 bz rit_disp_freq ; rit_freq_0 = LSByte of 5,000 bc rit_disp_freq ; rit_freq_0 < LSByte of 5,000 rit_set_maxp ; rit_freq_0 > LSByte of 5,000 movlw H'88' ; " movwf POSTINC2 ; rit_freq_0 movlw H'13' movwf INDF2 ; rit_freq_1 bra rit_disp_freq rit_negative movf POSTDEC2,w ; Get rit_freq_1 sublw H'EC' ; H'13' - rit_freq_1 bz rit_chk_nextn ; They are equal bn rit_disp_freq ; rit_freq_1 < MSByte of "5,000" bc rit_set_maxn ; rit_freq_1 > MSByte of "5,000" rit_chk_nextn movf INDF2,w ; LSByte of RIT frequency sublw H'78' ; LSByte "5,000" - RIT frequency LSByte bz rit_disp_freq ; They are equal bn rit_disp_freq ; rit_freq_0 < LSByte of "5,000" rit_set_maxn movlw H'78' ; Set maximum negative value "-5,000" movwf POSTINC2 ; " movlw H'EC' ; " movwf INDF2 ; " rit_disp_freq call Rit_display ; Display the new RIT frequency call Send_dds_freq ; Send new frequency to the DDS call Set_cursor_pos ; Return Entry Marker to frequency display return ; **************************************************************************** ; ; Purpose: This routine displays the RIT frequency ; ; Input: File Select register 2 points at two byte frequency, LSByte ; ; Output: RIT frequency is displayed ; ;***************************************************************************** Rit_display btfss MISC_flags,RTF ; Is Transmit in progress? return ; Yes, do not display btfsc MENU_flags,MENU ; Is main menu active? return ; Yes, do nothing, go back btfsc vfo_select,LSB ; Is VFO-A active? bra rit_disp_vfob ; No, check VFO-B btfss RIT_flags,RIT_VFOA ; Yes, Is RIT enabled for VFO-A? return ; No, go back lfsr 2,rit_vfoa_0 ; Yes, load FSR2 with data pointer bra Rit_disp_go ; Go display RIT rit_disp_vfob ; VFO-B active btfss RIT_flags,RIT_VFOB ; Is RIT enabled for VFO-B? return ; No, go back lfsr 2,rit_vfob_0 ; Yes, load FSR2 with VFO-B data pointer Rit_disp_go ; <<<<<< Entry point from RIT control movlw CMD_2ND_LINE+D'10' ; Move LCD cursor to correct position call Cmnd2LCD ; Move the cursor movlw ' ' ; Get a space character call Data2LCD ; and blank Bar Graph movff POSTINC2,BCD_2 ; Move the RIT frequency to work area movff INDF2,BCD_3 ; " movlw '+' ; Get a plus sign in anticiaption btfss BCD_3,MSB ; Is RIT frequency negative bra rit_disp_pos ; No comf BCD_2 ; Frequency is negative so must complement comf BCD_3 ; for the display infsnz BCD_2,f ; Add one to complete 2's complement incf BCD_3,f ; Add carry movlw '-' ; Get ASCII minus sign rit_disp_pos call Data2LCD ; Send correct sign lfsr 2,BCD_2 ; Point at RIT frequency data call Bin16BCD ; Convert HEX to BCD. FSR2 points at data source clrf temp swapf BCD_1,w ; Get first BCD digit call Send_digit ; Display high order digit movf BCD_1,w ; Get next lower BCD digit call Send_digit ; Go display character swapf BCD_0,w ; Next lower BCD digit call Send_digit ; Go display character movlw '0' ; The last character is always a zero call Data2LCD ; Display the zero return ; All done, go back ;>>>>>>>>>>>>>>>>>>>>>>>>> VFO OPERATIONS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;**************************************************************************** ; ; Purpose: This function copies the contents of the VFO A registers ; into the VFO B registers ; ; Inputs: VFO-A registers ; ; Outputs: VFO-B registers ; ;**************************************************************************** Copy_a_to_b movff vfo_a_0,vfo_b_0 ; Copy all 8 bytes of VFO A information movff vfo_a_1,vfo_b_1 ; into VFO B movff vfo_a_2,vfo_b_2 movff vfo_a_3,vfo_b_3 movff vfo_a_band,vfo_b_band movff decade_a,decade_b movff mode_a,mode_b return ;**************************************************************************** ; ; Purpose: This function copies the contents of the VFO B registers ; into the VFO A registers ; ; Inputs: VFO-B registers ; ; Outputs: VFO-A registers ; ;**************************************************************************** Copy_b_to_a movff vfo_b_0,vfo_a_0 ; Copy all 8 bytes of VFO B information movff vfo_b_1,vfo_a_1 ; into VFO A movff vfo_b_2,vfo_a_2 movff vfo_b_3,vfo_a_3 movff vfo_b_band,vfo_a_band movff decade_b,decade_a movff mode_b,mode_a return ;**************************************************************************** ; ; Purpose: This function swaps the active vfo. ; ; Inputs: vfo_select ; ; Outputs: Vfo's are swapped. Limits registers are set for the VFO. ; ;**************************************************************************** Swap_vfo ; <<<<<<<<<<<<<<< Entry Point <<<<<<<<<<<<< btfss vfo_select,VFO_SEL ; Determine which vfo is active bra swap_vfo_b ; A is active, go to B ; Was B, switch to A movff decade,decade_b ; First save the decade for VFO-B movff MODE_flags,mode_b ; Save mode for VFO-B bcf vfo_select,VFO_SEL ; Select VFO-A movlw vfo_a_0 ; Get address of vfo_A_0 movwf vfo_pointer ; Set up vfo pointer register movff decade_a,decade ; New VFO is A, move saved decade to work decade movff mode_a,MODE_flags ; Move VFO-A mode to working movf vfo_a_band,w ; Get VFO-A band mullw BAND_LGTH ; Multiply by band length to get band index movff PRODL,band_index ; and set band index for VFO-A call Set_limit_regs ; Set limit registers for the VFO return ; Finished, exit swap_vfo_b ; Was A, switch to VFO-B movff decade,decade_a ; Save decade for VFO-A movff MODE_flags,mode_a ; Save mode for VFO-A bsf vfo_select,VFO_SEL ; Select VFO B movlw vfo_b_0 ; Get address of vfo_b_0 movwf vfo_pointer ; Set up vfo pointer register movff decade_b,decade ; Move VFO-B saved decade to work decade movff mode_b,MODE_flags ; Restore VFO-B working mode movf vfo_b_band,w ; Get VFO-B band mullw BAND_LGTH ; Multiply by band length to get band index movff PRODL,band_index ; and set band index for VFO-B call Set_limit_regs ; Set limit registers for the VFO return ; Finished, exit ; **************************************************************************** ; ; Purpose: This routine will display selected VFO on 2nd line of LCD. If ; split operation, will append RCV or XMT depending on vfo. ; ; Input: vfo_select ; ; Output: "A" or "B" for VFO displayed on 2nd line of LCD ; ;***************************************************************************** Disp_vfo movlw CMD_2ND_LINE ; Point at digit 0 of second line call Cmnd2LCD ; send command to the LCD btfss vfo_select,VFO_SEL ; Determine which vfo is active bra disp_vfoa ; Its A movlw 'B' ; No, its "B", get "B" character call Data2LCD ; and display it btfss MODE_flags,SPLIT ; Split operation? return movlw xmt_msg ; Yes, point to transmit message call Display_message return disp_vfoa movlw 'A' ; Get an "A" character call Data2LCD ; and send to LCD btfss MODE_flags,SPLIT ; Split operation? return movlw rcv_msg ; Pointer to receive message call Display_message return ; Go back ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>DDS OPERATIONS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; ***************************************************************************** ; * * ; * Purpose: Initialise the AD9912 DDS. * ; * * ; * Input: None. * ; * * ; * Output: AD9912 is ready to go. * ; * * ; ***************************************************************************** ; ***************************************************************************** ; * Communication with the AD9912 is serial. The AD9912 requires an * ; * Instruction that is two bytes prior to the data. The program sends the * ; * Instruction bytes and then the required number of data bytes. * ; * Transmission to the AD9912 is Most Significant Byte, Most Significant * ; * Bit first. * ; * Information sent to DDS is put in DDS_7 thru DDS_0. DDS_7 & DDS_6 are the * ; * instruction bytes. The remaining bytes are sent, as required. * ; ***************************************************************************** DDS_init ; Is this the first time through startup after firmware burn? btfss PWR_flags,NOT_1ST ; Have we been through startup before? bra dds_not_saved ; No, go calculate clock ratio and set AD9912 PD&R reg ; Read the DDS Power-Down and Reset register data from SEPROM movlw dds_pdr_save ; Location of register in SEPROM movwf seprom_a_0,1 ; Set SEPROM write address clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'1' ; Count of 1 byte movwf se_count ; to byte counter lfsr 1,dds_pdr_reg ; Point at data receive location call Se_read ; Read register data from SEPROM call Send_pd_reset ; Send PDR register to AD9912 ; ; Read clock ratio values from SEPROM ; lfsr 1,clk_rat_0 ; Point at clock ratio bytes in RAM movlw clk_rat_save ; Point at save location in SEPROM movwf seprom_a_0,1 ; Set SEPROM address for Clock Ratio clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'6' ; Number of bytes to read movwf se_count ; into counter call Se_read ; Read from SEPROM bra dds_init_cont ; Do remainder of AD9912 init dds_not_saved ; First time through start up after firmware burn. ; Calculate clock ratio value and enter in SEPROM and Access Bank Ram ; call Calc_clk_ratio ; Go calculate clock ratio ; Register 0010 - 1 byte <----------------------------Power Down & Reset ; NOTE: CMOS Driver is not used and is always powered down. ; HSTL Driver and Output Doubler is powered up at startup. clrf dds_pdr_reg ; Clear the working register movlw CLK_MULT ; Get the clock multiplier movwf temp ; Put in working reg movlw D'1' ; Is multiplier "1", no multiplication? cpfseq temp bra pll_used ; There is a multiplier, do not power down PLL bsf dds_pdr_reg,4 ; No multiplier, power down DDS SYSCLOCK PLL pll_used bcf dds_pdr_reg,7 ; Power on HSTL bcf dds_pdr_reg,6 ; Turn off CMOS Output bsf dds_pdr_reg,5 ; Power on the Output Doubler call Send_pd_reset ; Send Power-Down & Reset to AD9912 ; ; Save the DDS Power-Down & Reset register in SEPROM. This will be used on subsequent ; power-up. ; movlw dds_pdr_save ; Point at save location in SEPROM movwf seprom_a_0,1 ; Set the SEPROM write address clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " movlw D'1' ; Number of bytes to write movwf se_count ; into the counter lfsr 1,dds_pdr_reg ; Point at data to save call Se_write ; and save the register data dds_init_cont ; Register 0020 - 1 byte <---------------- N Divider - Depends on Clock Multiplier btfsc dds_pdr_reg,4 ; Is the SYSCLOCK PLL powered down? bra mult_zero ; Yes, do not set N-divider movlw H'20' ; No, Set the Instruction address movwf DDS_6 ; Into instruction clrf DDS_7 ; " movlw CLK_MULT ; Get Clock multiplier movwf temp ; Save the multiplier bcf STATUS,C ; Clear Carry indicator rrncf temp,f ; Divide multiplier by 2 movlw D'2' ; Now need to subtract two subwf temp,w ; Subtract two movwf DDS_5 ; Move N divider to data to send to DDS movlw DDS_5 ; Set stop control for DDS send movwf send_dds_stop ; " call Send_dds_cfr ; Send register to AD9912 mult_zero ; Register 0022 - 1 byte <---------------- PLL Parameters -Depends on PLL options movlw H'22' ; Instruction address movwf DDS_6 ; Into instruction clrf DDS_7 ; " btfss PWR_flags,VCO_HI ; Is VCO High bit set in Pwr flags? bra vco_low_? ; No movlw H'04' ; Yes, set VCO high range, Charge pump default bra send_reg_22 ; Disable clock doubler vco_low_? btfss PWR_flags,VCO_LOW ; Is VCO Low bit set? bra vco_auto ; No movlw H'00' ; Set VCO low range bra send_reg_22 vco_auto movlw H'80' ; Must be VCO auto range send_reg_22 movwf DDS_5 ; Put in data to send to DDS call Send_dds_cfr ; and send it return ; Back to caller ; AD9912 has been initialized. ; ***************************************************************************** ; * * ; * Purpose: Two entry points. Send_dds_freq calculates the required VFO * ; * frequency, calculates the FTW and sends that FTW to the AD9912. * ; * Send_dds_cfr sends a Control Register to the AD9912. * ; * Transfer to the AD9912 is MSByte, MSBit. * ; * * ; * Input: vfo_X_0..3, CW offset, RIT frequency. * ; send_dds_stop initialized to point to last byte to send. * ; * * ; * Output: The AD9912 DDS is updated. * ; * * ; ***************************************************************************** Send_dds_freq ; <<<<<<<<<<<<<< Frequency Tuning Word Entry point. ; Calculate the DDS Frequency Tuning Word (FTW). If in CW mode, and receive ; mode, add CW Offset to frequency. If RIT enabled, and receive mode, add ; the RIT frequency. Multiply frequency by 2 or 8. Multiply the 32 bit number ; of frequency times the 48 bit number of clock ratio. ; ; Input: The clock ratio value in clk_rat_5 ... clk_rat_0 and the ; current frequency stored in vfo_X_3 ... vfo_X_0. If CW Mode the ; CW Offset. If RIT, RIT frequency. ; ; Output: The result is stored in DDS_5 ... DDS_0. ; Calc_dds_ftw ; Do not use vfo_X_0..3 but copy the active vfo frequency to freq_0..3 movff vfo_pointer,FSR0L ; Get the pointer to vfo_X_0 into FSR0 clrf FSR0H movff POSTINC0,freq_0 ; Get vfo_X_0 and store it for calculation movff POSTINC0,freq_1 ; vfo_X_1 movff POSTINC0,freq_2 ; vfo_X_2 movff INDF0,freq_3 ; vfo_X_3 ; If this calculate is for calibrate routine, bypass CW mode, RIT and ; quadrature calculations. btfsc MISC_flags,PCAL_FLG ; Check if in calibrate mode bra calc_dds_mult_byp ; Yes, go directly to FTW calcualtion ;Test for CW Offset addition to frequency. btfss MISC_flags,RTF ; Check R/T - Receiving? bra calc_freq_done ; No, transmitting, bypass adding CW offset & RIT btfss DDS_flags,CW_NG ; Is CW Negative flag high? bra calc_cwpos ; No lfsr 2,cwoffs_neg_0 ; Yes point at negative offset lfsr 1,freq_0 ; Pointer to frequency movf POSTINC2,w ; Get cwoffs_0 addwf POSTINC1,f ; Add freq__0 movf INDF2,w ; Get cwoffs_1 byte addwfc POSTINC1,f ; and add to freq_1 movlw H'FF' ; set W to -1 and add any carry through addwfc POSTINC1,f ; Add carry to freq_2 addwfc INDF1,f ; Add carry to freq_3 bra calc_rit_chk ; Continue to RIT processing calc_cwpos btfss DDS_flags,CW_PL ; Is CW Plus flag set? bra calc_rit_chk ; No, must be non-CW mode, no CW offset lfsr 2,cwoffs_pos_0 ; Yes, point at positive offset lfsr 1,freq_0 ; Pointer to frequency movf POSTINC2,w ; Get cwoffs_0 addwf POSTINC1,f ; Add freq_0 movf INDF2,w ; Get cwoffs_1 byte addwfc POSTINC1,f ; and add to freq_1 clrf WREG ; Clear W and add any carry through addwfc POSTINC1,f ; Add carry to freq_2 addwfc INDF1,f ; Add carry to freq_3 calc_rit_chk ; ; Check if RIT enabled. If yes, add RIT frequency. ; movlw RIT_ENA_MASK ; Mask for RIT active flags andwf RIT_flags,W ; Is either RIT enabled bit set bz calc_freq_done ; No RIT, continue btfsc vfo_select,LSB ; Which VFO is active? bra calc_rit_vfob ; Its B, go check VFO-B btfss RIT_flags,RIT_VFOA ; Is RIT enabled for VFO-A? bra calc_freq_done ; No, bypass RIT calculation lfsr 1,rit_vfoa_0 ; Yes, point at RIT frequency for VFO-A movf POSTINC1,w ; Get first byte of RIT frequency addwf freq_0,f ; Add to freq_0 movf INDF1,w ; Get second byte of RIT frequency addwfc freq_1,f ; and add to freq_1 movlw D'0' ; Get ready if RIT is not negative addwfc freq_2,f ; Add to freq_2 addwfc freq_3,f ; Add to freq_3 bra calc_freq_done ; Done RIT calcualtion calc_rit_vfob btfss RIT_flags,RIT_VFOB ; Is RIT enabled for VFO-B? bra calc_freq_done ; No, bypass RIT calculation lfsr 1,rit_vfob_0 ; Yes, point at RIT frequency for VFO-B movf POSTINC1,w ; Get first byte of RIT frequency addwf freq_0,f ; Add to freq_0 movf INDF1,w ; Get second byte of RIT frequency addwfc freq_1,f ; and add to freq_1 movlw H'FF' ; Yes, load -1 addwfc freq_2,f ; Add to freq_2 addwfc freq_3,f ; Add to freq_3 calc_freq_done ; ; The required output frequency is now in freq_3..0 ; ; If frequency, about to be sent to the DDS, is ABOVE 200MHz, set flag so ; HSTL Output Doubler will be enabled. If the frequency is BELOW 200 MHz, ; reset flag to disable HSTL Output Doubler. movlw H'0A' ; MSByte of 200 MHz- into W (0x0B-1) cpfsgt freq_3 ; Is freq_3 > ? bra doubler_off ; No, turn doubler off movlw H'EA' ; Next LSByte of 200 MHz- into W (0xEB-1) cpfsgt freq_2 ; Is freq_2 > ? bra doubler_off ; No, turn off doubler movlw H'C1' ; Next LSByte of 200 MHz- into W (0xC2-1) cpfsgt freq_1 ; Is freq_1 > ? bra doubler_off ; No, turn off doubler ; LSByte of 200 MHz is zero. If the above tests are completed the frequency is ; 200 MHz or above. bsf DDS_flags,HSTL_DBL ; Enable HSTL Output Doubler bra calc_dds_mult_byp ; Bypass any multiplication, HSTL Output Doubler does it. doubler_off ; Frequency is below 200 MHz bcf DDS_flags,HSTL_DBL ; Disable HSTL Output Doubler ; Frequency not above 200 MHz. ; Now check to see if the frequency is below 10 MHz which requires that it be ; multiplied by 8. tstfsz freq_3 ; Test MSByte of frequency for zero bra calc_dds_X4_byp ; No, frequency must be above 10 MHz movf freq_2,w ; Get 2nd MSByte of frequency sublw H'98' ; Subtract 2nd MSByte of 10 MHz K - W bz $+6 ; = Check next lower byte bc calc_dds_X4 ; Frequency < 10 MHz bn calc_dds_X4_byp ; Frequency > 10 MHz movf freq_1,w ; Get 3rd MSByte 0f 10 MHz sublw H'96' ; Subtract 3rd MSByte of 10 MHz bz $+6 ; = Check next lower byte bc calc_dds_X4 ; Frequency < 10 MHz bn calc_dds_X4_byp ; Frequency > 10 MHz movf freq_0,w ; LSByte of frequency sublw H'81' ; Subtract LSByte of 10 MHz bz calc_dds_X4_byp ; = Multiply by 2 bn calc_dds_X4_byp ; Frequency > 10 MHz calc_dds_X4 ; Multiply by 4 by shifting frequency left 2 bits. bcf STATUS,C ; Clear carry indicator rlcf freq_0,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlcf freq_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_3,f ; Rotate left, Carry->LS bit, MS bit->Carry bcf STATUS,C ; Clear carry indicator rlcf freq_0,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlcf freq_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_3,f ; Rotate left, Carry->LS bit, MS bit->Carry bsf DDS_flags,DIV_BY8 ; Set flag so Quad Generator divides by 8 bra $+4 calc_dds_X4_byp bcf DDS_flags,DIV_BY8 ; Reset flag so Quad Generator divides by 2 ; Multiply freq by 2 by rotating left once. Quadrature generation will divide ; by 2 or by 8. bcf STATUS,C ; Clear carry bit rlcf freq_0,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlcf freq_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlcf freq_3,f ; Rotate left, Carry->LS bit, MS bit->Carry ; ; The final frequency has been calculated and controls set. Now generate the DDS FTW. calc_dds_mult_byp clrf DDS_0 ; Clear the DDS bytes for multiply clrf DDS_1 ; " clrf DDS_2 ; " clrf DDS_3 ; " clrf DDS_4 ; " clrf DDS_5 ; " clrf DDS_6 ; " ; ; Calculate the DDS FTW by multiplying clock ratio in clk_rat_0..5 ; by the frequency in freq_0..3 movlw D'32' ; Set count to 32 (4 freq_X bytes of 8 bits) movwf count ; Set counter mult_loop lfsr 1,clk_rat_5 ; Point at clock ratio bcf STATUS,C ; Clear the carry btfss freq_0,0 ; Is bit 0 (Least Significant bit) of multiplier set? bra noAdd ; No, don't need to add clock ratio term to total movf POSTDEC1,w ; Yes, get the clock ratio 5 term addwf DDS_1,f ; and add it in to total movf POSTDEC1,w ; Then clock ratio 4 term addwfc DDS_2,f ; and add movf POSTDEC1,w ; ETC. addwfc DDS_3,f movf POSTDEC1,w addwfc DDS_4,f movf POSTDEC1,w addwfc DDS_5,f movf INDF1,w addwfc DDS_6,f noAdd rrcf DDS_6,f ; Shift carry out into next lower byte rrcf DDS_5,f ; rrcf DDS_4,f ; rrcf DDS_3,f ; rrcf DDS_2,f ; rrcf DDS_1,f ; rrcf DDS_0,f ; rrcf freq_3,f ; Shift next multiplier bit into position rrcf freq_2,f ; Rotate bits to right from byte to byte rrcf freq_1,f ; rrcf freq_0,f ; decfsz count,f ; One more bit has been done. Are we done? goto mult_loop ; No, go back to use next bit ; Multiply answer is in bytes DDS_5...0 ; Now that the time consuming math is completed, check to see if any DDS controls ; must be changed. First, is HSTL Output Doubler to be enabled or disabled. ; Second, is the divide by 4, on quadrature PCB, to be enabled or disabled. btfss DDS_flags,HSTL_DBL ; Is HSTL doubler supposed to be enabled? bra calc_dds_no_dblr ; No btfsc DDS_flags,REM_DBLR ; Yes, was frequency previously above 200 MHz bra calc_dds_dblr_exit ; Yes, exit bsf DDS_flags,REM_DBLR ; No, set flag, remember doubler is enabled movlw B'00000110' ; Enable Output Doubler bra calc_dds_chg_dblr ; Go enable output doubler calc_dds_no_dblr ; Frequency is below 200 MHz btfss DDS_flags,REM_DBLR ; Was frequency previously below 200MHz bra calc_dds_dblr_exit ; Yes, exit bcf DDS_flags,REM_DBLR ; No, remember doubler is disabled movlw B'00000101' ; Disable Doubler Output calc_dds_chg_dblr movff DDS_5,temp ; Save part of the FTW about to be destroyed movwf DDS_5 ; Data to be sent to AD9912 movlw H'02' ; Address of the AD9912 Doubler Driver register movwf DDS_7 ; 0x0200 clrf DDS_6 ; " movlw DDS_5 ; Set the stop sending to DDS location movwf send_dds_stop ; " call Send_dds_cfr ; Send enable or Disable to DDS Doubler Driver movff temp,DDS_5 ; Restore the FTW calc_dds_dblr_exit bcf PORTC,DIV_BY4 ; Turn off divide by 4 signal to quad PCB btfss DDS_flags,DIV_BY8 ; Is flag set to divide by 8? bra $+4 ; No bsf PORTC,DIV_BY4 ; Yes, turn on divide by 4 signal to quad PCB. ; Now send the FTW to AD9912. ; ; NOTE: The AD9912 serial data transfer can be set for byte lengths of 1, 2, 3 ; bytes or streaming mode (not counting the Instruction bytes). The ; length is set in Instruction MSByte, Bits 6 and 5. The FTW is 6 bytes ; and streaming mode would be a good way to send the data. However, ; streaming mode requires control of the Chip Select (CSB). To free-up ; a pin on the PIC micrcontroller, CSB is strapped to ground, always ; enabled. Therefore, FTW data transfer is split into two 3 byte ; transfers. ; movlw DDS_3 ; Stop after 5 bytes sent (2 Instruction + movwf send_dds_stop ; 3 FTW bytes). movlw H'41' ; Start at 9912 register 01AB. Use 3 byte movwf DDS_7 ; mode. Store in data to send to DDS movlw H'AB' ; Remainder of register address movwf DDS_6 ; into data to send. bsf DDS_flags,FTW_BIT ; Set indicator this is a FTW transfer (MSB) bsf DDS_flags,FFTW_BIT ; Set indicator this is first half of FTW send Send_dds_cfr ; <<<<<<<<<<<< Entry point for Control Register Transfer ; DDS_7..._X must contain Address and data ; send_dds_stop must be initialized to stop transfer lfsr 1,DDS_7 ; Pointer to start of send data next_byte movff POSTDEC1,byte2send ; Get byte to send bsf STATUS,C ; Set `end of loop' flag rlcf byte2send,f ; Place first bit into Carry dds_send_loop bcf PORTB,DDS_SDIO ; Precondition DDS serial data to a zero btfsc STATUS,C ; Check data - 0 or 1? bsf PORTB,DDS_SDIO ; Send one bsf PORTB,DDS_SCLK ; Raise DDS serial clock bcf PORTB,DDS_SCLK ; and drop the clock bcf STATUS,C ; Clear data in Carry rlcf byte2send,F ; Place next bit into Carry tstfsz byte2send ; Test is data byte is all zeros bra dds_send_loop ; No, send next bit movf send_dds_stop,w ; Get pointer to last word subwf FSR1L,w ; bnn next_byte ; All data not sent, do next byte btfss DDS_flags,FTW_BIT ; Is this an FTW transfer? bra send_dds_exit ; No, exit btfss DDS_flags,FFTW_BIT ; Is first half flag set? bra last_half_ftw ; No, send last half of FTW bcf DDS_flags,FFTW_BIT ; Yes, clear first half FTW flag movlw DDS_6 ; Stop send after register address sent movwf send_dds_stop ; " movlw D'3' ; Decrement DDS register address by three subwf DDS_6 ; for three bytes already sent bra Send_dds_cfr ; Now send the register address last_half_ftw movlw D'3' ; Subtract three from FSR1 to point to next subwf FSR1L,f ; byte to be sent (DDS_2) movlw DDS_0 ; Set the stop control (DDS_0) movwf send_dds_stop bcf DDS_flags,FTW_BIT ; Clear FTW being sent bit (exit next time thru) bra next_byte ; Send last three bytes of FTW send_dds_exit bsf PORTB,DDS_UPDATE ; Send I/O Update to the DDS bcf PORTB,DDS_UPDATE ; and drop update clrf PORTB return ; Exit ; *************************************************************************** ; ; Purpose: Send the Power-Down & Reset register data to the AD9912 ; ; Input: dds_pdr_reg, the register contents. ; ; Output: Register data sent to AD9912 ; ; *************************************************************************** Send_pd_reset movlw H'10' ; Initialize AD9912 register 0010 movwf DDS_6 ; Instruction bytes clrf DDS_7 ; " movff dds_pdr_reg,DDS_5 ; Set data to send to DDS movlw DDS_5 ; Point at last byte to send. movwf send_dds_stop ; " call Send_dds_cfr ; Send register to AD9912 return ; Finished, go back ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LCD OPERATIONS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; ***************************************************************************** ; * * ; * 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 call Wait_15ms ; Wait for LCD to power up call Wait_15ms ; More wait time - total 30 milliseconds movlw CMD_RESET_LCD ; LCD init instruction (First) Clear display, return home movwf PORTB ; Send to LCD. 0x03 bsf PORTB,LCD_E ; Raise "E" nop ; delay, nop ; delay, bcf PORTB,LCD_E ; then Clear E call Wait_5ms ; Wait for command to complete movlw CMD_RESET_LCD ; LCD init instruction (Second) movwf PORTB bsf PORTB,LCD_E ; Raise E nop ; delay, nop ; delay bcf PORTB,LCD_E ; Again clear E call Wait_.5ms ; Wait for LCD to complete command movlw CMD_RESET_LCD ; LCD init instruction (Third) movwf PORTB bsf PORTB,LCD_E ; Raise E nop ; delay, nop ; delay bcf PORTB,LCD_E ; Clear E call Wait_.5ms ; Wait for command to complete movlw CMD_IFC1 ; Return cursor home. 0x02 movwf PORTB bsf PORTB,LCD_E ; Raise E nop ; delay, nop ; delay bcf PORTB,LCD_E ; Clear E call Wait_.5ms ; Wait for LCD to complete command movlw CMD_IFC2 ; Set 4 Bit mode, 2 Line, 5x7 Dots Char. 0x28 call Cmnd2LCD ; Send command in w to LCD movlw CMD_DISPLAY_OFF ; Display off, cursor and blink off. 0x08 call Cmnd2LCD ; Send command to LCD movlw CMD_CLEAR_LCD ; Clear and reset cursor. 0x01 call Cmnd2LCD ; Send command in w to LCD call Wait_15ms ; Wait for LCD to finish command movlw CMD_CURSOR_RT ; Set cursor to move right, no display shift. 0x06 call Cmnd2LCD ; Send command in w to LCD movlw CMD_DISPLAY_ON ; Display on, cursor on, blink off. 0x04 call Cmnd2LCD ; Send command to LCD ; ; Basic LCD init is finished. ; Set up the required new characters in Character Generator Ram of the LCD. ; The characters are vertical bars used in the ADC display. ; movlw H'40' ; Set CGRAM address to character 00 call Cmnd2LCD ; " movlw B'00010000' ; Data to write to CGRAM, 20% bar -char 00 call wri_cgram ; Write 20% bar character to CGRAM of LCD movlw B'00011000' ; Next new character, 40% bar - char 01 call wri_cgram ; Write new character to CGRAM of LCD movlw B'00011100' ; Next new character, 60% bar - char 02 call wri_cgram ; Write new character to CGRAM of LCD movlw B'00011110' ; Next new character, 80% bar - char 03 call wri_cgram ; Write new character to CGRAM of LCD movlw B'00011111' ; Next new character, 100% block - char 04 call wri_cgram ; Write new character to CGRAM of LCD movlw B'00000110' ; Write right 80% to CGRAM of LCD - char 05 call wri_cgram movlw B'00000111' ; Data to write right full block to CGRAM, -char 06 call wri_cgram ; Write character to CGRAM of LCD movlw B'00001100' ; Data to write left 40% to CGRAM -char 07 call wri_cgram ; Write character to CGRAM of LCD movlw CMD_MOV_CUR_DISP ; Move cursor back to start of display call Cmnd2LCD ; " return ; Done! ; Write new character data to the LCD CGRAM. Each new character requires ; eight writes of bit patterns to fill the eight rows of a character. ; CGRAM address is already set. wri_cgram movwf temp ; Save data to write to LCD movlw D'7' ; Data will be written seven times movwf count ; Set the counter wri_cgram_loop movf temp,w ; Get the data to write to LCD CGRAM call Data2LCD ; Do it decfsz count,f ; Decrement counter and exit if zero bra wri_cgram_loop ; Not zero, do it again movlw B'00000000' ; Write 8th bit pattern, 0x00 call Data2LCD return ; Done! ; ;**************************************************************************** ; ; Purpose: Displays power-up message and version information. ; ; Input: Sign On message and Version message ; ; Output: LCD displays debug info ; ; **************************************************************************** Display_version movlw sign_msg ; Offset to Sign-on message call Display_message ; Display it on LCD movlw CMD_2ND_LINE ; Point to LCD second line call Cmnd2LCD ; Move entry marker movlw ver_msg ; Offset to version message call Display_message ; Display on the LCD call Wait_a_sec ; Wait a bit return ; back to caller ;******************************************************************************* ; ; Purpose: Displays the selected message on the LCD ; ; Input: Three entry points. This is because a message cannot cross a 256 ; byte page boundry. Entry point used depends on which message ; table contains the message. Wreg contains the offset of the ; desired message to display. ; ; Output: Message is displayed on LCD. ; ;******************************************************************************* Display_message ; <<<<<<<<<<<< Entry Point <<<<<<<<<<<<<<<<< movwf temp ; Save message offset movlw upper messages ; Set table pointer messages in flash memory movwf TBLPTRU ; Upper address in flash movlw high messages ; movwf TBLPTRH ; High address in flash movlw low messages ; Lower flash memory pointer addwf temp,w ; Add in offset to the wanted message movwf TBLPTRL ; Low address in lower pointer and load table pointer bra disp_msg_loop Display_message1 ; <<<<<<<<<<<< Entry Point <<<<<<<<<<<<<<<<< movwf temp ; Save message offset movlw upper messages1 ; Set table pointer messages in flash memory movwf TBLPTRU ; Upper address in flash movlw high messages1 ; movwf TBLPTRH ; High address in flash movlw low messages1 ; Lower flash memory pointer addwf temp,w ; Add in offset to the wanted message movwf TBLPTRL ; Low address in lower pointer and load table pointer disp_msg_loop TBLRD *+ ; Read a byte from flash movf TABLAT,w ; Get byte just read andlw H'FF' ; Just set status flags, Z to be precise. bz disp_exit ; If the char is null, then end of message disp_char call Data2LCD ; Else, send the character to the LCD bra disp_msg_loop ; Loop again disp_exit return ; Exit when the message has been sent ; ; ***************************************************************************** ; * * ; * Purpose: Display the frequency setting on the LCD. * ; * * ; * Input: Two entry points. First is normal frequency display. Second is * ; * special entry point to display frequency for the Menu. The * ; * value in freq_0 ... freq_3 is displayed. * ; * * ; * Output: The frequency is displayed on the LCD with leading zero's * ; * suppressed. * ; * * ; ***************************************************************************** ; Disp_freq ; <<<<<<<<<<<<<<<<<<<<<<<<< ENTRY POINT movlw CMD_MOV_CUR_DISP+D'1' ; Move LCD cursor to character position 1 call Cmnd2LCD ; Send starting digit location to LCD Disp_freq_menu ; <<<<<<<<<<<<<<<<<<<<<<<<< ENTRY POINT ; ***************************************************************************** ; ; Purpose: This subroutine converts a 32 bit binary number to a 10 digit ; BCD number. The input value taken from vfo_X_(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. ; ; Input: The value in vfo_X_0 ... vfo_X_3 ; ; Output: The BCD number in BCD_0 ... BCD_4 ; ; ***************************************************************************** ; Following code obtained from PIC List website. Originally coded for 3 binary ; bytes input and four packed BCD bytes output. Modified here for four binary bytes ; input and five packed BCD bytes output. ; ; From Oliver Broad [obroad at TELINCO.CO.UK]. Oliver Broad says ; The following program is based on the binary to BCD code from the microchip appnote. ; The method seems odd at first, compared to the more conventional divide-by-10-and- ; output-the-remainder. ; It was originally written to convert data from an AD7714 sigma delta ADC. Looping ; version of Binary to BCD converter for 24 bit values. Code is designed for MPSIM ; testing, so insert stimuli on ports B and B and B, and read out R0,R1,R2,R3. ; Code is based on the fact that a bcd number may be multiplied by two by a simple ; bit shift followed by a BCD fixup. The fixup is greatly simplified by performing ; it BEFORE the shift, which is why 3H and 30H are used instead of 6H and 60H. Bin2BCD movlw D'32' ; Set loop counter movwf count ; to 32 clrf BCD_0 ; Clear output clrf BCD_1 ; " " clrf BCD_2 ; " " clrf BCD_3 ; " " clrf BCD_4 ; " " btfsc MENU_flags,DSPY ; Is this frequency display for Menu routines? bra bin2BCD ; Yes, do not move frequency, it is already in ; freq_0..3 ; ; Copy the active VFO data to freq_3..0 for use by this function. ; movff vfo_pointer,FSR0L ; Get vfo_X_0 address into FSR0 for indirection clrf FSR0H movff POSTINC0,freq_0 ; Get vfo_X_0 and store it for conversion movff POSTINC0,freq_1 ; Get vfo_X_1 movff POSTINC0,freq_2 ; Get the byte vfo_X_2 movff INDF0,freq_3 ; Get it, vfo_X_3 ; Now ready for conversion bin2BCD bcf STATUS,C ; Clear Carry indicator bra binbc2 ; Go do shift (multiply by 2) bin2_loop lfsr 2,BCD_0 ; Init FSR2 to point at BCD_0 bcdadj movlw H'03' ; Adjust addwf INDF2,f btfss INDF2,3 subwf INDF2,f movlw H'30' addwf INDF2,f btfss INDF2,7 subwf INDF2,f incf FSR2L,f ; Bump to next BCD output location movlw BCD_4+D'1' ; 1 beyond last BCD location cpfseq FSR2L ; FSR2 pointing 1 above last location? bra bcdadj ; No, do adjust again binbc2 rlcf freq_0,f ; Shift frequency 1 bit to left rlcf freq_1,f ; and insert Carry into bit 0 of next rlcf freq_2,f ; location rlcf freq_3,f rlcf BCD_0,f ; Shift packed BCD 1 bit to left rlcf BCD_1,f rlcf BCD_2,f rlcf BCD_3,f rlcf BCD_4,f decfsz count,f ; Decrement count bra bin2_loop ; Not done, do it again clrf temp ; Clear the suppression test flag ; Get 100 MHz digit from BCD_4 lower nibble. "0000XXXX" movf BCD_4,w ; Put 100MHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD ; Extract and send "XXXX" from byte containing "XXXXYYYY" ; - Swap halves to get YYYYXXXX swapf BCD_3,w ; Swap 10MHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD movf BCD_3,w ; Put 1MHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD IFDEF MHZ movlw '.' ; Get a period for MHz display ELSE movlw ',' ; Get a comma ENDIF btfsc temp,0 ; Still supressing zero's? bra comma_ok ; No, display the comma or period movlw ' ' ; Yes, get suppression character comma_ok call Data2LCD ; Send byte in W to LCD swapf BCD_2,w ; Swap 100KHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD movf BCD_2,w ; Put 10KHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD swapf BCD_1,w ; Swap 1KHz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD IFDEF KHZ movlw '.' ; Get a period for KHZ display ELSE movlw ',' ; Get a comma for HZ display ENDIF btfsc temp,0 ; Still supressing zero's? bra char_ok ; No, display the comma or period movlw ' ' ; Yes, get suppression character char_ok call Data2LCD ; Send byte in W to LCD movf BCD_1,w ; Put 100 Hz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD swapf BCD_0,w ; Swap 10 Hz BCD digit into lower nibble of W call Send_digit ; Check suppression and send byte to LCD ; The Least signicant character will not be suppressed movf BCD_0,w ; Put 1 Hz BCD digit into lower nibble of W andlw LO_NYBBLE_MASK ; Mask for lower nibble only (0000YYYY) addlw ASCII_NUM_BASE ; Add offset for ASCII char set (0011YYYY) call Data2LCD ; Send byte in W to LCD btfsc MENU_flags,MENU_DISP ; Is this display for menu: return ; Yes, go back call Set_cursor_pos ; Set entry marker to LCD position for user change return ; Return to caller ; ================================================================================ ; Internal subroutine called by Disp_freq ; Leading zero suppression takes place here. ; - Mask with 0x0F to get 0000XXXX ; If suppressing and character is zero, a blank character is substituted until ; first non-zero character, then flag is set and suppression ends. ; - Add ASCII bias (0011XXXX) if character is not suppressed ; Send_digit andlw LO_NYBBLE_MASK ; Just the nibble (0000XXXX) btfsc temp,LSB ; Is suppression done? bra send_digit_ns ; Yes, send this character to display bnz send_digit_es ; No, if character non-zero, go end suppression movlw ' ' ; A zero and still suppressing, get space character bra Data2LCD ; and send to LCD. Return is to Disp_freq send_digit_es incf temp,f ; Set the end suppression flag send_digit_ns addlw ASCII_NUM_BASE ; Add offset for ASCII char set (0011XXXX) bra Data2LCD ; Send to LCD, return is to Disp_freq ; ***************************************************************************** ; ; 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. ; NOTE: The LCD control R/W must be strapped to ground ; (Always write). ; ; Input: W has the command or data byte to be sent to the LCD. ; ; Output: None ; ; ***************************************************************************** Cmnd2LCD ; ****** Entry point ****** bcf PORTB,LCD_RS ; Clear RS for command bra write2LCD ; Go to common code Data2LCD ; ****** Entry point ******** bsf PORTB,LCD_RS ; Raise RS for data write2LCD ; ; Transfer Most Significant nibble (XXXX portion of XXXXYYYY) ; movwf byte2send ; Save byte to write to LCD movlw HI_NYBBLE_MASK ; Set up mask andwf PORTB,f ; Keep RB7..RB4 but clear old RB3..RB0 swapf byte2send,w ; Put byte into W (reverse nibbles) andlw LO_NYBBLE_MASK ; Mask to give 0000XXXX in W iorwf PORTB,f ; To RB3..RB0 with RB7..RB4 unchanged bsf PORTB,LCD_E ; Raise LCD "E" nop ; wait 166 nanoseconds bcf PORTB,LCD_E ; and drop E line ; ; Transfer Least Significant nibble (YYYY portion of XXXXYYYY) ; movlw HI_NYBBLE_MASK ; Set up mask andwf PORTB,f ; Clear old RB3..RB0 movf byte2send,w ; Move byte into W andlw LO_NYBBLE_MASK ; Mask to give LS nibble (0000YYYY) in W iorwf PORTB,f ; To RB3..RB0 with RB7..RB4 unchanged bsf PORTB,LCD_E ; Raise "E" nop ; wait 166 nanoseconds bcf PORTB,LCD_E ; and drop E ; It is possible to use the LCD Busy to determine when LCD has finished command. ; This requires use of the RW control to LCD and a seperate PIC output pin. To ; save the use of the PIC output pin, timing is used to delay for a period of time ; to allow the LCD to complete the write command. ; 50 useconds is the longest time found for LCD controllers to finish simple ; write commands. Other commands, such as clear and return home, take longer and ; that wait time must be programmed elsewhere. movlw LCD_DLY_CT ; Get delay count to wait for LCD to un-busy movwf timer1 ; Put delay count into timer1 LCD_wait_loop decfsz timer1,f ; Decrement timer bra LCD_wait_loop ; Exit on timer1=0 or else loop back return ; ***************************************************************************** ; ; Purpose: Display the Frequency Unit. ; ; Input: None ; ; Output; Frequency unit is displayed on LCD line 1 after the frequency ; ; ***************************************************************************** Disp_funit movlw CMD_MOV_CUR_DISP+D'13' ; Move to character 14 on 1st line of LCD call Cmnd2LCD ; Send it IFDEF MHZ movlw 'M' ; Megaherts - display "M" call Data2LCD ; Send to LCD ENDIF IFDEF KHZ movlw 'K' ; If Kilohertz display get a "K" call Data2LCD ; Send to LCD ENDIF movlw hz_msg ; Display "Hz" on LCD call Display_message return ; ***************************************************************************** ; ; Purpose: Display the operating mode on LCD ; ; Input: MODE_flags ; ; Output; Operating mode is displayed on second line of LCD ; ; ***************************************************************************** Disp_mode movlw CMD_2ND_LINE+D'6' ; Move cusor to 2nd line character 7 call Cmnd2LCD ; Do it movlw LO_NYBBLE_MASK ; Mask for signal mode andwf MODE_flags,w ; Save just signal mode movwf temp ; Move signal mode to working temp movlw AM ; Get mode bits for AM cpfseq temp ; Is it AM mode bra disp_mode_a ; No movlw am_msg ; Pointer to AM call Display_message ; and display it return disp_mode_a movlw SSBU ; Get mode bits for SSB Upper cpfseq temp ; Is mode SSB Upper? bra disp_mode_b ; No, go on movlw sbu_msg ; Pointer to SSB messaage bra disp_mode_exit ; Display and exit disp_mode_b movlw SSBL ; Get mode bits for SSB Lower cpfseq temp ; Is mode SSB Lower? bra disp_mode_c ; No, go on movlw sbl_msg ; Pointer to SSB messaage bra disp_mode_exit ; Display and exit disp_mode_c movlw CW_POS ; Get mode bits for CW+ cpfseq temp ; Is mode CW plus? bra disp_mode_d ; No movlw cwplus_msg ; Yes, message for CW Plus bra disp_mode_exit ; Display and exit disp_mode_d movlw CW_NEG ; Get mode bits for CW- cpfseq temp ; Is mode CW-? bra disp_mode_e ; No, go on movlw cwmin_msg ; Pointer to CW Minus bra disp_mode_exit ; Display and exit disp_mode_e movlw DS ; Get mode bits for Double Sideband cpfseq temp ; Is mode Double Sideband? bra disp_mode_f ; No, go on movlw dsbnd_msg ; Pointer to Double Sideband message bra disp_mode_exit ; Display and exit disp_mode_f movlw phase_msg ; Only mode remaining is Phase disp_mode_exit call Display_message ; Display the message return ; and return ; ***************************************************************************** ; ; Purpose: Position entry marker at the frequency digit position being updated. ; ; Input: The cur_pos variable holds the digit position. ; ; Output: None ; ; ***************************************************************************** Set_cursor_pos movlw CMD_MOV_CUR_DISP ; Cursor position base command iorwf cur_pos,w ; Or in the cursor postion call Cmnd2LCD ; Move the cursor return ; and go back ; ; ***************************************************************************** ; ; Purpose: Clears the LCD and set cursor at line 1 character 1. ; ; Input: None ; ; Output: None. ; ; ***************************************************************************** Clear_lcd movlw CMD_CLEAR_LCD ; Prepare to display a new message on LCD call Cmnd2LCD ; by clearing the current one call Wait_15ms ; Wait required time for LCD to finish return ;**************************************************************************** ; ; Purpose: Convert a binary byte to two BCD digits. ; Maximum value: 0x63 or B'01100011 = 99 ; ; Input: BCD_0 contains byte to be converted. ; ; Output: BCD_1 contains MSD and BCD_0 contains LSD of BCD data. ; ;**************************************************************************** Bin8BCD ; Convert the binary value to BCD clrf BCD_1 ; Clear MSD gtenth movlw D'10' ; Get a ten subwf BCD_0,w ; Subract 10 from the data bc $+4 ; Was there a carry bra over ; No, under 10 movwf BCD_0 ; Yes, save remainder incf BCD_1,f ; Increment MSD bra gtenth ; Go again over return ; *************************************************************************** ; ; Purpose: Display the CW Offset on 2nd line of LCD ; ; Input: None ; ; Output: CW Offset displayed ; ; *************************************************************************** Disp_cwoffset lfsr 2,cwoffs_pos_0 ; Point at offset call Bin16BCD ; Convert CW offset to BCD clrf temp swapf BCD_1,w ; Get highest nibble (thousand's) call test4zero movf BCD_1,w ; Get next nibble (hundred's) call test4zero swapf BCD_0,w ; Get next nibble (ten's) call test4zero movf BCD_0,w ; Get last nibble (one's), do not suppress andlw LO_NYBBLE_MASK ; Save just digit addlw ASCII_NUM_BASE ; Make it ASCII call Data2LCD ; and display last digit return test4zero andlw LO_NYBBLE_MASK ; Just the nibble btfsc temp,LSB ; Is suppression done? bra display_nyb ; Yes, go display bz $+4 ; NO, Is the nibble zero bra display_nyb_ns ; No, go display movlw ' ' ; Yes, Get a space character call Data2LCD ; and display it return ; Return to Change_cwoff display_nyb_ns incf temp,f ; Indicate end suppression display_nyb addlw ASCII_NUM_BASE ; Convert to ASCII call Data2LCD ; and display return ; *************************************************************************** ; ; Purpose: Convert two bytes of binary data to BCD. ; This is a cut down version of Bin2BCD. ; ; Input: File Select Register 2 points at LSByte of two data bytes. ; ; Output: Two bytes of packed BCD characters (4) in BCD_1 and BCD_0. ; ; *************************************************************************** ; Cut down version of Bin2BCD. Used to display CW Offset. Bin16BCD movlw D'16' ; Set loop counter movwf count ; to 16 clrf BCD_0 ; Clear output clrf BCD_1 ; " " movff POSTINC2,freq_0 ; Move LSByte movff INDF2,freq_1 ; Move MSByte bcf STATUS,C bra bin16b bin16loop lfsr 2,BCD_0 bcd16adj movlw H'03' addwf INDF2,f btfss INDF2,3 subwf INDF2,f movlw H'30' addwf INDF2,f btfss INDF2,7 subwf INDF2,f incf FSR2L,f movlw BCD_1+D'1' cpfseq FSR2L bra bcd16adj bin16b rlcf freq_0,f rlcf freq_1,f rlcf BCD_0,f rlcf BCD_1,f decfsz count,f bra bin16loop return IFDEF BAR_GRAPH ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MATH ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; ************************************************************************* ; ; Purpose: Multiply 16 bit by 16 bit integers. ; ; Input: One integer in DDS_4 (ARG1H), DDS_5 (ARG1L). ; Another integer in DDS_6(ARG2H), DDS_7 (ARG2L). ; ; Output: Four byte result in DDS_0(RES3), DDS_1(RES2), DDS_2(RES1), ; and DDS_3(RES0). DDS_0 is MSByte. ; ; From: PIC 18F2550 Product Specification. ; ; ************************************************************************* Mpy16X16 movf DDS_5,w ; ARG1L, W mulwf DDS_7 ; ARG2L ; ARG1L * ARG2L-> PRODH:PRODL movff PRODH,DDS_2 ; RES1 movff PRODL,DDS_3 ; RES0 movf DDS_4,w ; ARG1H, W mulwf DDS_6 ; ARG2H ; ARG1H * ARG2H-> PRODH:PRODL movff PRODH,DDS_0 ; RES3 movff PRODL,DDS_1 ; RES2 movf DDS_5,w ; ARG1L, W mulwf DDS_6 ; ARG2H ; ARG1L * ARG2H-> PRODH:PRODL movf PRODL,w ; addwf DDS_2,f ; RES1, F ; Add cross movf PRODH,w ; products addwfc DDS_1,f ; RES2, F clrf WREG ; addwfc DDS_0,f ; RES3, F movf DDS_4,w ; ARG1H, W mulwf DDS_7 ; ARG2L ; ARG1H * ARG2L-> PRODH:PRODL movf PRODL,w ; addwf DDS_2,f ; RES1, F ; Add cross movf PRODH,w ; W ; products addwfc DDS_1,f ; RES2, F clrf WREG ; addwfc DDS_0,f ; RES3, F return ; Done ; ************************************************************************* ; ; Function: Div16by8_Fract ; ; Purpose: Divide 16 bit number by 8 bit number. Provide Fractional ; remainder ; ; Input: Numerator in DDS_5 and DDS_5. Denominator in DDS_2. Non-Zero ; indicator for fractional remainder in DDS_3. ; ; Output: Quotient in DDS_5 and DDS_6. DDS_0 and DDS_1 contains ; remainder. ; ; ************************************************************************* ; FROM PIClist web site. ; 16 bits by 8 with result as fraction rather than remainder ; from Nikolai Golovchenko. ; Modified to make choice of remainder. Fractional or not. ; ; Division of 16bit by 8 bit with a result in Q16.16 form ; ; X_Int.X_Frac = X_Int / Y ; ; RAM - 6 bytes (1 temp): ; X_Int = X_IntH:X_IntL 16 bit input/ output integer part ; X_Frac = X_FracH:X_FracL 16 bit output fractional part ; Y divisor / temporary ; Counter counter ; F fractional indicator ; ; Size = 39 instructions ; Execution time = 6+16*14-1+3+16*14-1+3+2(return) ; = 460 instruction cycles ; ; 8-July-2000 by Nikolai Golovchenko ; 16-February-2001 fixed, reduced execution time and temporaries ; Numerator & Quotient = X_Int =X_IntH, X_IntL = DDS_5, DDS_6 ; Denominator = Y = DDS_2 ; Fraction = X_Frac = X_FracH, X_FracL = DDS_0, DDS_1 ; Indicator = F = DDS_3 ; ************************************************************************* Div16by8_Fract clrf DDS_1 ; X_fracL clrf DDS_0 ; X_fracH movlw D'16' movwf count ; Counter movf DDS_2,w ; Y, w ; keep Y value in accumulator clrf DDS_2 ; Y ; and use Y register as temporary ; Find integer part Div16by8to16_16a rlcf DDS_6,f ; X_IntL, f ; shift next msb into temporary rlcf DDS_5,f ; X_IntH, f rlcf DDS_2,f ; Y, f rlcf count,f ; Counter, f ; carry has 9th bit of temporary ; copy carry to counter subwf DDS_2,f ; Y, f ;subtract Y (in w) from temporary bnc $+4 ; if no borrow, set Counter.0 bsf count,0 ; Counter, 0 btfss count,0 ; Counter, 0 ;if Counter.0 clear (borrow) restore temporary addwf DDS_2,f ; Y, f bcf STATUS,C ; clrc ;restore counter rrcf count,f ; Counter, f ; at this point carry is the next bit of result decfsz count,f ; Counter, f ; repeat 16 times to find integer part bra Div16by8to16_16a ; shift last integer bit rlcf DDS_6,f ; X_IntL, f rlcf DDS_5,f ; X_IntH, f tstfsz DDS_3 ; test if fractional part is to be done bra fract ; if non-zero, do fractional return ; Zero - return fract ; Find fractional part bsf count,4 ; Counter, 4 ; Counter = 16 Div16by8to16_16b ; Shift zero bit into temporary rlcf DDS_1,f ; X_fracL, f rlcf DDS_0,f ; X_fracH, f rlcf DDS_2,f ; Y, f rlcf count,f ; Counter, f ; carry has 9th bit of temporary ; copy carry to counter subwf DDS_2,f ; Y, f ; substract Y(in w) from temporary bnc $+4 ; skpnc ; if no borrow, set Counter.0 bsf count,0 ; Counter, 0 btfss count,0 ; Counter, 0 ; clrc ;if Counter.0 clear (borrow) restore temporary addwf DDS_2,f ; Y, f bcf STATUS,C ; clrc ;restore counter rrcf count,f ; Counter, f decfsz count,f ; Counter, f ; repeat 16 times bra Div16by8to16_16b ; shift last fractional bit rlcf DDS_1,f ; X_fracL, f rlcf DDS_0,f ; X_FracH,f movwf DDS_2 ; Y ; restore divisor return ;******************************************************************** ; ; Expanded version of Divide from Microchip AN526 Math Routines ; ; Optained from PICList and modified. ; ;******************************************************************** ; Divide ( 24/16 -> 16 with Remainder ) ; ; ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output ; with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ; ACCc (ACCcHI,ACCcLO). ; NOTE: Before calling this routine, the user should make sure that ; the Numerator(ACCb) is greater than Denominator(ACCa). If ; the case is not true, the user should scale either Numerator ; or Denominator or both such that Numerator is greater than ; the Denominator. ; ; ACCa = Denominator = Freq_0, Freq_1 ; ACCb = Numerator = Quotient = DDS_5, DDS_6, DDS_7 ; ACCc = Remainder = DDS_0, DDS_1 ; ACCd = work_x = DDS_2, DDS_3, DDS_4 ;******************************************************************** Div24_16 movff DDS_5,DDS_2 ; Move numerator to work movff DDS_6,DDS_3 movff DDS_7,DDS_4 clrf DDS_0 ; Clear remainder clrf DDS_1 clrf DDS_5 ; Clear area for quotient clrf DDS_6 clrf DDS_7 movlw D'24' ; 24 Bits movwf count dloop bcf STATUS,C ; rlcf DDS_4,f rlcf DDS_3, F rlcf DDS_2, F rlcf DDS_1, F rlcf DDS_0, F movf freq_0,W subwf DDS_0,W ; check if Denominator>Remainder bz $+4 bra nochk movf freq_1,W ; if msbyte equal then check lsb subwf DDS_1,W nochk bc $+4 ; carry set if Remainder>Denominator bra nogo movf freq_1,W ; Remainder-Denominator into Remainder subwf DDS_1, F bc $+4 decf DDS_0, F movf freq_0,W subwf DDS_0, F bsf STATUS,C ; shift a 1 into b (result) nogo rlcf DDS_7,f rlcf DDS_6, F rlcf DDS_5,f decfsz count, F ; loop until all bits checked bra dloop return ENDIF ;>>>>>>>>>>>>>>>>>>>>>>>>> MISCELLANEOUS ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<< ; **************************************************************************** ; ; Purpose: Process Timer 0 overflows. Control ADC AN0. ; ; Input: Enter from Main. T0_ofl_ctr. ; ; Output: Turn on ADC AN0 and set ADC process flag. If T0_ofl_ctr expired, ; vfo restore information saved in SEPROM. Both sets of VFO registers ; are saved in EEPROM band table. ; ;****************************************************************************** Proc_timer0 bcf T0CON,TMR0ON ; A Timer 0 overflow occurred, Stop Timer 0 bcf INTCON,TMR0IF ; Reset the overflow flag movlw TIMER0_HI ; Count for timer 0 for 0.1 seconds movwf TMR0H ; High clrf TMR0L ; Reload Timer 0 Low and High movlw T0CON_INIT ; Restart Timer 0 with prescale = 8 movwf T0CON ; Timer 0 control IFDEF BAR_GRAPH btfsc MENU_flags,MENU ; If Menu in use do not display bra proc_ovfl ; the Bar Graph decfsz adc_counter,f ; Decrement ADC sampling counter bra proc_ovfl ; Counter not zero, continue bsf ADCON0,GO ; Start the ADC bsf MISC_flags,AGD ; Set the ADC process flag movlw ADC_COUNT ; Get the sampling count movwf adc_counter ; and initialize counter ENDIF proc_ovfl decfsz T0_ofl_ctr,f ; Decrement the overflow counter goto Main ; Not zero, exit ; Save vfo restart information in SEPROM. ; VFO Select, VFO-A band number and VFO-B band number are saved to SEPROM. movlw restore_vfo ; Pointer to start of SEPROM save movwf seprom_a_0,1 ; Set SEPROM write address clrf seprom_a_1,1 clrf seprom_a_2,1 movlw D'3' ; Write 3 bytes movwf se_count movff vfo_select,se_buf_0 ; Move vfo_select to SEPROM buffer movff vfo_a_band,se_buf_1 ; Move vfo_a_band to SEPROM buffer movff vfo_b_band,se_buf_2 ; Move vfo_b_band to SEPROM buffer lfsr 1,se_buf_0 ; FSR1 points at SEPROM buffer call Se_write ; Save the data ; Save vfo registers in EEPROM band table and Ram Bank 2 band table. lfsr 0,vfo_a_0 ; Pointer to start of VFO-A registers movlw FREQ_CT+CTL_CT ; Four bytes of frequency plus controls movwf count ; Save in counter - # bytes to write lfsr 1,BAND_BASE ; Start of band table in ram movf vfo_a_band,w ; Get VFO-A band number mullw BAND_LGTH ; Multiply by length of band table entry movf PRODL,w ; Get the band index addwf FSR1L ; Point at VFO-A registers in Ram band table addlw band_tbl ; Add start of EEPROM band table to band index movwf EEADR ; Start of VFO-A registers in EEPROM band table proc_vfoA_loop movff INDF0,EEDATA ; Get a byte -> EEPROM data, FSR0 points at source call Write_EEPROM ; Write VFO register byte to EEPROM. movff POSTINC0,POSTINC1 ; Write to ram band table incf EEADR,f ; Increment EEPROM address decfsz count,f ; Decrement count and loop if not zero bra proc_vfoA_loop lfsr 0,vfo_b_0 ; Pointer to start of VFO-B registers movlw FREQ_CT+CTL_CT ; Four bytes of frequency plus controls movwf count ; Save in counter - # bytes to write lfsr 1,BAND_BASE ; Point at ram band table movf vfo_b_band,w ; Get VFO-B band number mullw BAND_LGTH ; Multiply by length of band table entry movf PRODL,w ; Get the band index addlw FREQ_CT+CTL_CT ; Add in offset to bypass VFO-A registers addwf FSR1L ; Point at VFO-B registers in Ram band table addlw band_tbl ; Add start of band table in EEPROM movwf EEADR ; Start of VFO-B registers in EEPROM band table proc_vfoB_loop movff INDF0,EEDATA ; Get a byte, FSR0 points at source call Write_EEPROM ; Write VFO register byte to EEPROM. movff POSTINC0,POSTINC1 ; Write to ram band table incf EEADR,f ; Increment EEPROM address decfsz count,f ; Decrement count and loop if not zero bra proc_vfoB_loop movlw count_of_ovfl ; Initialize the counter movwf T0_ofl_ctr ; to start over goto Main ; Back to Main ;**************************************************************************** ; ; Purpose: Move the Band Table from EEPROM to Ram Bank 2 ; ; Input: None ; ; Output: Band Table is moved from EEPROM to Ram Bank 2 ; ;**************************************************************************** Move_band_tbl lfsr 0,BAND_BASE ; Intialize indirect address, Ram Bank 2 movlw BAND_LGTH ; Get length of an entry in band table mullw num_bands ; Multiply by number of bands movff PRODL,count ; Number of bytes to move, must be < 256 movlw band_tbl ; Start of band table in EEPROM movwf EEADR ; Set EEPROM address mv_band_tbl call Read_EEPROM ; Read a Byte from EEPROM movff EEDATA,POSTINC0 ; Save in Ram Bank 1 incf EEADR ,f ; Increment EEPROM address decfsz count,f ; Decrement counter, all done? bra mv_band_tbl ; No return ; Yes ; **************************************************************************** ; ; Purpose: Move a Table from Flash memory to Ram. ; ; Input: File Select Register 2 points at starting Ram Location. ; TBLPTR points a starting Flash location. count contains ; number of bytes to transfer. ; ; Output: Table is moved to Ram. ; ; **************************************************************************** Move_flash2ram TBLRD *+ ; Read a byte from flash, increment pointer movff TABLAT,POSTINC2 ; and move to Ram Bank X decfsz count,f ; Decrement counter bra Move_flash2ram ; Not zero, do again return ; All done, go back ; ***************************************************************************** ; ; Purpose: This routine is entered at start up if the Menu Switch ; is pressed at power-on time. ; ; " Defined Frequency Hz" ; "CALIBRATE " is displayed. ; ; The DDS chip is programmed to produce defined calibration frequency. ; As long as the Menu Switch is pressed, and Frequency ; encoder is turned the clk_ratio value in Ram is slowly altered to allow ; the output frequency to be trimmed to exactly the defined calibration ; frequency. VFO Switch and Menu Switch pressed while ; Frequency Encoder is turned results in faster frequency correction. ; When the Menu Switch is released and Frequency Encoder ; is turned, the new clk_ratio value is stored in the SEPROM and ; normal operation begins. ; ; Input: The original clk_ratio constant in clk_rat_0...5 ; ; Output: The corrected clk_ratio constant in SEPROM and clk_rat_0...5 ; ; ***************************************************************************** Calibrate bsf MISC_flags,PCAL_FLG ; Set the calibrate flag to go directly to FTW calc clrf vfo_select ; Use VFO-A movlw vfo_a_0 ; Get address of VFO A registers movwf vfo_pointer ; save in pointer register movlw cal_freq_0 ; Set frequency by moving defined calibration movwf vfo_a_0 ; frequency to VFO-A registers movlw cal_freq_1 ; movwf vfo_a_1 ; movlw cal_freq_2 ; movwf vfo_a_2 ; movlw cal_freq_3 ; movwf vfo_a_3 ; call Clear_lcd ; Clear the display ; call Disp_freq ; Display the calibration frequency on the LCD movlw CMD_2ND_LINE+D'3' ; Positon cursor on 2nd line call Cmnd2LCD ; Do it movlw cal_msg ; Display "CALIBRATE" on LCD call Display_message ; cal_loop call Send_dds_freq ; Have DDS generate the calibration frequency bcf MISC_flags,ENC_MOV ; Reset the encoder movement flag cal_encoder_wait call Tune_encoder ; Check for encoder movement btfss MISC_flags,ENC_MOV ; Has encoder moved? bra cal_encoder_wait ; No, keep looping until encoder moves btfsc PORTA,MENU_SW ; Check if Menu switch was pressed bra cal_done ; If released, then exit calibrate mode ; Else, stay in cal and update clock ratio. clrf fstep_0 ; Clear the bytes used for addition clrf fstep_1 ; " clrf freq_3 ; " clrf freq_2 ; " clrf freq_0 ; " movlw SMALL_FSTEP ; Assume that we are adjusting slowly movwf freq_1 ; Use small increment btfsc PORTA,VFO_SW ; Was VFO Switch pressed? bra update_osc ; No, then continue with small increment movlw LARGE_FSTEP ; Yes, then use the large increment movwf freq_2 ; update_osc btfsc last_dir,DIR_BIT ; Is Tuning Encoder moving counter clockwise (down)? bra faster ; No, increase the clock ratio value ; Decrease clock ratio value by subtracting the calibrate increment. movf freq_0,w subwf clk_rat_5 movf freq_1,w subwfb clk_rat_4 movf freq_2,w subwfb clk_rat_3 movf freq_3,w subwfb clk_rat_2 movf fstep_0,w subwfb clk_rat_1 movf fstep_1,w subwfb clk_rat_0 bra cal_loop ; Increase the clock ratio value by adding the calibrate increment. faster movf freq_0,w addwf clk_rat_5 movf freq_1,w addwfc clk_rat_4 movf freq_2,w addwfc clk_rat_3 movf freq_3,w addwfc clk_rat_2 movf fstep_0,w addwfc clk_rat_1 movf fstep_1,w addwfc clk_rat_0 bra cal_loop cal_done ; Calibration is complete. bcf MISC_flags,ENC_MOV ; Reset the encoder movement flag ; The clock ratio bytes are now in Ram at clk_rat_0..5. Save in SEPROM. movlw clk_rat_save ; Point at location for save in SEPROM movwf seprom_a_0,1 ; Move to SEPROM write address clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " lfsr 1,clk_rat_0 ; Point at data to be written into SEPROM movlw D'6' ; 6 bytes movwf se_count ; Count to write counter call Se_write ; Do the write to SEPROM return ; Return to the caller ; **************************************************************************** ; ; Purpose: Calculate AD9912 Clock Ratio. The clock ratio is used to ; calculate the AD9912 DDS Frequency Tuning Word. ; ; Input: DDS Input clock at INPUT_CLOCK and DDS Clock Multiplier ; at CLK_MULT. ; ; Output: Calculated Clock Ratio bytes are stored in RAM at clk_rat_0..5. ; and saved in SEPROM. ; ; **************************************************************************** ;The AD9912 output frequency is defined as: ; Fdds = (FTW/2^48)*Fs ; Where: ; Fdds = Required Output Frequency ; FTW = Frequency Tuning Word ; Fs = AD9912 System Clock ; Rearranging the equation to derive the Frequency Tuning Word: ; FTW = Fdds*(2^48/Fs) ; Multiplying the AD9912 Required Output Frequency by "2^48/Fs" calculates the ; FTW. ; The ratio "2^48/Fs" is called the Clock Ratio in this program. The routine ; Calc_clk_ratio calculates the Clock Ratio. Calc_clk_ratio ; Move AD9912 INPUT_CLOCK to fstep_0..3 for math that follows where the AD9912 ; System Clock is calculated (Fs). fstep_0 is MSByte movlw INPUT_CLOCK&H'FF' ; Get LSByte of Input Clock movwf fstep_3 ; movlw INPUT_CLOCK>>D'8'&H'FF' ; Get next byte movwf fstep_2 ; " movlw INPUT_CLOCK>>D'16'&H'FF' ; and next byte movwf fstep_1 ; " movlw INPUT_CLOCK>>D'24'&H'FF' ; and MSByte movwf fstep_0 ; " ; Check that the configured Clock Multiplier meets the specifications. movlw CLK_MULT ; Get the DDS clock multiplier movwf temp ; and put in work location movlw D'67' ; Is multiplier < Maximum cpfslt temp bra mult_bad ; No, bad multiplier movlw D'1' ; Is multiplier "1" cpfseq temp ; Compare to specified clock multiplier bra calc_clk_cont ; No, continue checks bra no_mpy ; Yes, no multiply. INPUT_CLOCK = DDS clock calc_clk_cont btfsc temp,0 ; Is multiplier bit 0 set (I.E. odd)? bra mult_bad ; Yes, bad multiplier movlw D'4' ; Minimum value for clock multiplier subwf temp,w ; Is multiplier < 4? bc $+4 ; No goto mult_bad ; Yes, go display message and halt clrf freq_0 ; Clear product registers clrf freq_1 clrf freq_2 clrf freq_3 ; ; Mutilpy the DDS input clock at fstep0..3 by the clock multiplier at temp. ; movlw D'8' ; Set counter for 8 bits movwf count loop_8x32 lfsr 1,fstep_3 ; Point at multiplicand LSbyte bcf STATUS,C ; Start with Carry clear btfss temp,0 ; Is multplier bit 0 (Least Significant bit) set? bra mnoadd ; No, don't need to add multiplicand term to total movf POSTDEC1,w ; Yes, get the multiplicand fstep_3 term addwf freq_3,f ; and add it in to total bnc madd1 ; Does this addition result in a carry? incfsz freq_2,f ; Yes, add one and check for another carry bra madd1 ; No, continue with next multiplicand term incfsz freq_1,f ; Yes, add one and check for another carry bra madd1 ; No, continue with next multiplicand term incf freq_0,f ; Add infintum madd1 movf POSTDEC1,w ; Use the fstep_2 term addwf freq_2,f ; Add product term to total in correct position bnc madd2 incfsz freq_1,f bra madd2 incf freq_0,f madd2 movf POSTDEC1,w ; Use the fstep_1 term addwf freq_1,f ; Add product term to total in correct position bnc madd3 incf freq_0,f madd3 movf POSTDEC1,w ; Use the fstep_0 term addwf freq_0,f ; and add it in to total mnoadd rlcf fstep_3,f ; Shift carry out into next lower byte rlcf fstep_2,f ; rlcf fstep_1,f ; rlcf fstep_0,f ; rrcf temp,f ; Shift next multiplier bit into position decfsz count,f ; One more bit has been done. Are we done? goto loop_8x32 ; No, go back to use this bit bra chk_clk_max ; Go check if clock exceeds maximum no_mpy movff fstep_3,freq_3 ; Multiplier is one so just move clock movff fstep_2,freq_2 ; bytes for maximum clock check movff fstep_1,freq_1 ; " movff fstep_0,freq_0 ; " ; AD9912 System Clock is in freq_0..3 = INPUT_CLOCK * CLK_MULT. ; Check that AD9912 System Clock does not exceed maximum allowed of 1.2 GHz. chk_clk_max movlw H'47' ; Maximum DDS clock frequency is 1.2 GHz subwf freq_0,w ; Check Hi-order byte bz mhi_byte_ok ; If equal ok bn clk_max_ok ; If less than, ok bra clock_too_hi ; Too high, display message and halt mhi_byte_ok movlw H'86' ; Check next lower byte subwf freq_1,w bz mlow1_byte_ok bn clk_max_ok bra clock_too_hi mlow1_byte_ok movlw H'8C' ; Check next lower byte subwf freq_2,w bz mlow2_byte_ok bn clk_max_ok bra clock_too_hi mlow2_byte_ok movf freq_3,w ; If we got this far, the last byte must be zero. bz $+4 ; If zero, OK goto clock_too_hi ; Not zero, go display message and halt. clk_max_ok ; DDS system clock frequency not above 1.2 GHz ; Determine the VCO setting for AD9912. First is system clock 900 MHZ or above lfsr 1,freq_0 ; Point at DDS System clock movlw H'35' ; 900 Mhz MSbyte subwf POSTINC1,w ; freq_0 - 900 MHz MSByte bz clk_900_1 ; They are equal, check next LSByte bc clk_vco_high ; freq_0 > 900MHZ MSbyte bra clk_810 ; freq_0 < 900MHZ MSBYTE clk_900_1 movlw H'84' ; 900MHz Next MSByte subwf POSTINC1,w ; freq_1 - 900MHz NMSByte bz clk_900_2 ; They are equal, check next LSByte bc clk_vco_high ; freq_1 > 900MHz NMSByte bra clk_810 ; freq_1 < 900MHz NMSByte clk_900_2 movlw H'E9' ; 900MHz LSByte subwf POSTINC1,w ; freq_2 - 900MHz LSByte bz clk_vco_high ; They are equal bc clk_vco_high ; freq_2 > 900MHz LSByte bra clk_810 ; freq_2 < 900MHz LSByte clk_vco_high bsf PWR_flags,VCO_HI ; Set flag for DDS VCO High bra calc_clk_rat ; and calcualte clock ratio clk_810 ; Is system clock 810 MHz to 900 MHz lfsr 1,freq_0 ; Point at DDS System clock movlw H'30' ; 810 Mhz MSbyte subwf POSTINC1,w ; freq_0 - 810 MHz MSByte bz clk_810_1 ; They are equal, check next LSByte bc clk_vco_mid ; freq_0 > 810MHZ MSbyte bra clk_700 ; freq_0 < 810MHZ MSBYTE clk_810_1 movlw H'47' ; 810MHz Next MSByte subwf POSTINC1,w ; freq_1 - 810MHz NMSByte bz clk_810_2 ; They are equal, check next LSByte bc clk_vco_mid ; freq_1 > 810MHz NMSByte bra clk_700 ; freq_1 < 810MHz NMSByte clk_810_2 movlw H'9E' ; 810MHz Next LSByte subwf POSTINC1,w ; freq_2 - 810MHz LSByte bz clk_810_3 ; They are equal bc clk_vco_mid ; freq_2 > 810MHz NLSByte bra clk_700 ; freq_2 < 810MHz LNSByte clk_810_3 movlw H'80' ; 810 Mhz LSbyte subwf POSTINC1,w ; freq_0 - 810 MHz LSByte bz clk_vco_mid ; They are equal, check next LSByte bc clk_vco_mid ; freq_0 > 810MHZ LSbyte bra clk_700 ; freq_0 < 810MHZ LSByte clk_vco_mid bsf PWR_flags,VCO_MID ; Set flag for VCO Middle in flags bra calc_clk_rat ; and go calaulate clock ratio ; DDS System clock must be less than 810 MHz. clk_700 bsf PWR_flags,VCO_LOW ; Set bit for VCO Low in flags ; ; Now calculate the Clock Ratio by dividing 2^48 by AD9912 System Clock. ; freq_3..0 contains the AD9912 System Clock. ; Clock Ratio will be in clk_rat_5...0. calc_clk_rat clrf DDS_7 clrf DDS_6 ; 2^48 converted to hex clrf DDS_5 ; LSByte of the dividend clrf DDS_4 clrf DDS_3 clrf DDS_2 clrf DDS_1 movlw 0x01 movwf DDS_0 ; MSByte of dividend movlw D'88' movwf count ; Set counter clrf clk_rat_0 ; Clear clock ratio output - MSByte clrf clk_rat_1 clrf clk_rat_2 clrf clk_rat_3 clrf clk_rat_4 clrf clk_rat_5 ; LSByte clrf band_low_0 clrf fstep_3 ; Clear remainder - Not used for output clrf fstep_2 clrf fstep_1 clrf fstep_0 calc_loop bcf STATUS, C rlcf DDS_7,f ; Shift the dividend rlcf DDS_6,f rlcf DDS_5,f rlcf DDS_4,f rlcf DDS_3,f rlcf DDS_2,f rlcf DDS_1,f rlcf DDS_0,f rlcf fstep_3,f ; Shift the remainder rlcf fstep_2,f rlcf fstep_1,f rlcf fstep_0,f bnc $+4 ; overrun bra subtract movf freq_0,w ; Subtract the input clock from subwf fstep_0,w ; remainder bnz nonz ; Not zero movf freq_1,w subwf fstep_1,w bnz nonz movf freq_2,w subwf fstep_2,w bnz nonz movf freq_3,w subwf fstep_3,w nonz bc $+4 bra calc_shift subtract movf freq_3,w subwf fstep_3,f movf freq_2,w subwfb fstep_2,f movf freq_1,w subwfb fstep_1,f movf freq_0,w subwfb fstep_0, f bsf STATUS,C calc_shift rlcf band_low_0,f ; Shift the clock ratio rlcf clk_rat_5,f rlcf clk_rat_4,f rlcf clk_rat_3,f rlcf clk_rat_2,f rlcf clk_rat_1,f rlcf clk_rat_0,f decfsz count,f ; Decrement count and loop if bra calc_loop ; not zero ; The Clock Ratio bytes are now in RAM at clk_rat_0..5. Save in SEPROM. movlw clk_rat_save ; Save location in SEPROM movwf seprom_a_0,1 ; " clrf seprom_a_1,1 ; " clrf seprom_a_2,1 ; " lfsr 1,clk_rat_0 ; Point at data to be written into SEPROM movlw D'6' ; 6 bytes movwf se_count ; Count to write counter call Se_write ; Do the write to SEPROM return ; Done, Back to caller mult_bad call Clear_lcd ; Bad DDS clock multiplier specified movlw mult_bad_msg ; Point at first half of message call Display_message ; and display it movlw CMD_2ND_LINE ; Move LCD to second line call Cmnd2LCD ; Do it movlw mult_bad_msg1 ; Display second half of message call Display_message mult_bad_halt ; AND HALT!!!!!!! goto mult_bad_halt clock_too_hi call Clear_lcd ; DDS clock frequency specified is too large movlw clock_high_msg ; Display first half of message call Display_message ; do it movlw CMD_2ND_LINE ; Move LCD to second line call Cmnd2LCD movlw clock_high_msg1 ; Display second half of message call Display_message ; " clock_bad_halt ; and HALT!!!!!! goto clock_bad_halt ;<<<<<<<<<<<<<<<<<<<<<< EEPROM AND SERIAL EPROM ROUTINES <<<<<<<<<<<<<<<<<<<< ; ***************************************************************************** ; * * ; * Purpose: Write the byte of data at EEDATA to the EEPROM at address * ; * EEADR. * ; * * ; * Input: EEDATA contains the data to be written. * ; * EEADR contains the write address. * ; * * ; * Output: The EEPROM value is updated. * ; * * ; ***************************************************************************** Write_EEPROM bcf EECON1,CFGS ; Access EEPROM or flash memory bcf EECON1,EEPGD ; Make it EEPROM bsf EECON1,WREN ; Set the EEPROM write enable bit movlw H'55' ; Write 0x55 and 0xAA to EEPROM movwf EECON2 ; control register, as required movlw H'AA' ; for the write movwf EECON2 ; bsf EECON1,WR ; Set WR to initiate write write_wait btfsc EECON1,WR ; Has WR cleared? bra write_wait ; No, wait until it does bcf EECON1,WREN ; Clear the EEPROM write enable bit return ; Return to the caller ; ***************************************************************************** ; * * ; * Purpose: Read a byte of EEPROM data at address EEADR into EEDATA. * ; * * ; * Input: The address EEADR. * ; * * ; * Output: The EEPROM data byte is in EEDATA. * ; * * ; ***************************************************************************** Read_EEPROM bcf EECON1,EEPGD ; Point to Data memory bcf EECON1,CFGS ; Access EEPROM bsf EECON1,RD ; Request the read read_EEPROM_wait btfsc EECON1,RD bra read_EEPROM_wait return ; Return to the caller ; ***************************************************************************** ; * * ; * Purpose: Read from the Serial EEPROM. * ; * * ; * Input: File Select Register 1 points at start of data receive location.* ; * seprom_a_0..2 contains start address in Serial EPROM of data. * ; * se_count contains count of bytes to read. * ; * * ; * Output: Data is read from SEPROM into desired location. * ; * * ; ***************************************************************************** Se_read bcf PORTC,EICS ; Lower Chip Select to enable the SEPROM movlw SE_READ ; Get read command movwf byte2send ; Move to send data work location call Se_byte_send ; Send read command to SEEPROM movff seprom_a_2,byte2send ; Get MSByte of address call Se_byte_send ; and send movff seprom_a_1,byte2send ; Get Middle Byte of address call Se_byte_send ; and send movff seprom_a_0,byte2send ; Get LSByte of address call Se_byte_send ; and send se_read_byte clrf temp ; Clear byte receive register movlw D'8' ; Count of 8 to bit counter movwf count clrf STATUS,C ; Clear the Carry status bit se_read_next_bit rlncf temp,f ; Form data byte LSB to MSB bsf PORTC,EICK ; Raise clock to read in data byte btfsc PORTC,EIDO ; Test the receive data bit, is it a zero? bsf temp,LSB ; Bit is a one bcf PORTC,EICK ; Drop the clock decfsz count,f ; Decrement the bit count and if not zero bra se_read_next_bit ; read next bit movff temp,POSTINC1 ; Move byte to receive buffer decfsz se_count,f ; Decrement the byte count and if not zero bra se_read_byte ; go read the next byte se_read_exit bsf PORTC,EICS ; Raise chip select to stop read and deselct SEPROM return ; All done ; ***************************************************************************** ; * * ; * Purpose: Write to the Serial EEPROM. * ; * * ; * Input: File Select Register 1 points at start of data send location. * ; * seprom_a_0..2 contains start address in Serial EEPROM of data. * ; * se_count contains count of bytes to write. * ; * * ; * output: Data is written to the SEPROM into desired location. * ; * * ; ***************************************************************************** Se_write bcf PORTC,EICS ; Drop Chip Select to start the write movlw SE_WREN ; Get Write Enable command movwf byte2send ; Move to send data work location call Se_byte_send ; Send write enable command to SEEPROM bsf PORTC,EICS ; Raise Chip Select to set the Write Enable nop ; Wait nop ; Wait bcf PORTC,EICS ; Drop Chip Select so that write can start movlw SE_WRITE ; Get write command movwf byte2send ; Move to send data work location call Se_byte_send ; Send write command to SEEPROM movff seprom_a_2,byte2send ; Get MSByte of address call Se_byte_send ; and send movff seprom_a_1,byte2send ; Get Middle Byte of address call Se_byte_send ; and send movff seprom_a_0,byte2send ; Get LSByte of address call Se_byte_send ; and send se_write_data movff POSTINC1,byte2send ; Move byte to be written call Se_byte_send ; Send the byte to Serial EEPROM decfsz se_count,f ; Decrement the byte count and if zero exit bra se_write_data ; Go write the next byte bsf PORTC,EICS ; Raise chip select to stop write call Wait_5ms ; Delay for completion of write call Wait_1ms ; total of 6 milliseconds return ; All done ; ***************************************************************************** ; * * ; * Purpose: Erase the entire Serial EEPROM. * ; * * ; * Input: None * ; * * ; * Output: Serial EEPROM is erased. * ; * * ; ***************************************************************************** Se_chip_erase bcf PORTC,EICS ; Drop Chip Select to start the write movlw SE_WREN ; Get write enable command movwf byte2send ; Move to send data work location call Se_byte_send ; Send write enable command to SEEPROM bsf PORTC,EICS ; Raise Chip Select to set the Write Enable nop ; Wait nop ; Wait bcf PORTC,EICS ; Drop Chip Select so that write can start movlw SE_ERASE ; Get Chip erase command movwf byte2send ; Move to send data work location call Se_byte_send ; Send Chip Erase command to SEEPROM bsf PORTC,EICS ; Raise the Chip Select to start erase se_chip_erase_wait call Wait_5ms ; Wait 10 milliseconds for the erase command call Wait_5ms ; " return ; Erase completed, go back ; ***************************************************************************** ; * * ; * Purpose: Write a byte to Serial EEPROM. Write order is Most significant * ; * bit to least significant bit. * ; * * ; * Input: byte2send contains data byte to be written. * ; * Chip Select must be low (enabled). * ; * * ; * Output: Byte is written to the Serial EEPROM. * ; * * ; ***************************************************************************** Se_byte_send ; Send a byte to the Serial EEPROM bsf STATUS,C ; Set `end of loop' flag rlcf byte2send,f ; Place first bit into Carry se_byte_send_loop bcf PORTB,EIDI ; Precondition SEEPROM serial data to a zero bnc $+4 ; Check data - 0 or 1? bsf PORTB,EIDI ; Send one bsf PORTC,EICK ; Raise SEEPROM serial clock bcf PORTC,EICK ; and drop the clock bcf STATUS,C ; Clear data in Carry rlcf byte2send,f ; Place next bit into Carry tstfsz byte2send ; Test is data byte is all zeros bra se_byte_send_loop ; send next bit return ; Else, exit ;>>>>>>>>>>>>>>>>>>>>>>>>>> TIMING ROUTINE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; ***************************************************************************** ; * * ; * Purpose: Wait for a specified time period . * ; * * ; * Entry point Wait_a_sec : Wait for 1 second * ; * Entry point Wait_500ms : Wait for 0.5 second * ; * Entry point Wait_100ms : Wait for 100 msecond * ; * Entry point Wait_25ms : Wait for 25 msecond * ; * Entry point Wait_15ms : Wait for 15 msecond * ; * Entry point Wait_5ms : wait for 5 msecond * ; * Entry point Wait_1ms : Wait for 1 msecond * ; * Entry point Wait_.5ms : Wait for 0.5 msecond * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; ***************************************************************************** ; Wait_a_sec ; ****** Entry point ****** call Wait_500ms ; call Wait_500ms ; Wait_500ms ; ****** Entry point ****** call Wait_100ms ; call Wait_100ms ; call Wait_100ms ; call Wait_100ms ; call Wait_100ms ; return Wait_100ms ; ****** Entry point ****** movlw D'200' ; Set up outer loop movwf count ; counter to 200 bra outer_loop ; Go to wait loops Wait_25ms ; ****** Entry point ****** movlw D'50' ; Set up outer loop movwf count ; counter to 50 bra outer_loop ; Go to wait loops Wait_15ms ; ****** Entry point ****** movlw D'30' ; Set up outer loop movwf count ; counter to 30 bra outer_loop Wait_5ms ; ****** Entry point ****** movlw D'10' ; Set up outer loop movwf count ; counter to 10 bra outer_loop ; Go to wait loops Wait_1ms ; ****** Entry point ****** movlw D'2' ; Set up outer loop movwf count ; counter to 2 bra outer_loop ; Go to wait loop Wait_.5ms ; ****** Entry point ****** movlw D'1' ; Set up outer loop movwf count ; counter to 1 ; ; Wait loops used by other wait routines. outer_loop = 0.5 millisecond for ; 20 MHz microcontroller clock. ; outer_loop movlw D'0' movwf timer1 movlw D'0' movwf timer2 movlw D'0' movwf timer3 Delay_0 decfsz timer1,f goto $+2 decfsz timer2,f goto $+2 decfsz timer3,f goto Delay_0 movlw D'0' movwf timer1 movlw D'0' movwf timer2 movlw D'0' movwf timer3 Delay_1 decfsz timer1,f goto $+2 decfsz timer2,f goto $+2 decfsz timer3,f goto Delay_1 movlw D'0' movwf timer1 movlw H'32' movwf timer2 Delay_2 decfsz timer1,f goto $+2 decfsz timer2,f goto Delay_2 decfsz count,f ; Decrement outer loop counterFE) & 0xFF00 ; Move to next 256 byte boundry goto outer_loop ; If outer loop counter not down to zero, ; then go back to outer loop again return pgm_code_end ; End of Program Code, Tables follow. ORG (pgm_code_end + 0xFE) & 0xFF00 ;**************************************************************************** ; Message Tables ; Most of the LCD messages are stored here. The last byte of the message ; string is set to zero to indicate the end of string. The messages are ; indexed via an offset value, which is calculated at assembly time. ; The table is limited to 256 locations, cannot cross a 256 byte boundry ;**************************************************************************** messages ; Message Table 1 sign_msg_offset db "CEGen-DCPro V",CODE_VERSION,"", 0 ; Using Direct Conversion sign_msg equ sign_msg_offset - messages ver_msg_offset ; Call displayed at start up db " Buck - KD8RRI", 0 ; <<-- PUT YOUR NAME AND/OR CALL ver_msg equ ver_msg_offset - messages cal_msg_offset ; Displayed during Calibrate mode only db "CALIBRATE", 0 cal_msg equ cal_msg_offset - messages hz_msg_offset db "Hz", 0 ; Hz display hz_msg equ hz_msg_offset - messages menu_msg_offset db "MENU", 0 ; Menu message menu_msg equ menu_msg_offset - messages band_msg_offset db "Select Band", 0 ; Menu message band_msg equ band_msg_offset - messages mode_msg_offset db "Select Mode", 0 ; Menu message mode_msg equ mode_msg_offset - messages am_offset db "AM ", 0 am_msg equ am_offset - messages sbu_msg_offset db "US", 0 ; Menu message sbu_msg equ sbu_msg_offset - messages sbl_msg_offset db "LS", 0 ; Menu message sbl_msg equ sbl_msg_offset - messages cwplus_msg_offset db "C+", 0 ; Menu message cwplus_msg equ cwplus_msg_offset - messages cwmin_msg_offset db "C-", 0 ; Menu message cwmin_msg equ cwmin_msg_offset - messages dsbnd_msg_offset db "DS", 0 dsbnd_msg equ dsbnd_msg_offset - messages phase_msg_offset db "PM", 0 ; Menu message phase_msg equ phase_msg_offset - messages split_msg_offset db "Split Operation", 0 ; menu message split_msg equ split_msg_offset - messages no_msg_offset db " NO", 0 ; Menu message no_msg equ no_msg_offset - messages yes_msg_offset db "YES", 0 ; Menu message yes_msg equ yes_msg_offset - messages xmt_msg_offset db "-Tx", 0 ; Menu message xmt_msg equ xmt_msg_offset - messages rcv_msg_offset db "-Rx", 0 ; Menu message rcv_msg equ rcv_msg_offset - messages cw_adj_msg_offset db "Adjust CW Offset", 0 ; Menu message cw_adj_msg equ cw_adj_msg_offset - messages copy_msg_offset db "VFO Copied", 0 ; VFO copy message copy_msg equ copy_msg_offset - messages blank_msg_offset db " ", 0 blank_msg equ blank_msg_offset - messages mult_bad_msg_offset db "Clock Multiplier", 0 mult_bad_msg equ mult_bad_msg_offset - messages mult_bad_msg1_offset db "Not 1 + E 4 - 66", 0 mult_bad_msg1 equ mult_bad_msg1_offset - messages clock_high_msg_offset db "DDS Clock Is Too", 0 clock_high_msg equ clock_high_msg_offset - messages clock_high_msg1_offset db "High. > 1.2 GHz", 0 clock_high_msg1 equ clock_high_msg1_offset - messages message_table_end ORG (message_table_end + 0x00FE) & 0xFF00 ;******************************************************************************* ; ; Second Message Storage ; ;******************************************************************************* messages1 ; Message Table 2 rit_offset db "RIT Step ", 0 rit_step_msg equ rit_offset - messages1 blank_rit_offset db " ", 0 blank_rit equ blank_rit_offset - messages1 mem_msg_offset db "Memory ", 0 mem_msg equ mem_msg_offset - messages1 mem_retr_offset db "Retrieve?", 0 mem_retr equ mem_retr_offset - messages1 mem_save_offset db "SAVE? ", 0 mem_save equ mem_save_offset - messages1 mem_erase_offset db "ERASE? ", 0 mem_erase equ mem_erase_offset - messages1 full_buffer_offset db "Memory Is Full!", 0 full_buffer equ full_buffer_offset - messages1 end_of_mem_offset db " END OF MEMORY", 0 end_of_mem_msg equ end_of_mem_offset - messages1 end_mem_entry_offset db "NO MORE ENTRIES" end_mem_entry_msg equ end_mem_entry_offset - messages1 erase_msg_offset db "VFO-YES MENU-NO", 0 erase_msg equ erase_msg_offset - messages1 mem_encsw_msg_offset db "Tune-YES MENU-NO", 0 mem_encsw_msg equ mem_encsw_msg_offset - messages1 seprom_erase_offset db "ERASE SEPROM?", 0 seprom_erase equ seprom_erase_offset - messages1 seprom_strt_msg1 db " Erase Memory?", 0 strt_msg1 equ seprom_strt_msg1 - messages1 message1_table_end ORG (message1_table_end + 0x00FE) & 0xFF00 ; Move to next 256 byte boundry ;**************************************************************************** ; Decade Table ; ; Each entry is four bytes long, with each group of four bytes representing ; the frequency as a 32 bit integer. Each four byte entry represents a ; frequency digit. As the Entry Marker is moved to a frequency digit the ; appropriate decade value is loaded into fstep_3, fstep_2, fstep_1 and fstep_0. ; Data is stored MSByte first. As the tuning encoder is turned, this value is ; added to or subtracted from the displayed frequency. ; ;**************************************************************************** decade_tbl de 0x05, 0xF5, 0xE1, 0x00 ; 100 MHz increment de 0x00, 0x98, 0x96, 0x80 ; 10 MHz increment de 0x00, 0x0F, 0x42, 0x40 ; 1 MHz increment de 0x00, 0x01, 0x86, 0xA0 ; 100 KHz increment de 0x00, 0x00, 0x27, 0x10 ; 10 KHz increment de 0x00, 0x00, 0x03, 0xE8 ; 1 KHz increment de 0x00, 0x00, 0x00, 0x64 ; 100 Hz increment de 0x00, 0x00, 0x00, 0x0A ; 10 Hz increment de 0x00, 0x00, 0x00, 0x01 ; 1Hz increment ;**************************************************************************** ; ; Cursor Positioning Table ; ; Cursor positioning for frequency display is put into a table to simplify ; the software. The table defines LCD character position for most significant ; digit to the least significant digit. ; ;**************************************************************************** cursor_tbl_loc de D'1', D'2', D'3', D'5', D'6', D'7', D'9', D'10', D'11' cursor_tbl equ cursor_tbl_loc - decade_tbl c_d_table_end ;***************************************************************************** ; * ; LNA Boundry Table * ; * ; Each entry is two Most Significant Bytes 0f the Maximum Frequency of each * ; LNA. Periodically the table is compared to displayed frequency to be sure * ; correct Low Noise Amplifier is selected. * ; * ;***************************************************************************** LNA_bnd_tbl_loc ; Frequency ; MSByte LSByte de 0x00, 0x20 ; 2.12MHz - LNA #1 de 0x00, 0x44 ; 4.5MHz - LNA #2 de 0x00, 0x5D ; 6.15MHz - LNA #3 de 0x00, 0x81 ; 8.5MHz - LNA #4 de 0x00, 0xAC ; 11.3Mhz - LNA #5 de 0x00, 0xF5 ; 16.1MHz - LNA #6 de 0x01, 0x63 ; 23.3MHz - LNA #7 de 0x02, 0x0B ; 34.3MHz - LNA #8 de 0x03, 0x65 ; 57.0MHZ - LNA #9 de 0x08, 0xD3 ; 148.0 MHz - LNA #10 de 0x0D, 0x70 ; 225.0 MHZ - LNA #11 LNA_bnd_tbl equ LNA_bnd_tbl_loc - decade_tbl LNA_bnd_tbl_end LNA_bnd_tbl_lgth equ LNA_bnd_tbl_end -c_d_table_end ;**************************************************************************** ; ; S-Meter Table ; ; Bar Graph S-Meter Display on the R2Pro is 5 characters in length ; Table is ADC counts per bar on the Liquid Crystal Display (LCD). The LCD ; display is five blocks (characters) with 5 bars per block for a total of ; 25 bars. Thus the table has 25 entries. ; ;**************************************************************************** smeter_tbl_loc smeter_tbl equ smeter_tbl_loc - decade_tbl ; |---S0--|---S1----|---S2----|---S3----|---S4----| data 00D, 027, 042, 05C, 077, 091, 0AB, 0C5, 0DF, 0F9 ; ---S5----|---S6----|---S7----|---S8----|---S9----| data 113, 12D, 147, 161, 17B, 195, 1AF, 1C9, 1E3, 23B ; |---+20--|---+40---|+60| data 294, 2EC, 344, 39C, 3F4 smeter_tbl_end smeter_tbl_lgth equ smeter_tbl_end - LNA_bnd_tbl_end c_d_LNA_table_lgth equ smeter_tbl_end - decade_tbl ORG (smeter_tbl_end + 0x00FE) & 0xFF00 ; Move to next 256 byte boundry ; **************************************************************************** ; * Programmable Band Limit Tables - Direct Conversion * ; * * ; * The tables contain the first and last frequency of each band. They are * ; * used to test that the frequency has not exceeded the lower and upper * ; * band limits. * ; * The tables are in the same order as the band_tbl. Frequency is Hz, * ; * expressed in hexadecimal, Least Significant Byte first. * ; * To remove band limit, set the low limit table for 1.5 MHz and the high * ; * for the maximum frequency. See the entries for band 16. * ; * * ; * NOTE: If band_tbl is changed, these tables MUST also be changed * ; * * ; **************************************************************************** ; BAND LOW LIMIT TABLE low_tbl de 0x40, 0x77, 0x1b, 0x00 ; Band 0 1.800 MHz de 0xE0, 0x67, 0x35, 0x00 ; Band 1 3.500 MHz de 0x44, 0x56, 0x51, 0x00 ; Band 2 5.3305 Mhz de 0xC4, 0x94, 0x51, 0x00 ; Band 3 5.3465 MHz de 0xE4, 0xE2, 0x51, 0x00 ; Band 4 5.3665 MHz de 0x6C, 0xF6, 0x51, 0x00 ; Band 5 5.3715 MHz de 0x6C, 0x73, 0x52, 0x00 ; Band 6 5.4035 MHz de 0xC0, 0xCF, 0x6A, 0x00 ; Band 7 7.000 MHz de 0x20, 0x1D, 0x9A, 0x00 ; Band 8 10.100 MHz de 0x80, 0x9F, 0xD5, 0x00 ; Band 9 14.000 MHz de 0x20, 0xB2, 0x13, 0x01 ; Band 10 18.068 MHz de 0x40, 0x6F, 0x40, 0x01 ; Band 11 21.000 MHz de 0x90, 0xCA, 0x7B, 0x01 ; Band 12 24.890 MHz de 0x00, 0x3F, 0xAB, 0x01 ; Band 13 28.000 MHz de 0x80, 0xF0, 0xFA, 0x02 ; Band 14 50.000 MHz de 0x00, 0x44, 0x95, 0x08 ; Band 15 144.000 MHz de 0x80, 0x73, 0x3B, 0x0D ; Band 16 222.000 MHz de 0x60, 0xE3, 0x16, 0x00 ; Band 17 1.500 MHz low_tbl_end low_tbl_lgth equ low_tbl_end - low_tbl ORG (low_tbl_end + 0x00FE) & 0xFF00 ; Move to next 256 byte boundry ; **************************************************************************** ; BAND HIGH LIMIT TABLE high_tbl de 0x80, 0x84, 0x1E, 0x00 ; Band 0 2.000 Mhz - 160 Meters de 0x00, 0x09, 0x3D, 0x00 ; Band 1 4.000 MHz - 80 Meters de 0x44, 0x56, 0x51, 0x00 ; Band 2 5.3305 Mhz - 60 Meters CH1 de 0xC4, 0x94, 0x51, 0x00 ; Band 3 5.3465 MHz - 60 Meters CH2 de 0xE4, 0xE2, 0x51, 0x00 ; Band 4 5.3665 MHz - 60 Meters CH3 de 0x6C, 0xF6, 0x51, 0x00 ; Band 5 5.3715 MHz - 60 Meters CH4 de 0x6C, 0x73, 0x52, 0x00 ; Band 6 5.4035 MHz - 60 Meters CH5 de 0xA0, 0x63, 0x6F, 0x00 ; Band 7 7.300 MHz - 40 Meters de 0x70, 0xE0, 0x9A, 0x00 ; Band 8 10.150 MHz - 30 Meters de 0xB0, 0xF6, 0xDA, 0x00 ; Band 9 14.350 MHZ - 20 Meters de 0xC0, 0x38, 0x15, 0x01 ; Band 10 18.168 MHz - 17 Meters de 0x10, 0x4D, 0x47, 0x01 ; Band 11 21.450 MHz - 15 Meters de 0x30, 0x51, 0x7D, 0x01 ; Band 12 24.990 MHz - 12 Meters de 0xA0, 0x2F, 0xC5, 0x01 ; Band 13 29.700 MHz - 10 Meters de 0x80, 0xF9, 0x37, 0x03 ; Band 14 54.000 MHz - 6 Meters de 0x00, 0x4D, 0xD2, 0x08 ; Band 15 148.000 MHz - 2 Meters de 0x40, 0x3A, 0x69, 0x0D ; Band 16 225.000 MHZ - 1.25 Meters de 0x00, 0x84, 0xD7, 0x17 ; Band 16 400.000 MHz - Maximum high_tbl_end high_tbl_lgth equ high_tbl_end - high_tbl ORG ID_LOC ; ID Locations errorlevel -220 ; Eliminate maximum range warnings ;****************************************************************************** data "BER2P3q " ; BE R2Pro Rev. 3.q errorlevel +220 ; >>>>>>>>>>>>>>>>>>>>>>>>>> END OF FLASH MEMORY <<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>> EEPROM MEMORY <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ; ORG EEPROM_BASE restore_flag_loc restore_flag equ restore_flag_loc - EEPROM_BASE de 0x00 ; Restore flags ;***************************************************************************** ; * Programmable Band Memories. * ; * * ; * The following EEPROM locations store the user-programmed frequencies of * ; * their choice. The notion of bands is a loose one since any frequency can * ; * be stored here. The program defaults are the U.S. Amateur Band start * ; * frequencies for each band. * ; * * ; * Each band entry is 14 bytes in length. * ; * First 4 bytes is VFO-A starting frequency, in Hz, expressed in * ; * hexadecimal and is stored LSByte first. * ; * Next byte is the VFO-A default decade. This determines the initial Entry * ; * Marker location on the frequency display and the frequency digit that * ; * may be changed. * ; * Next byte is VFO-A Mode, that contains the default starting signal mode * ; * for VFO-A. AM = H'00', Upper SSB = H'01', Lower SSB = H'02', CW Positive * ; * = H'03', CW Negative = H'04', Double Sideband = H'05', Phase = H'06'. * ; * * ; * Following six bytes are equivalent six bytes for VFO-B. * ; * * ; * The next byte in each band table entry are Controls. * ; * Bits 0-3; Control Out select (LNA select, etc.) * ; * Bit 4; Future Use * ; * Bit 5; Future Use * ; * Bit 6; Future Use * ; * Bit 7; Cannot Transmit on this Band * ; * * ; * The last byte is the highest decade for this band table entry. This is * ; * is to limit the Entry Marker positioning to highest frequency digit for * ; * the band. * ; * * ; * IF FREQUENCY CHANGES ARE MADE TO BAND TABLE, THE BAND LIMITS TABLES, * ; * low_tbl AND high_tbl, MUST ALSO BE CHANGED. * ; * BAND TABLE IS LIMITED TO 18 ENTRIES * ;***************************************************************************** band_loc band_tbl equ band_loc - EEPROM_BASE ; ; VFO-A VFO-A VFO-A VFO-B VFO-B VFO-B EM Band ; Frequency Decade Mode Frequency Decade Mode Control Index # ; 76543210 de 0x40, 0x77, 0x1B, 0x00, D'6', H'02', 0x40, 0x77, 0x1B, 0x00, D'6', H'02', B'00000000', D'2' ; 1.800 MHz 0 de 0xE0, 0x67, 0x35, 0x00, D'6', H'02', 0xE0, 0x67, 0x35, 0x00, D'6', H'02', B'00000001', D'2' ; 3.500 MHz 1 de 0x44, 0x56, 0x51, 0x00, D'6', H'01', 0x44, 0x56, 0x51, 0x00, D'6', H'01', B'00000010', D'2' ; 5.3305 Mhz 2 de 0xC4, 0x94, 0x51, 0x00, D'6', H'01', 0xC4, 0x94, 0x51, 0x00, D'6', H'01', B'00000010', D'2' ; 5.3465 MHz 3 de 0xE4, 0xE2, 0x51, 0x00, D'6', H'01', 0xE4, 0xE2, 0x51, 0x00, D'6', H'01', B'00000010', D'2' ; 5.3665 MHz 4 de 0x6C, 0xF6, 0x51, 0x00, D'6', H'01', 0x6C, 0xF6, 0x51, 0x00, D'6', H'01', B'00000010', D'2' ; 5.3715 MHz 5 de 0x6C, 0x73, 0x52, 0x00, D'6', H'01', 0x6C, 0x73, 0x52, 0x00, D'6', H'01', B'00000010', D'2' ; 5.4035 Mhz 6 de 0xC0, 0xCF, 0x6A, 0x00, D'6', H'02', 0xC0, 0xCF, 0x6A, 0x00, D'6', H'02', B'00000011', D'2' ; 7.000 MHz 7 de 0x20, 0x1D, 0x9A, 0x00, D'6', H'03', 0x20, 0x1D, 0x9A, 0x00, D'6', H'03', B'00000100', D'1' ; 10.100 MHz 8 de 0x80, 0x9F, 0xD5, 0x00, D'6', H'01', 0x80, 0x9F, 0xD5, 0x00, D'6', H'01', B'00000101', D'1' ; 14.000 MHz 9 de 0x20, 0xB2, 0x13, 0x01, D'6', H'01', 0x20, 0xB2, 0x13, 0x01, D'6', H'01', B'00000110', D'1' ; 18.068 MHz 10 de 0x40, 0x6F, 0x40, 0x01, D'6', H'01', 0x40, 0x6F, 0x40, 0x01, D'6', H'01', B'00000110', D'1' ; 21.000 MHz 11 de 0x90, 0xCA, 0x7B, 0x01, D'6', H'01', 0x90, 0xCA, 0x7B, 0x01, D'6', H'01', B'00000111', D'1' ; 24.890 MHz 12 de 0x00, 0x3F, 0xAB, 0x01, D'6', H'01', 0x00, 0x3F, 0xAB, 0x01, D'6', H'01', B'00000111', D'1' ; 28.000 MHz 13 de 0x80, 0xF0, 0xFA, 0x02, D'6', H'01', 0x80, 0xF0, 0xFA, 0x02, D'6', H'01', B'00001000', D'1' ; 50.000 MHz 14 de 0x00, 0x44, 0x95, 0x08, D'6', H'01', 0x00, 0x44, 0x95, 0x08, D'6', H'01', B'00001001', D'0' ; 144.00 MHz 15 de 0x80, 0x73, 0x3B, 0x0D, D'6', H'01', 0x80, 0x73, 0x3B, 0x0D, D'6', H'01', B'00001010', D'0' ; 222.00 MHz 16 de 0x80, 0x96, 0x98, 0x00, D'6', H'00', 0x80, 0x96, 0x98, 0x00, D'6', H'00', B'10000100', D'0' ; 10.000 MHz 17 band_end_loc num_bands equ (band_end_loc - band_loc) / BAND_LGTH Max_band_no equ num_bands - 1 ;save_stuff_loc ;save_stuff equ save_stuff_loc - EEPROM_BASE ; de 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>> END OF EEPROM MEMORY <<<<<<<<<<<<<<<<<<<<<<<<< ; **************************************************************************** ; * ; SERIAL EPROM * ; * ; Serial EPROM is a Microchip 25AA1024 IC. Capacity is 1.024 Megabit or * ; 128,000 Bytes. Interface to the 25AA1024 is a Modified Serial Peripheral * ; Interface (SPI). * ; Control lines are: * ; EICS - RC2 - Chip Select * ; EIDO - RC0 - Data Out * ; EIDI - RB3 - Data In * ; EICK - RC7 - Data Clock * ; **************************************************************************** ; Layout of the memory in this application is: ; 0x00000 to 0x00007 Used to save Save-Retrieve Memory (S/R-M) Write Address ; and Control information. se_wriadr_save equ SEPROM_BASE+H'00' ; Saved SEPROM write address - 3 bytes save_misc_flags equ SEPROM_BASE+H'03' ; Miscellaneous Flags- SEEP_EN flag ; 0x00010 is start of the Restart Control save. clk_rat_save equ SEPROM_BASE+H'10' ; Saved Clock Ratio - 6 bytes restore_vfo equ SEPROM_BASE+H'16' ; vfo_select, THIS AND NEXT TWO LOCATIONS save_vfo_a equ SEPROM_BASE+H'17' ; VFO A band, MUST BE CONSECUTIVE save_vfo_b equ SEPROM_BASE+H'18' ; VFO B band, " dds_pdr_save equ SEPROM_BASE+H'19' ; DDS Power-Up & Reset register save saved_cwoffset equ SEPROM_BASE+H'1A' ; Saved CW Offset - 2 bytes saved_ritstep equ SEPROM_BASE+H'1C' ; Saved RIT Step Size - 1 byte ; * ; **************************************************************************** ; ; 0x00100 to 0x1FFFF 16 byte S/R-M save entries. Total of 7984 entries. * ; Defined by SE_MEM_STRT. SEPROM_BASE+H'100'. * SE_MEM_STRT equ 0x01 ; Starting address (seprom_a_1) ; ***************************************************************************** ; END