;!!!! MAJOR CHANGES MADE SEE FURTHER DOWN !!!! ;------------------------------------------------------------------------ ; ----- SELECT ONE OF THESE DDS TYPES AND TURN THE OTHER OFF !! --------- ;#define AD9850 ; Using the AD9850 (DDS-30) #define AD9851 ; Using the AD9851 (DDS-60) ; ----- SELECT 16-CHAR OR 8-CHAR LCD BY ENABLING ONE OF THESE --------- #define LCDCHAR 16 ; Turn on code for 16-character ;#define LCDCHAR 8 ; Turn on code for 8-character ; ; ---- MAY SELECT ONE OF THESE MULT FACTORS (NOT BOTH!) NORMALLY BOTH OFF -- ;#define MULTBY2 ; Generate DDS signal which is 2x frequency on LCD ;#define MULTBY4 ; Generate DDS signal which is 4x frequency on LCD ; #define LCD2LINES ; Turn on code for 2-line LCD (REQUIRES LCDCHAR 16) ; ; **************************************************************************** ; * PICELgen - Signal Generator (VFO) with AD9850/AD9851 DDS on PIC-EL * ; * Version 6.0c * ; * January 13, 2007 * ; * Author: Craig B Johnson, AA0ZZ * ; **************************************************************************** ; Description: ; Originally, this was the control program for a DDS VFO built with an AD9850 ; DDS chip, a shaft encoder, two push button switches and an Liquid crystal ; display. This version supports the newer DDS - the AD9851 as well as the ; AD9850. Simply select the DDS that you are using at the top of this file. ; ; Features: ; VARIABLE RATE TUNING based on the speed at which the encoder is turned. ; Pressing a pushbutton switch (PIC-EL PB_1) will change the step size from ; 1Hz to 1kHz. ; ; BAND MEMORIES a pushbutton switch (PIC-EL PB_2) allows the frequency to ; be cycled around the HF ham bands. ; ; CALIBRATE MODE is entered if a pushbutton switch (PIC-EL PB_1) is pressed ; during power-on. The display is set to 10 MHz and remains fixed, ; even as adjustments are being made. If pushbutton is held pressed, then ; turning the shaft encoder will increase or decrease the value "osc" used to ; calculate the DDS control word. The basic calibrate adjustment rate is very ; low (on the order of a few cycles per turn of the encoder). A somewhat ; faster adjustment speed is available by pressing the encoder shaft down ; while turning. ; An external frequency counter on the DDS output is required to observe this ; adjustment. To exit calibrate mode, release the pushbutton and turn the ; shaft encoder one more time. The calibrated value of "osc" will then be ; stored in EEPROM memory. ; ; MOVE UP 1 MHz - Press and hold PIC-EL pushbutton PB_2 and then press and ; then press and release PIC-EL pushbutton PB_1. ; ; MOVE DOWN 1 MHz - Press and hold PIC-EL pushbutton PB_1 and then press and ; then press and release PIC-EL pushbutton PB_2. ; ; SAVE NEW START-UP FREQUENCY - Select the frequency. Then press and hold ; PIC-EL pushbuttons PB_1 and PB_2 for 2 seconds. The frequency will be ; stored in EEPROM and will be used on next start-up. ; ; The MULTBY2/4 feature allows a user to use PICELgen with a quadrature sampling ; detector. The QSD mechanism divides the "input" frequency by 2 or 4, so the DDS ; generated frequency must be 2 or 4 times as large to generate a chosen frequency. ; The modification, enabled via a "DEFINE" statement at the top of this listing, ; causes the frequency word to be multiplied by 2 or 4 before being sent to the DDS. ; The original frequency is displayed on the LCD and all frequency calculations ; (UP/DN MHz, encoder changes, etc) are done with the standard frequency. ;****************************************************************************** ; Original Author - Curtis W. Preuss - WB2V ; ; Modification History ; 8/19/98 - Version 1 - Initial Version by Curtis W. Preuss - WB2V ; 12/xx/98 - Version 2 - Converted to MPASM by Bruce Stough, AA0ED ; 4/21/99 - Version 3 - Fixed and modified by ; Bruce Stough, AAED (sbs1@visi.com) and ; Craig Johnson, AA0ZZ (cbjohns@cbjohns.com) ; ; 10/31/03 PICELgen1.0 Modify for PIC Elmer project by Craig Johnson, AA0ZZ ; 1) Change to use 1x8 LCD instead of 1x16 ; - Freq displayed as Hz (e.g. 14025000) ; - CAL freq displayed as Hz (e.g. 10000000) ; 2) Set up to support the NJQRP DDS Daughterboard ; - 50 MHz oscillator ; 3) Change PORTB pin allocations to support PIC-EL ; - Change RB4-RB7 to RB0-RB3 ; - Change RB0 (DDS LOAD) to RB7 ; - Change RB1 (LCD_rs) to RB6 ; - Change RB2 (LCD_rw) to RB5 ; - Change RB3 (LCD_e) to RB4 ; - Change Busy_check routine to check correct bit ; - Change cmnd2LCD/data2LCD routine - swap nibbles ; 4) Change RA2 to always be an LOW output ; 5) Support pushbutton on RA3 instead of encoder ; shaft switch for bandswitch and calibrate ; 6) Support pushbutton on RA4 for fast tuning ; ; 1/3/04 PICELgen1.1 Add Title and Version on start-up ; 1/7/04 PICELgen1.2 Restructure main loop and change_band ; 2/2/04 PICELgen1.2a Fix confusing comment in the header re shaft sw. ; Fix comment in header regarding PB_2 for calib. ; 2/8/04 PICELgen1.3 Fix for reliable startup of DDS ; Add code for 1 MHz steps (up or down) ; Add code to save new start-up frequency ; 3/12/04 PICELgen1.4 Remove use of watchdog timer (temp sensitivity) ; Add code to support either 1x8 or 1x16 LCDs ; - Use #DEFINE to select the LCD type ; Fix calibrate routine so it stays in cal_loop ; 12/20/05 PICELgen4.0 Updated to use AD9851 instead of AD9850 ; 12/25/05 PICELgen5.0 Support BOTH AD9850 and AD9851 via complier option ; 12/29/05 PICELgen5.1 Fix bug. Speaker being left on, wasting 73mA ; 1/13/06 PICELgen5.2 Support MULTBY2 or MULTBY4 ; Support 16x2 LCD also (#DEFINE LCD2LINES) ; 1/16/06 PICELgen6.0 Ported to 16F628 ; 6/16/06 PICELgen6.0a Fix bug in x2 and x4 code (sometimes jumps in freq) ; (Thanks PA0RWE!) ; 12/12/06 PICELgen6.0b Fix hang in calibration routine (pb conflict) ; Fix EEPROM WRITE mechanism after calibration ; 1/13/07 PICELgen6.0c Clean up. Use full Microchip dev files instead of ; bringing in subset ; ; 17/5/2012 Changed config word so that PIC runs on internal clock - Terry Mowles VK5TM ; 13/01/2013 PEGen7_1 Removed variable rate tuning and changed code to support ; mechanical encoder ; ; 14/01/2013 PEGen7_2 Major changes to code - see notes above ; ; 1/04/2013 PEGen7_2a Added initialise DDS to zero freq at start up to prevent hash ; generation as per datasheet - Terry Mowles VK5TM ; ;****************************************************************************** ; 14/01/2013 - Changes - by Terry Mowles VK5TM ; ; The major changes to the functionality of this code is as follows:- ; ; Code modified to suit a mechanical encoder (optical encoder not supported) ; Variable rate tuning removed ; ; PB_1 functions changed - still used for entry to CALIBRATION mode ; - BAND CHANGE function moved to this button ; ; PB_2 function changed - now selects between 1Hz, 1kHz and 1MHz steps ; - default is 1Hz steps ; - push once to move to 1kHz, push again to move to 1MHz steps ; push again to return to 1Hz steps ; - Step size is indicated on the bottom left of the LCD ; - you do not need to hold the button once the step has been selected ; ; PB_3 added - This button is used exclusively to save the current frequency ; to memory as the start-up frequency. ; - Press and hold for 2 seconds. The LCD will show ; 'SAVED' on the bottom right hand side of the screen for 1s ; ; ; ;***************************************************************************** ; ; Target Controller - PIC16F628A in PIC-EL board ; __________ ; PB_3-Speaker----RA2 |1 18| RA1---------ENCODER A ; PB_2-Bandswitch-RA3 |2 17| RA0---------ENCODER B ; PB_1-Tuning etc-RA4 |3 16| OSC1--------XTAL ; +5V-----------!MCLR |4 15| OSC2--------XTAL ; Ground----------Vss |5 14| VDD---------+5 V ; LCD D4----------RB0 |6 13| RB7---------DDS_LOAD ; LCD D5----------RB1 |7 12| RB6---------LCD_rs (LCD Pin 4) ; LCD D6/DDS_CLK--RB2 |8 11| RB5---------LCD_rw (LCD Pin 5) ; LCD D7/DDS_DATA-RB3 |9 10| RB4---------LCD_e (LCD Pin 6) ; ---------- ; ; **************************************************************************** ; * Device type and options. * ; **************************************************************************** ; processor 16F628A radix dec errorlevel -302 ; Skip out of bank nuisance messages ; **************************************************************************** ; * Configuration fuse information for 16F628A: * ; **************************************************************************** include __config _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_ON&_PWRTE_ON&_WDT_OFF& _INTOSC_OSC_NOCLKOUT ; ; *************************************************************************** ; Info for power-up display MCODE_REV_0 equ '7' ; Current code version is 7.0 MCODE_REV_1 equ '.' ; MCODE_REV_2 equ '2' ; MCODE_REV_3 equ '-' ; #ifdef AD9850 MCODE_REV_4 equ '3' ; MCODE_REV_5 equ '0' ; #endif #ifdef AD9851 MCODE_REV_4 equ '6' ; MCODE_REV_5 equ '0' ; #endif #ifdef MULTBY2 MCODE_REV_6 equ 'x' ; MCODE_REV_7 equ '2' ; #else #ifdef MULTBY4 MCODE_REV_6 equ 'x' ; MCODE_REV_7 equ '4' ; #else MCODE_REV_6 equ ' ' ; MCODE_REV_7 equ ' ' ; #endif #endif ; ; **************************************************************************** ; * General equates. These may be changed to accommodate the reference clock* ; * frequency, the desired upper frequency limit, and the default startup * ; * frequency. * ; **************************************************************************** ; ; ref_osc represents the change in the frequency control word which results ; in a 1 Hz change in output frequency. It is interpreted as a fixed point ; integer in the format . ; ; The values for common oscillator frequencies are as follows: ; ; Frequency ref_osc_3 ref_osc_2 ref_osc_1 ref_osc_0 ; ; 180.00 MHz 0x17 0xDC 0x65 0xDF ; 125.00 MHz 0x22 0x5C 0x17 0xD0 ; 120.00 MHz 0x23 0xCA 0x98 0xCE ; 100.00 MHz 0x2A 0xF3 0x1D 0xC4 ; 90.70 MHz 0x2F 0x5A 0x82 0x7A ; 66.66 MHz 0x40 0x6E 0x52 0xE7 ; 66.00 MHz 0x41 0x13 0x44 0x5F ; 50.00 MHz 0x55 0xE6 0x3B 0x88 ; 30.00 MHz 0x8F 0x2A 0x63 0x39 ; ; To calculate other values: ; ref_osc_3 = (2^32 / oscillator_freq_in_Hertz). ; ref_osc_2, ref_osc_1, and ref_osc_0 are the fractional part of ; (2^32 / oscillator_freq_in_Hertz) times 2^24. ; Note: 2^32 = 4294967296 and 2^24 = 16777216 ; ; For example, for a 120 MHz clock: ; ref_osc_3 is (2^32 / 120 x 10^6) = 35.791394133 truncated to 35 (0x23) ; ref_osc_2 is the high byte of (.791394133 x 2^24) = 13277390.32 ; 13277390.32 = 0xCA98CE, so high byte is CA. ; ref_osc_1 is the next byte of 0xCA98CE, or 98 ; ref_osc_0 is the last byte of 0xCA98CE, or CE ; ; For example, for a 180 MHz clock: ; ref_osc_3 is (2^32 / 180 x 10^6) = 23.860929422 truncated to 23 (0x17) ; ref_osc_2 is the high byte of (.860929422 x 2^24) = 14443998 ; 14443998 = 0xDC65DE, so high byte is 14. ; ref_osc_1 is the next byte of 0xDC65DE, or 65 ; ref_osc_0 is the last byte of 0xDC65DE, or DE ; #ifdef AD9850 ; For 125 MHz Oscillator ======= ref_osc_3 equ 0x22 ; Most significant osc byte ref_osc_2 equ 0x5C ; Next byte ref_osc_1 equ 0x17 ; Next byte ref_osc_0 equ 0xD0 ; Least significant byte #endif #ifdef AD9851 ; For 180 MHz (30 MHz clock and 6x multiplier) ref_osc_3 equ 0x17 ; Most significant osc byte ref_osc_2 equ 0xDC ; Next byte ref_osc_1 equ 0x65 ; Next byte ref_osc_0 equ 0xDE ; Least significant byte #endif ; Limit contains the upper limit frequency as a 32 bit integer. ; This should not be set to more than one third of the reference oscillator ; frequency. The output filter of the DDS board must be designed to pass ; frequencies up to the maximum. ; #ifdef AD9850 #ifdef MULTBY2 limit_3 equ 0x00 ; Most significant byte for 15 MHz limit_2 equ 0xE4 ; Next byte limit_1 equ 0xE1 ; Next byte limit_0 equ 0xC0 ; Least significant byte #else #ifdef MULTBY4 limit_3 equ 0x00 ; Most significant byte for 7.5 MHz limit_2 equ 0x72 ; Next byte limit_1 equ 0x70 ; Next byte limit_0 equ 0xE0 ; Least significant byte #else // No multiplier limit_3 equ 0x01 ; Most significant byte for 30 MHz limit_2 equ 0xC9 ; Next byte limit_1 equ 0xC3 ; Next byte limit_0 equ 0x80 ; Least significant byte #endif #endif #endif // AD9850 #ifdef AD9851 #ifdef MULTBY2 limit_3 equ 0x01 ; Most significant byte for 30 MHz limit_2 equ 0xC9 ; Next byte limit_1 equ 0xC3 ; Next byte limit_0 equ 0x80 ; Least significant byte #else #ifdef MULTBY4 limit_3 equ 0x00 ; Most significant byte for 15 MHz limit_2 equ 0xE4 ; Next byte limit_1 equ 0xE1 ; Next byte limit_0 equ 0xC0 ; Least significant byte #else ; No multiplier limit_3 equ 0x03 ; Most significant byte for 60 MHz limit_2 equ 0x93 ; Next byte limit_1 equ 0x87 ; Next byte limit_0 equ 0x00 ; Least significant byte #endif #endif #endif ; AD9851 ; ; Default contains the default startup frequency as a 32 bit integer. ; #ifdef AD9850 #ifdef MULTBY4 ; Max is 7.5 MHz, so start at 7.0 MHz default_3 equ 0x00 ; Most significant byte for 7.0 MHz default_2 equ 0x6A ; Next byte default_1 equ 0xCF ; Next byte default_0 equ 0xC0 ; Least significant byte #else default_3 equ 0x00 ; Most significant byte for 14.025 MHz default_2 equ 0xD6 ; Next byte default_1 equ 0x01 ; Next byte default_0 equ 0x28 ; Least significant byte #endif #endif #ifdef AD9851 default_3 equ 0x00 ; Most significant byte for 14.025 MHz default_2 equ 0xD6 ; Next byte default_1 equ 0x01 ; Next byte default_0 equ 0x28 ; Least significant byte #endif ; band_end equ 0x28 ; The offset to the last band table entry ; ; PB_flags bits PB1Calibrate_Active equ 0 ; Bit set indicates calibrate is now active HZ10 equ 1 HZ100 equ 2 KHZ equ 3 MHZ equ 4 ; EEstartup_adr equ 4 ; Location of startup frequency in EEPROM ; ; **************************************************************************** ; * Setup the initial constant, based on the frequency of the reference * ; * oscillator. This can be tweaked with the calibrate function. * ; **************************************************************************** ; ORG 0x2100 ; ref_osc bytes must be first 4 bytes of EEPROM DATA ref_osc_0 DATA ref_osc_1 DATA ref_osc_2 DATA ref_osc_3 ; startup frequency bytes must be next 4 bytes of EEPROM DATA default_0 ; startup -> freq_0 DATA default_1 ; startup -> freq_1 DATA default_2 ; startup -> freq_2 DATA default_3 ; startup -> freq_3 ; ; Clear unused EEPROM bytes (128 bytes for 16F628) ; DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; ; **************************************************************************** ; * Assign names to IO pins. * ; **************************************************************************** ; ; B register bits: ; DDS_clk equ 0x02 ; AD9850/AD9851 write clock DDS_dat equ 0x03 ; AD9850/AD9851 serial data input LCD_busy equ 0x03 ; LCD busy bit LCD_e equ 0x04 ; 0=disable, 1=enable LCD_rw equ 0x05 ; 0=write, 1=read LCD_rs equ 0x06 ; 0=instruction, 1=data DDS_load equ 0x07 ; Update pin on AD9850/AD9851 ; ; A register bits: ; speaker equ 0x02 ; Speaker (always exit with a low output) pb_3 equ 0x02 ; PB also on this pin pb_2 equ 0x03 ; Bandswitch Pushbutton, pb_1 equ 0x04 ; Tuning-increment/Calibrate Pushbutton ; ; **************************************************************************** ; * Allocate variables in general purpose register space * ; **************************************************************************** ; CBLOCK 0x20 ; Start Data Block ; freq_0 ; Display frequency (hex) freq_1 ; (4 bytes) freq_2 freq_3 freq_0A ; Display temp frequency (hex) (MULTBY2/4) freq_1A ; (4 bytes) freq_2A freq_3A BCD_0 ; Display frequency (BCD) BCD_1 ; (5 bytes) BCD_2 BCD_3 BCD_4 AD9851_0 ; AD9850/AD9851 control word AD9851_1 ; (5 bytes) AD9851_2 AD9851_3 AD9851_4 fstep_0 ; Frequency inc/dec fstep_1 ; (4 bytes) fstep_2 fstep_3 BCD_count ; Used in bin2BCD routine BCD_temp ; " mult_count ; Used in calc_dds_word bit_count ; " byte2send ; osc_0 ; Current oscillator osc_1 ; (4 bytes) osc_2 osc_3 osc_temp_0 ; Oscillator frequency osc_temp_1 ; (4 bytes) osc_temp_2 osc_temp_3 LCD_char ; Character being sent to the LCD LCD_read ; Character read from the LCD timer1 ; Used in delay routines timer2 ; " ren_timer_0 ; For variable rate tuning ren_timer_1 ; (2 bytes) ren_new ; New value of encoder pins A and B ren_old ; Old value of encoder pins A and B ren_read ; Encoder pins A and B and switch pin last_dir ; Indicates last direction of encoder next_dir ; Indicates expected direction count ; loop counter (gets reused) enc_counter ; Divide by 4 counter for mechanical encoder band ; Used to index a table of frequencies rs_value ; The LCD rs line flag value PB_FLAGS ; Pushbutton flags PB_wait_count ; Wait for 2 seconds ; ENDC ; End of Data Block ; ; **************************************************************************** ; * The 16F84 resets to 0x00. * ; * The Interrupt vector is at 0x04. (Unused) * ; **************************************************************************** ; ORG 0x0000 reset_entry goto start ; Jump around the band table to main program ; ; **************************************************************************** ; * This is the band table. Each entry is four instructions long, with each * ; * group of four literals representing the frequency as a 32 bit integer. * ; * New entries can be added to the end of the table or between existing * ; * entries. The constant band_end must be incremented by 4 for each entry * ; * added. * ; * * ; * This table is placed near the top of the program to allow as large a * ; * a table as possible to be indexed with the eight bit value in W. * ; * * ; **************************************************************************** ; band_table addwf PCL,f ; retlw 0x00 ; 0 Hz retlw 0x00 ; retlw 0x00 ; retlw 0x00 ; retlw 0x00 ; 160 meters retlw 0x1B ; retlw 0x77 ; retlw 0x40 ; retlw 0x00 ; 80 meters retlw 0x35 ; retlw 0x67 ; retlw 0xE0 ; retlw 0x00 ; 40 meters retlw 0x6A ; retlw 0xCF ; retlw 0xC0 ; retlw 0x00 ; 30 meters retlw 0x9A ; retlw 0x1D ; retlw 0x20 ; retlw 0x00 ; 20 meters retlw 0xD5 ; retlw 0x9F ; retlw 0x80 ; retlw 0x01 ; 17 meters retlw 0x13 ; retlw 0xB2 ; retlw 0x20 ; retlw 0x01 ; 15 meters retlw 0x40 ; retlw 0x6F ; retlw 0x40 ; retlw 0x01 ; 12 meters retlw 0x7B ; retlw 0xCA ; retlw 0x90 ; retlw 0x01 ; 10 meters retlw 0xAB ; retlw 0x3F ; retlw 0x00 ; retlw 0x01 ; 30 MHz retlw 0xC9 ; retlw 0xC3 ; retlw 0x80 ; ; ; ***************************************************************************** ; * * ; * Purpose: This is the start of the program. It initializes the LCD and * ; * detects whether to enter calibrate mode. If so, it calls the * ; * Calibrate routine. Otherwise, it sets the power-on frequency * ; * and enters the loop to poll the encoder. * ; * * ; * Input: The start up frequency is defined in the default_3 ... * ; * definitions above, and relies on the reference oscillator * ; * constant defined in ref_osc_3 ... ref_osc_0. * ; * * ; * Output: Normal VFO operation. * ; * * ; ***************************************************************************** ; start clrf INTCON ; No interrupts for now movlw 0x07 ; Code to turn off the analog comparitors movwf CMCON ; Turn off comparators call wait_8ms ; Wait for LCD to settle bsf STATUS,RP0 ; Switch to bank 1 bcf 0x01,7 ; Enable weak pullups movlw 0xFF ; Tristate PORTA (all Inputs ) movwf TRISA ; clrf TRISB ; Set port B to all outputs bcf STATUS,RP0 ; Switch back to bank 0 ; Initialize DDS Module with zero freq clrf AD9851_0 ; AD9850/51 control word clrf AD9851_1 ; (5 bytes) clrf AD9851_2 clrf AD9851_3 clrf AD9851_4 call send_dds_word ; Send it to the DDS call send_dds_word ; twice to be sure ; bcf PORTA,speaker ; Set speaker output low (not uses here) call init_LCD ; Initialize the LCD call display_version ; Display title and version ; ; Enter Calibrate Mode if push button is pressed while turning the ; power on. ; clrf PB_FLAGS ; Default - calibrate is not active btfsc PORTA,pb_1 ; Tuning-increment/Cal pushbutton pressed? goto read_EEocs ; No, get clock freq from EEPROM bsf PB_FLAGS,PB1Calibrate_Active ; Calibrate is now active call calibrate ; and call routine bcf PB_FLAGS,PB1Calibrate_Active ; Now calibrate is done ; ; Get the reference oscillator constant from the EEPROM. ; read_EEocs bsf STATUS,RP0 ; Switch to bank 1 clrf EEADR ; Reset the EEPROM read address call read_EEPROM ; Read EEPROM (all in bank 1) movf EEDATA,w ; Get the first osc byte bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_0 ; Save osc frequency bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_1 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_2 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_3 ; Save it ; ; Set the power on frequency to the defined value. ; (They always follow the osc bytes) ; bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM movf EEDATA,w ; Get the first default freq byte bcf STATUS,RP0 ; Back to bank 0 for store movwf freq_0 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM movf EEDATA,w ; Get the next freq byte bcf STATUS,RP0 ; Back to bank 0 for store movwf freq_1 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM movf EEDATA,w ; Get the next freq byte bcf STATUS,RP0 ; Back to bank 0 for store movwf freq_2 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read EEPROM movf EEDATA,w ; Get the last freq byte bcf STATUS,RP0 ; Back to bank 0 for store movwf freq_3 ; Save it ; ; Display the power on frequency. ; call bin2BCD ; Convert it to BCD call show_freq ; Display it ; ; Send power on frequency to the DDS chip. ; call calc_dds_word ; Convert to delta value call send_dds_word ; Send the power-on frequency to the ; AD9850/AD9851 in serial mode ; call send_dds_word ; Send it twice. Sometimes needed for ; a clean start-up ; ; Get the power on encoder value. ; movf PORTA,w ; Read port A movwf ren_read ; Save it in ren_read movlw 0x03 ; Get encoder mask (RA0 and RA1) andwf ren_read,w ; Get encoder bits movwf ren_old ; Save in ren_old ; ; Initialize variables. ;***************************************************************************** ; clrf ren_timer_1 ; Initialize the encoder speed timer > ; movlw 0x40 ; to >Used in variable rate tuning ; movwf ren_timer_0 ; 0x0040 > ;***************************************************************************** clrf last_dir ; Clear the knob direction indicator clrf band ; Clear the band indicator ; ; Fall into the Main Program Loop ; ; ***************************************************************************** ; * * ; * Purpose: This is the Main Program Loop. The program's main loop * ; * calls poll_encoder, which continuously polls the rotary shaft * ; * encoder. When the shaft encoder has changed, the direction * ; * it moved is determined and stored in last_dir. The subroutine * ; * then returns to main. * ; * * ; * If the tuning-increment pushbutton (PIC-EL PB_1) is not pressed,* ; * then the variable fstep is calculated based on the delay * ; * between shaft encoder changes. ren_timer contains the delay * ; * value determined by the poll_encoder subroutine. The variable * ; * fstep is added or subtracted from the current VFO frequency * ; * stored in freq. The contents of freq are then converted to a * ; * BCD number in subroutine bin2BCD. The subroutine show_freq is * ; * then called to display the result on the Liquid Crystal Display.* ; * Next, the subroutine calc_dds_word is used to calculate the DDS * ; * frequency control word from the values in freq and osc. * ; * The result is stored in AD9850/AD9851. This data is transferred* ; * to the AD9851 DDS chip by calling the subroutine send_dds_word. * ; * * ; * If the bandswitch pushbutton (PIC-EL PB_2) is pressed while * ; * turning the encoder then the freq is loaded with a constant * ; * stored in band_table. The variable band is used as an index * ; * into the table. Band is incremented or decremented based on * ; * the encoder direction. * ; * * ; * Input: None. * ; * * ; * Output: None. * ; * * ; ***************************************************************************** ; main call poll_encoder ; Check for knob movement (wait there!) ; Return here when encoder change detected ;------------------------------------------------------------------------------------ ; btfsc ren_read,pb_2 ; Is bandswitch pushbutton pressed? ORIGINAL CODE ;------------------------------------------------------------------------------------ btfsc ren_read,pb_1 ; Is bandswitch pushbutton pressed? goto step ; No, just step call change_band ; Yes, change_band goto main ; Continue main loop ;##################################################################################### ;-----------------------------------ORIGINAL CODE------------------------------------ ;step ; ;; ;; Determine step size to use (1 Hz or 1 kHz). ;; ; clrf fstep_3 ; Guess that we want 1 Hz steps by ; clrf fstep_2 ; setting fstep to one. ; clrf fstep_1 ; ; movlw 0x01 ; ; movwf fstep_0 ; ; btfsc ren_read,pb_1 ; Is the tuning-increment button pressed? ; goto go_step ; No, use the 1 Hz step ; movlw 0xE8 ; Yes, set the step value to 1 kHz ; movwf fstep_0 ; by setting fstep_0 to 0xE8 and ; movlw 0x03 ; fstep_1 to 0x03 ; movwf fstep_1 ; ; goto go_step ; Use the 1 kHz step ;; ;; Adjust the tuning step based on ren_timer. ren_timer is incremented ;; by 8 from its initial value of 0x0040 each time the poll_encoder finds ;; no change in the encoder input, until the high bit of ren_timer_1 ;; becomes a one. The default fstep of 1 Hz is multiplied by two for ;; each leading zero in ren_timer, up to a maximum of 9 times. (This is ;; because ren_timer starts at 0x0040, only the first nine bits can be ;; zero in a row). The faster the knob is turned, the lower the number ;; in ren_timer will be, and the larger the step value will be. ;; ;;****************************************************************************** ;;bump_step ;; bcf STATUS,C ; Clear the carry flag ;; rlf fstep_0,f ; Multiply the step by 2 by rotating left ;; rlf fstep_1,f ; Used in variable rate tuning ;; rlf fstep_2,f ; ;; rlf fstep_3,f ; ;;****************************************************************************** ;go_step ;;****************************************************************************** ; ; rlf ren_timer_0,f ; Multiply the encoder timer by 2 > ; ; rlf ren_timer_1,f ; > Used in variable rate tuning ; ; btfss STATUS,C ; Has a one floated to the carry yet? > ; ; goto bump_step ; No, then double the step size > ;;****************************************************************************** ;; Based on the knob direction, either add or subtract the increment, ;; then update the LCD and DDS. ;; ; btfsc last_dir,1 ; Is the knob going up? ; goto up ; Yes, then add the increment ;down ; call sub_step ; Subtract fstep from freq ; goto update ; Update LCD and DDS ;up ; call add_step ; Add fstep to freq ; call check_add ; Make sure we did not exceed the maximum ;update ; call bin2BCD ; Convert the frequency to BCD ; call show_freq ; Display the frequency on the LCD ; call calc_dds_word ; Find the control word for the DDS chip ; call send_dds_word ; Send the control word to the DDS chip ; goto main ; Continue main loop ;; ;-------------------------MODIFIED CODE---------------------------------------------- step ; ; Determine step size to use (1 Hz or 1 kHz or 1MHz). ; clrf fstep_3 ; clrf fstep_2 ; clrf fstep_1 ; btfsc PB_FLAGS,KHZ ; test if 1khz flag pb_flags,khz bit is set goto khz ; yes - goto khz btfsc PB_FLAGS,MHZ ; test if 1MHz flag pb_flags,mhz bit is set goto mhz ; yes - goto mhz movlw 0x01 ; Guess that we want 1 Hz steps by movwf fstep_0 ; setting fstep to one. ; display step 1Hz on LCD goto go_step ; khz movlw 0xE8 ; Setup for step of 1 kHz movwf fstep_0 ; movlw 0x03 ; movwf fstep_1 ; ; display step 1kHz on LCD goto go_step ; mhz movlw 0x0F ; Setup for step of 1 MHz movwf fstep_2 ; movlw 0x42 ; movwf fstep_1 ; movlw 0x40 ; movwf fstep_0 ; ; display step 1MHz on LCD go_step ; Based on the knob direction, either add or subtract the increment, ; then update the LCD and DDS. ; btfsc last_dir,1 ; Is the knob going up? goto up ; Yes, then add the increment down call sub_step ; Subtract fstep from freq goto update ; Update LCD and DDS up call add_step ; Add fstep to freq call check_add ; Make sure we did not exceed the maximum update call bin2BCD ; Convert the frequency to BCD call show_freq ; Display the frequency on the LCD call calc_dds_word ; Find the control word for the DDS chip call send_dds_word ; Send the control word to the DDS chip goto main ; Continue main loop ;################################################################################## ; ***************************************************************************** ; * * ; * Purpose: This routine increments through the band table each time the * ; * knob moves a notch, updating the LCD and DDS, until the band * ; * button is no longer pushed. * ; * * ; * Input: The value of the band push button and the encoder bits * ; * * ; * Output: Updated freq value, and new frequency on the LCD and DDS. * ; * * ; ***************************************************************************** ; change_band btfsc last_dir,1 ; Are we going up in the band list? goto band_up ; Yes, increment band address movlw 0x04 ; No, get 4 bytes to subtract subwf band,f ; Move down in band list movlw 0xFF-band_end ; Check to see if we have fallen off the addwf band,w ; bottom of the table. btfss STATUS,C ; Off the bottom? goto valid ; No, continue movlw band_end ; Yes, go to highest entry movwf band ; valid call get_band ; Get the new band frequency goto write ; Set the frequency and continue band_up movlw 0x04 ; Table entries are 4 bytes apart addwf band,f ; Increment the band pointer movlw 0xFF-band_end ; Check to see if we have gone over the addwf band,w ; top of the table. btfsc STATUS,C ; Did we go over the top of the table? clrf band ; Yes, go to the bottom entry call get_band ; Get the new band frequency write call bin2BCD ; Convert the frequency to BCD call show_freq ; Display the frequency on the LCD call calc_dds_word ; Find the control word for the DDS chip call send_dds_word ; Send the control word to the DDS chip return ; Return to main loop ; ; ***************************************************************************** ; * * ; * Purpose: This routine reads the frequency value of a band table entry * ; * pointed to by band and returns it in freq_3...freq_0. * ; * * ; * Input: band must contain the index of the desired band entry * 4 * ; * (with the entries numbered from zero). * ; * * ; * Output: The band frequency in freq. * ; * * ; ***************************************************************************** ; get_band movf band,w ; Get the index of the high byte call band_table ; Get the value into W movwf freq_3 ; Save it in freq_3 incf band,f ; Increment index to next byte movf band,w ; Get the index of the next byte call band_table ; Get the value into W movwf freq_2 ; Save it in freq_2 incf band,f ; Increment index to the next byte movf band,w ; Get the index to the next byte call band_table ; Get the value into W movwf freq_1 ; Save it in freq_1 incf band,f ; Increment index to the low byte movf band,w ; Get the index to the low byte call band_table ; Get the value into W movwf freq_0 ; Save it in freq_0 movlw 0x03 ; Get a constant three subwf band,f ; Restore original value of band return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: Power on initialization of Liquid Crystal Display. The LCD * ; * controller chip must be equivalent to an Hitachi 44780. The * ; * LCD is assumed to be a 8x1 or a 16x1 display. * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; ***************************************************************************** ; init_LCD call wait_64ms ; Wait for LCD to power up ; Put 4-bit command in RB3..RB0 ; PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11) movlw 0x03 ; LCD init instruction (First) movwf PORTB ; Send to LCD via RB3..RB0 bsf PORTB,LCD_e ; Set the LCD E line high, call wait_64ms ; wait a "long" time, bcf PORTB,LCD_e ; and then Clear E movlw 0x03 ; LCD init instruction (Second) movwf PORTB ; Send to LCD via RB3..RB0 bsf PORTB,LCD_e ; Set E high, call wait_32ms ; wait a while, bcf PORTB,LCD_e ; and then Clear E movlw 0x03 ; LCD init instruction (Third) movwf PORTB ; Send to LCD via RB3..RB0 bsf PORTB,LCD_e ; Set E high, call wait_32ms ; wait a while, bcf PORTB,LCD_e ; and then Clear E movlw 0x02 ; 4-bit mode instruction movwf PORTB ; Send to LCD via RB3..RB0 bsf PORTB,LCD_e ; Set E high, call wait_16ms ; wait a while, bcf PORTB,LCD_e ; and then Clear E movlw 0x28 ; 1/16 duty cycle, 5x8 matrix call cmnd2LCD ; Send command in w to LCD movlw 0x08 ; Display off, cursor and blink off call cmnd2LCD ; Send command to LCD movlw 0x01 ; Clear and reset cursor call cmnd2LCD ; Send command in w to LCD movlw 0x06 ; Set cursor to move right, no shift call cmnd2LCD ; Send command in w to LCD movlw 0x0C ; Display on, cursor and blink off call cmnd2LCD ; Send command in w to LCD return ; ; ; ****************************************************************************P ; * P ; * Purpose: Display version and other info on LCD for 2 seconds P ; * upon power-up P ; * P ; * Input: MCODE_REV_0 through MCODE_REV_4 set up P ; * P ; * Output: LCD displays debug info P ; * P ; ****************************************************************************P ; display_version #IF LCDCHAR == 8 movlw 0x80 ; Point LCD at digit 1 call cmnd2LCD ; movlw 'P' ; digit 1 call data2LCD ; movlw 'I' ; digit 2 call data2LCD ; movlw 'C' ; digit 3 call data2LCD ; movlw 'E' ; digit 4 call data2LCD ; movlw 'L' ; digit 5 call data2LCD ; movlw 'g' ; digit 6 call data2LCD ; movlw 'e' ; digit 7 call data2LCD ; movlw 'n' ; digit 8 call data2LCD ; call wait_a_sec ; Wait one second ; movlw 0x80 ; Point LCD at digit 1 call cmnd2LCD ; movlw MCODE_REV_0 ; Get mcode rev byte call data2LCD ; and display it (digit 1) movlw MCODE_REV_1 ; Get mcode rev byte call data2LCD ; and display it (digit 2) movlw MCODE_REV_2 ; Get mcode rev byte call data2LCD ; and display it (digit 3) movlw MCODE_REV_3 ; Get mcode rev byte call data2LCD ; and display it (digit 4) movlw MCODE_REV_4 ; Get mcode rev byte call data2LCD ; and display it (digit 5) movlw MCODE_REV_5 ; Get mcode rev byte call data2LCD ; and display it (digit 6) movlw MCODE_REV_6 ; Get mcode rev byte call data2LCD ; and display it (digit 7) movlw MCODE_REV_7 ; Get mcode rev byte call data2LCD ; and display it (digit 8) #ENDIF ; End of 8-character LCD code #IF LCDCHAR == 16 ; Start 16-character LCD code movlw 0x80 ; Point LCD at digit 1 call cmnd2LCD ; movlw 'P' ; digit 1 call data2LCD ; movlw 'I' ; digit 2 call data2LCD ; movlw 'C' ; digit 3 call data2LCD ; movlw 'E' ; digit 4 call data2LCD ; movlw 'L' ; digit 5 call data2LCD ; movlw 'g' ; digit 6 call data2LCD ; movlw 'e' ; digit 7 call data2LCD ; movlw 'n' ; digit 8 call data2LCD ; #IFDEF LCD2LINES ; 16 x 2 LCD movlw 0x88 ; Point LCD at digit 9 #ELSE ; 16 x 1 LCD movlw 0xC0 ; Point LCD at digit 9 #ENDIF movwf LCD_char ; call cmnd2LCD ; Send command to LCD movlw MCODE_REV_0 ; Get mcode rev byte call data2LCD ; and display it (digit 9) movlw MCODE_REV_1 ; Get mcode rev byte call data2LCD ; and display it (digit 10) movlw MCODE_REV_2 ; Get mcode rev byte call data2LCD ; and display it (digit 11) movlw MCODE_REV_3 ; Get mcode rev byte call data2LCD ; and display it (digit 12) movlw MCODE_REV_4 ; Get mcode rev byte call data2LCD ; and display it (digit 13) movlw MCODE_REV_5 ; Get mcode rev byte call data2LCD ; and display it (digit 14) movlw MCODE_REV_6 ; Get mcode rev byte call data2LCD ; and display it (digit 15) movlw MCODE_REV_7 ; Get mcode rev byte call data2LCD ; and display it (digit 16) #IFDEF LCD2LINES movlw 0xC0 ; Point LCD at digit 1 of second line call cmnd2LCD ; movlw 'P' ; digit 1 call data2LCD ; movlw 'I' ; digit 2 call data2LCD ; movlw 'C' ; digit 3 call data2LCD ; movlw 'G' ; digit 4 call data2LCD ; movlw 'E' ; digit 5 call data2LCD ; movlw 'N' ; digit 6 call data2LCD ; movlw ' ' ; digit 7 call data2LCD ; movlw '7' ; digit 8 call data2LCD ; movlw '.' ; digit 9 call data2LCD ; movlw '2' ; digit 10 call data2LCD ; movlw ' ' ; digit 11 call data2LCD ; movlw ' ' ; digit 12 call data2LCD ; movlw ' ' ; digit 13 call data2LCD ; movlw ' ' ; digit 14 call data2LCD ; movlw ' ' ; digit 15 call data2LCD ; movlw ' ' ; digit 16 call data2LCD ; #ENDIF #ENDIF ; End of 16-character LCD call wait_a_sec ; Wait one second ;-------------------ORIGINAL CODE -------------------------------------- ;#IFDEF LCD2LINES ; movlw 0xC0 ; Point LCD at digit 1 of second line ; call cmnd2LCD ; ; movlw ' ' ; digit 1 ; call data2LCD ; ; movlw ' ' ; digit 2 ; call data2LCD ; ; movlw ' ' ; digit 3 ; call data2LCD ; ; movlw ' ' ; digit 4 ; call data2LCD ; ; movlw ' ' ; digit 5 ; call data2LCD ; ; movlw ' ' ; digit 6 ; call data2LCD ; ; movlw ' ' ; digit 7 ; call data2LCD ; ; movlw ' ' ; digit 8 ; call data2LCD ; ; movlw ' ' ; digit 9 ; call data2LCD ; ; movlw ' ' ; digit 10 ; call data2LCD ; ; movlw ' ' ; digit 11 ; call data2LCD ; ; movlw ' ' ; digit 12 ; call data2LCD ; ; movlw ' ' ; digit 13 ; call data2LCD ; ; movlw ' ' ; digit 14 ; call data2LCD ; ; movlw ' ' ; digit 15 ; call data2LCD ; ; movlw ' ' ; digit 16 ; call data2LCD ; ;#ENDIF ;----------------------------------------------------------------------- ;-----------------------MODIFIED CODE ---------------------------------- #IFDEF LCD2LINES movlw 0xC0 ; Point LCD at digit 1 of second line call cmnd2LCD ; movlw 'S' ; digit 1 call data2LCD ; movlw 'T' ; digit 2 call data2LCD ; movlw 'E' ; digit 3 call data2LCD ; movlw 'P' ; digit 4 call data2LCD ; movlw ':' ; digit 5 call data2LCD ; movlw '1' ; digit 6 call data2LCD ; movlw 'H' ; digit 7 call data2LCD ; movlw 'z' ; digit 8 call data2LCD ; movlw ' ' ; digit 9 call data2LCD ; movlw ' ' ; digit 10 call data2LCD ; movlw ' ' ; digit 11 call data2LCD ; movlw ' ' ; digit 12 call data2LCD ; movlw ' ' ; digit 13 call data2LCD ; movlw ' ' ; digit 14 call data2LCD ; movlw ' ' ; digit 15 call data2LCD ; movlw ' ' ; digit 16 call data2LCD ; #ENDIF ; return ; ; ***************************************************************************** ; * * ; * Purpose: This routine adds the 32 bit value of fstep to the 32 bit * ; * value in freq. When incrementing, the fstep value is a * ; * positive integer. When decrementing, fstep is the complement * ; * of the value being subtracted. * ; * * ; * Input: The 32 bit values in fstep and freq * ; * * ; * Output: The sum of fstep and freq is stored in freq. When incrementing * ; * this value may exceed the maximum. When decrementing, it may * ; * go negative. * ; * * ; ***************************************************************************** add_step movf fstep_0,w ; Get low byte of the increment addwf freq_0,f ; Add it to the low byte of freq btfss STATUS,C ; Any carry? goto add1 ; No, add next byte incfsz freq_1,f ; Ripple carry up to the next byte goto add1 ; No new carry, add next byte incfsz freq_2,f ; Ripple carry up to the next byte goto add1 ; No new carry, add next byte incf freq_3,f ; Ripple carry up to the highest byte add1 movf fstep_1,w ; Get the next increment byte addwf freq_1,f ; Add it to the next higher byte btfss STATUS,C ; Any carry? goto add2 ; No, add next byte incfsz freq_2,f ; Ripple carry up to the next byte goto add2 ; No new carry, add next byte incf freq_3,f ; Ripple carry up to the highest byte add2 movf fstep_2,w ; Get the next to most significant increment addwf freq_2,f ; Add it to the freq byte btfss STATUS,C ; Any carry? goto add3 ; No, add last byte incf freq_3,f ; Ripple carry up to the highest byte add3 movf fstep_3,w ; Get the most significant increment byte addwf freq_3,f ; Add it to the most significant freq return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: Check if freq exceeds the upper limit. * ; * * ; * Input: The 32 bit values in freq * ; * * ; * Output: If freq is below the limit, it is unchanged. Otherwise, it is * ; * set to equal the upper limit. * ; * * ; ***************************************************************************** ; check_add ; ; Check the most significant byte. ; movlw 0xFF-limit_3 ; Get (FF - limit of high byte) addwf freq_3,w ; Add it to the current high byte btfsc STATUS,C ; Was high byte too large? goto set_max ; Yes, apply limit movlw limit_3 ; Get high limit value subwf freq_3,w ; Subtract the limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the second most significant byte. ; movlw 0xFF-limit_2 ; Get (FF - limit of next byte) addwf freq_2,w ; Add it to the current byte btfsc STATUS,C ; Is the current value too high? goto set_max ; Yes, apply the limit movlw limit_2 ; Second limit byte subwf freq_2,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the third most significant byte. ; movlw 0xFF-limit_1 ; Get (FF - limit of next byte) addwf freq_1,w ; Add it to the current byte btfsc STATUS,C ; Is the current value too high? goto set_max ; Yes, apply the limit movlw limit_1 ; Third limit byte subwf freq_1,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. ; ; Check the least significant byte. ; movlw limit_0 ; Fourth limit byte subwf freq_0,w ; Subtract limit value btfss STATUS,C ; Are we at the limit for the byte? goto exit1 ; No, below. Checks are done. set_max movlw limit_0 ; Get least significant limit movwf freq_0 ; Set it in freq movlw limit_1 ; Get the next byte limit movwf freq_1 ; Set it in freq_1 movlw limit_2 ; Get the next byte limit movwf freq_2 ; Set it in freq_2 movlw limit_3 ; Get the most significant limit movwf freq_3 ; Set it in freq_3 exit1 return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: Subtract the increment step from freq, checking that it does * ; * not go below zero. * ; * * ; * Input: The values in fstep and freq. * ; * * ; * Output: The updated value in freq. * ; * * ; ***************************************************************************** ; sub_step comf fstep_0,f ; Subtraction of fstep from comf fstep_1,f ; freq is done by adding the comf fstep_2,f ; twos compliment of fstep to comf fstep_3,f ; freq. incfsz fstep_0,f ; Increment last byte goto comp_done ; Non-zero, continue incfsz fstep_1,f ; Increment next byte goto comp_done ; Non-zero, continue incfsz fstep_2,f ; Increment next byte goto comp_done ; Non-zero, continue incf fstep_3,f ; Increment the high byte comp_done call add_step ; Add the compliment to do the subtraction ; ; If the frequency has gone negative, clear it to zero. ; btfss freq_3,7 ; Is high order frequency byte "negative"? goto exit2 ; No, keep going set_min clrf freq_0 ; Yes, set the frequency to zero clrf freq_1 ; clrf freq_2 ; clrf freq_3 ; exit2 return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: This routine does the following: * ; * 1. Records how long it took for the knob to move a notch * ; * in ren_timer. * ; * 2. Clears the watchdog timer. * ; * 3. Reads the encoder bits until a change is detected, then * ; * determines the direction the knob was moved. * ; * * ; * Input: Knob input read from port A * ; * ren_old -> the last encoder bits read * ; * last_dir -> the last direction moved * ; * * ; * Output: ren_timer -> an indication the speed of the knob. * ; * ren_new -> the current encoder bits * ; * last_dir -> the last direction (0 = down, 2 = up) * ; * * ; ***************************************************************************** ; poll_encoder ;********************************************************************************** ; clrf ren_timer_1 ; Put starting values in ren_timer cells > ; movlw 0x40 ; Start with the high bit set ORIGINAL > Used in variable rate tuning ; movwf ren_timer_0 ; in ren_timer_0 > ;********************************************************************************** read_encoder btfss PB_FLAGS,PB1Calibrate_Active ; Is calibration now active? call PB_look ; No, look at PB's for possible 1 MHz jump ;********************************************************************************** ; btfsc ren_timer_1,7 ; Has the bit floated to top of ren_timer_1? ; goto no_inc ; Yes, don't move it any further Used in variable rate tuning ; movlw 0x08 ; No, keep going ; addwf ren_timer_0,f ; Add constant to ren_timer_0 ; btfsc STATUS,C ; Did the add force a carry? ; incf ren_timer_1,f ; Yes, then add one to ren_timer_1 ;*********************************************************************************** no_inc ; movf PORTA,w ; Get the current encoder value movwf ren_read ; Save it ;!!!--- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!! call wait_1ms ; debounce time movf PORTA,w ; read the port again xorwf ren_read,w ; Compare with previous value btfss STATUS,Z ; Are they equal? goto poll_encoder ; Poll again if not ;!!! ------------------------------------------------------------- !!! movlw 0x03 ; Get encoder mask (to isolate RA0 and RA1) andwf ren_read,w ; Isolated encoder bits into W movwf ren_new ; Save new value xorwf ren_old,w ; Has it changed? btfsc STATUS,Z ; Check zero-flag (zero if no change) goto read_encoder ; No change, keep looking until it changes !read encoder originally ; ; Zero-flag is not set, so continue on ; It changed. Now determine which direction the encoder turned. ;============================================================================= ; Encoder bits are on RA0 and RA1 - the two low order bits of ren_new ; A and B are "gray code" - 90 degrees out of phase (quadrature) ; ___ ___ ; | | | | ; A ____| |___| |___ ; ___ ___ ; | | | | ; B ___| |___| |___ ; ^ ^ ^ ^ ^ ^ ^ ^ ; a b c d a b c d ; ; A B ; At point a: 0 0 ; At point b: 1 0 ; At point c: 1 1 ; At point d: 0 1 ; ; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is: ; 00, 10, 11, 01, 00, 10, 11, 01, etc. ; ; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is: ; 01, 11, 10, 00, 01, 11, 10, 00, etc. ; ; To determine if the sequence is UP or DOWN: ; 1) Take the "Right-Bit" of any pair. ; 2) XOR it with the "Left-Bit" of the next pair in the sequence. ; 3) If the result is 1 it is UP ; If the result is 0 it is DOWN ; ; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning ;============================================================================= ;################################################################################################# ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ORIGINAL CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; bcf STATUS,C ; Clear the carry bit to prepare for rotate ; rlf ren_old,f ; Rotate old bits left to align "Right-Bit" ; movf ren_new,w ; Set up new bits in W ; xorwf ren_old,f ; XOR old (left shifted) with new bits ; movf ren_old,w ; Put XOR results into W also ; andlw 0x02 ; Mask to look at only "Left-Bit" of pair ; movwf next_dir ; Save result (in W) as direction (bit=UP) ; xorwf last_dir,w ; See if direction is same as before ;; ;; Prevent encoder slip from giving a false change in direction. ;; ; btfsc STATUS,Z ; Zero flag set? (i.e, is direction same?) ; goto pe_continue ; Yes, same direction so no slip; keep going ; movf next_dir,w ; No Zero-flag, so direction changed ; movwf last_dir ; Update the direction indicator ; movf ren_new,w ; Save the current encoder bits (now in W) ; movwf ren_old ; for next time ; goto read_encoder ; Try again ;pe_continue ; clrf last_dir ; Clear last_dir (default is DN) ; btfss ren_old,1 ; Are we going UP? ; goto exit3 ; No, exit3 ;up2 ; movlw 0x02 ; Get UP value ; movwf last_dir ; and set in last_dir ;exit3 ; movf ren_new,w ; Get the current encoder bits ; movwf ren_old ; Save them in ren_old for the next time ; return ; Return to the caller ;##################################################################################################### bcf STATUS,C ; Clear the carry bit to prepare for rotate rlf ren_old,f ; Rotate old bits left to align "Right-Bit" movf ren_new,w ; Set up new bits in W xorwf ren_old,f ; XOR old (left shifted) with new bits movf ren_old,w ; Put XOR results into W also andlw 0x02 ; Mask to look at only "Left-Bit" of pair movwf next_dir ; Save result (in W) as direction (bit=UP) xorwf last_dir,w ; See if direction is same as before pe_continue clrf last_dir ; Clear last_dir (default is DN) btfsc ren_old,1 ; Are we going UP? goto up2 ; No, exit3 ;!!! --- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!! movf ren_new,w ; Get the current encoder bits movwf ren_old ; Save them in ren_old for the next time decf enc_counter,f ; decrement the inter-detent counter btfsc enc_counter,0 ; Check if bit 0 is cleared goto poll_encoder ; If not, then poll some more btfsc enc_counter,1 ; Else, check if bit 1 is cleared goto poll_encoder ; If not, then poll some more ; Else, assume the encoder is on a detent ;!!! -------------------------------------------------------------- !!! goto exit3 up2 movlw 0x02 ; Get UP value movwf last_dir ; and set in last_dir ;!!! --- DETENT_ENCODER --- ADDED CODE FOR MECHANICAL ENCODER ----- !!! movf ren_new,w ; Get the current encoder bits movwf ren_old ; Save them in ren_old for the next time incf enc_counter,f ; Increment the encoder counter btfsc enc_counter,0 ; Return only on modulo 4 counts goto poll_encoder ; btfsc enc_counter,1 goto poll_encoder ; Loop again if not modulo 4 ;!!! -------------------------------------------------------------- !!! exit3 movf ren_new,w ; Get the current encoder bits movwf ren_old ; Save them in ren_old for the next time return ; Return to the caller ;-------------------------------------------------------------------------------------------------------- ;############################################################################################ ;-------------------ORIGINAL CODE------------------------------------------------------------ ; ; **************************************************************************** ; * ; * Purpose: This routine is entered to see if the pushbuttons PB1 and PB2 ; * being pressed. ; * If both are being pressed, determine direction (depending on ; * which was first) and change the frequency UP/DN by 1 MHz. ; * If only one is being pressed, remember it as being "first". ; * ; * Input: None ; * Output: PB_flag set up ; **************************************************************************** ; ;PB_look ; ; btfss PORTA,pb_1 ; Is PB_1 being pressed? ; goto PB_yes1 ; Yes, PB_yes1 ;PB_no1 ; 0,X ; btfsc PORTA,pb_2 ; Not1, but is PB_2 being pressed? ; goto PB_neither ; No, Neither ; goto PB_2butnot1 ; Yes, PB_2butnot1 ;PB_yes1 ; 1,X ; btfsc PORTA,pb_2 ; Is PB_2 being pressed? ; goto PB_1butnot2 ; No, PB_1butnot2 ; ; fall through to PB_both ; Yes, PB_both ;PB_both ; 1,2 ; ; both set, so add code to find out which was first. ; btfsc PB_flags,PB1first ; Was PB1 pressed first? ; goto PB_up ; Yes, going down by 1 MHz ; goto PB_dn ; NO, PB2 was first, so going UP by 1 MHz ;PB_2butnot1 ; 0,2 ; bsf PB_flags,PB1first ; Set PB1 first ; goto PB_exit ; Exit ;PB_1butnot2 ; 1,0 ; bcf PB_flags,PB1first ; Clear PB1 first ; goto PB_exit ; Exit ;PB_neither ; 0,0 ; clrf PB_flags ; Neither, so clear flags ; goto PB_exit ; Exit ;; ;PB_up ; ; ; PB_2 was pressed first, then PB_1 ; ; Wait for PB_1 to be released ; ; (If wait is longer than 2 seconds, call update_EEPROM and exit) ; movlw 0x10 ; Set up ; movwf PB_wait_count ; loop count ;PB_up_release_wait ; ; btfsc PORTA,pb_1 ; Is PB_1 still behing held? ; goto PB_up_released ; No, it's released, so move up ; call wait_128ms ; Wait a bit ; decfsz PB_wait_count,f ; Have we waited long enough? ; goto PB_up_release_wait ; No, continue waiting ; ; Button was held longer than 2 seconds, so ; call update_EEPROM ; Update EEPROM with current frequency ; goto PB_exit ; and exit ;; ;PB_up_released ; ; clrf fstep_3 ; ; movlw 0x0F ; Setup for step of 1 M ; movwf fstep_2 ; ; movlw 0x42 ; ; movwf fstep_1 ; ; movlw 0x40 ; ; movwf fstep_0 ; ; call add_step ; Add a step and update ; call check_add ; See if band limits reached. If so, adjust ; ; and update ; goto PB_update ; ;; ;PB_dn ; ; ; PB_1 was pressed first, then PB_2 ; ; Wait for PB_2 to be released ; ; (If wait is longer than 2 seconds, call update_EEPROM and exit) ; movlw 0x10 ; Set up ; movwf PB_wait_count ; loop count ;PB_dn_release_wait ; ; btfsc PORTA,pb_2 ; Is PB_2 still being held? ; goto PB_dn_released ; No, it's released, so move up ; call wait_128ms ; Wait a bit ; decfsz PB_wait_count,f ; Have we waited long enough? ; goto PB_dn_release_wait ; No, continue waiting ; ; Button was held longer than 2 seconds, so ; call update_EEPROM ; Update EEPROM with current frequency ; goto PB_exit ; and exit ;; ;PB_dn_released ; ; clrf fstep_3 ; ; movlw 0x0F ; Setup for step of 1 M ; movwf fstep_2 ; ; movlw 0x42 ; ; movwf fstep_1 ; ; movlw 0x40 ; ; movwf fstep_0 ; ; call sub_step ; Add a step and update ;; ;PB_update ; ; call bin2BCD ; Convert the frequency to BCD ; call show_freq ; Display the frequency on the LCD ; call calc_dds_word ; Find the control word for the DDS chip ; call send_dds_word ; Send the control word to the DDS chip ;; ;PB_exit ; ; return ; ;############################################################################################ ;------------------ MODIFIED CODE------------------------------------------------------------ ; **************************************************************************** ; * ; * Purpose: This routine is entered to see if the pushbuttons PB2 and PB3 ; * are being pressed. ; * ; * ; * ; * ; * Input: None ; * Output: PB_flag set up ; **************************************************************************** ; PB_look ; btfsc PORTA,pb_2 ; is pb_2 pressed goto PB_UP call wait_8ms ; de-bounce btfsc PORTA,pb_2 ; check pb_2 again goto PB_UP ; no - go check if pb_3 is pressed btfss PB_FLAGS,KHZ ; is pb_flags,khz set goto pb_mhz ; no - go check pb_flags,mhz bcf PB_FLAGS,KHZ ; yes - clear pb_flags,khz nop bsf PB_FLAGS,MHZ ; and set pb_flags,mhz and exit movlw 0xC6 ; Point LCD at digit 7 of second line call cmnd2LCD ; movlw 'M' ; digit 7 call data2LCD ; movlw 'H' ; digit 8 call data2LCD ; movlw 'z' ; digit 9 call data2LCD ; goto PB_exit pb_mhz btfsc PB_FLAGS,MHZ ; is pb_flags,mhz set goto pb_clr bsf PB_FLAGS,KHZ ; no - set pb_flags,khz and exit movlw 0xC6 ; Point LCD at digit 7 of second line call cmnd2LCD ; movlw 'k' ; digit 7 call data2LCD ; movlw 'H' ; digit 8 call data2LCD ; movlw 'z' ; digit 9 call data2LCD ; goto PB_exit pb_clr bcf PB_FLAGS,MHZ ; yes - clear pb_flags,mhz and exit movlw 0xC6 ; Point LCD at digit 7 of second line call cmnd2LCD ; movlw 'H' ; digit 7 call data2LCD ; movlw 'z' ; digit 8 call data2LCD ; movlw ' ' ; digit 9 call data2LCD ; goto PB_exit ; PB_UP btfsc PORTA,pb_3 ; is pb_3 pressed goto PB_exit ;no - goto exit ;; PB_3 pressed ;; (If wait is longer than 2 seconds, call update_EEPROM and exit) movlw 0x10 ; Set up movwf PB_wait_count ; loop count PB_up_release_wait ; btfsc PORTA,pb_3 ; Is PB_3 still behing held? goto PB_exit ; No, it's released, so exit call wait_128ms ; Wait a bit decfsz PB_wait_count,f ; Have we waited long enough? goto PB_up_release_wait ; No, continue waiting ; Button was held longer than 2 seconds, so call update_EEPROM ; Update EEPROM with current frequency ; display message on LCD - 'SAVED' for 1sec and exit movlw 0xCB ; Point LCD at digit 12 of second line call cmnd2LCD ; movlw 'S' ; digit 12 call data2LCD ; movlw 'A' ; digit 13 call data2LCD ; movlw 'V' ; digit 14 call data2LCD ; movlw 'E' ; digit 15 call data2LCD ; movlw 'D' ; digit 16 call data2LCD ; call wait_a_sec ; Wait one second movlw 0xCB ; Point LCD at digit 12 of second line call cmnd2LCD ; movlw ' ' ; digit 12 call data2LCD ; movlw ' ' ; digit 13 call data2LCD ; movlw ' ' ; digit 14 call data2LCD ; movlw ' ' ; digit 15 call data2LCD ; movlw ' ' ; digit 16 call data2LCD ; PB_exit btfss PORTA,pb_2 goto PB_exit ; return ; ;############################################################################################ ; ; ***************************************************************************** ; * * ; * Purpose: This routine is entered at start up if the calibrate * ; * push button is (PIC-EL PB_1) is pressed at power-on time. * ; * * ; * If a 8-character LCD is used,"10000000" is displayed on the LCD * ; * If a 16-character LCD is used," 10,000.000 CAL " is displayed * ; * on the LCD. * ; * * ; * The DDS chip is programmed to produce 10 MHz, based on the * ; * osc value stored in the EEPROM. As long as the button is * ; * pressed, the osc value is slowly altered to allow the output * ; * to be trimmed to exactly 10 MHz. Once the encoder is turned * ; * after the button is released, the new osc value is stored in * ; * the EEPROM and normal operation begins. * ; * * ; * Input: The original osc constant in EEPROM * ; * * ; * Output: The corrected osc constant in EEPROM * ; * * ; ***************************************************************************** ; calibrate movlw 0x80 ; Set frequency to 10MHz by setting freq movwf freq_0 ; to the binary equivalent of 10 MHz movlw 0x96 ; movwf freq_1 ; movlw 0x98 ; movwf freq_2 ; movlw 0x00 ; movwf freq_3 ; ; ; Read the starting reference oscillator value from EEPROM. ; bsf STATUS,RP0 ; Switch to bank 1 clrf EEADR ; Reset the EEPROM read address call read_EEPROM ; Read first byte from EEPROM (all in bank 1) movf EEDATA,w ; Get the first osc byte bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_0 ; Save osc frequency bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read second byte from EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_1 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read third byte from EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_2 ; Save it bsf STATUS,RP0 ; Switch to bank 1 call read_EEPROM ; Read fourth byte from EEPROM (all in bank 1) movf EEDATA,w ; bcf STATUS,RP0 ; Back to bank 0 for store movwf osc_3 ; Save it call bin2BCD ; Calculate BCD version of 10 MHz call show_freq ; Display the frequency on the LCD #IF LCDCHAR == 16 movlw 0xC4 ; Point LCD at position 13 movwf LCD_char ; call cmnd2LCD ; Send command to LCD movlw 'C' ; Send a C (position 13) movwf LCD_char ; call data2LCD ; movlw 'A' ; Send an A (position 14) movwf LCD_char ; call data2LCD ; movlw 'L' ; Send an L (position 15) movwf LCD_char ; call data2LCD ; ; ; (position 16 is blank) #ENDIF cal_loop call calc_dds_word ; Calculate DDS value based on current osc call send_dds_word ; Update the DDS chip call poll_encoder ; Wait until the encoder has moved. clrf fstep_3 ; Clear the three most significant clrf fstep_2 ; bytes of fstep clrf fstep_1 ; movlw 0x10 ; Assume that we are adjusting slowly movwf fstep_0 ; Use small increment btfsc ren_read,2 ; Was the encoder changing slowly? goto update_osc ; Yes, then continue with small increment movlw 0x80 ; No, then use the large increment movwf fstep_0 ; update_osc nop ; Wait a cycle btfsc last_dir,1 ; Are we moving down? goto faster ; No, increase the osc value ; ; slower ; comf fstep_0,f ; Subtraction of fstep is done by comf fstep_1,f ; adding the twos compliment of fsetp comf fstep_2,f ; to osc comf fstep_3,f ; incfsz fstep_0,f ; Increment last byte goto faster ; Non-zero, continue incfsz fstep_1,f ; Increment next byte goto faster ; Non-zero, continue incfsz fstep_2,f ; Increment next byte goto faster ; Non-zero, continue incf fstep_3,f ; Increment the high byte faster movf fstep_0,w ; Get the low byte increment addwf osc_0,f ; Add it to the low osc byte btfss STATUS,C ; Was there a carry? goto add4 ; No, add the next bytes incfsz osc_1,f ; Ripple carry up to the next byte goto add4 ; No new carry, add the next bytes incfsz osc_2,f ; Ripple carry up to the next byte goto add4 ; No new carry, add the next bytes incf osc_3,f ; Ripple carry up to the highest byte add4 movf fstep_1,w ; Get the second byte increment addwf osc_1,f ; Add it to the second osc byte btfss STATUS,C ; Was there a carry? goto add5 ; No, add the third bytes incfsz osc_2,f ; Ripple carry up to the next byte goto add5 ; No new carry, add the third bytes incf osc_3,f ; Ripple carry up to the highest byte add5 movf fstep_2,w ; Get the third byte increment addwf osc_2,f ; Add it to the third osc byte btfss STATUS,C ; Was there a carry? goto add6 ; No, add the fourth bytes incf osc_3,f ; Ripple carry up to the highest byte add6 movf fstep_3,w ; Get the fourth byte increment addwf osc_3,f ; Add it to the fourth byte btfss PORTA,pb_1 ; Tuning-increment pushbutton pressed? goto cal_loop ; Yes, stay in calibrate mode ; ; Write final value to EPROM ; movf osc_0,w ; Get the first osc byte to record (bank 0) bsf STATUS,RP0 ; Switch to bank 1 clrf EEADR ; osc bytes start at EEPROM address 0 movwf EEDATA ; Put byte in EEPROM write location call write_EEPROM ; (all in bank 1) bcf STATUS,RP0 ; Back to bank 0 movf osc_1,w ; Get the second byte to record (bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Put byte in EEPROM write location call write_EEPROM ; (all in bank 1) bcf STATUS,RP0 ; Back to bank 0 movf osc_2,w ; Get the third byte to record (bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Put byte in EEPROM write location call write_EEPROM ; (all in bank 1) bcf STATUS,RP0 ; Back to bank 0 movf osc_3,w ; Get the fourth byte to record (bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Put byte in EEPROM write location call write_EEPROM ; (all in bank 1) bcf STATUS,RP0 ; Back to bank 0 return ; Return to the caller ; ; **************************************************************************** ; * ; * Purpose: This routine will save the current frequency in EEPROM. This ; * frequency will then be used as the initial frequency upon start ; * up. The routine is entered by pressing and holding both PB_1 ; * and PB_2 for more than 2 seconds. . ; * ; * Input: The original osc constant in EEPROM ; * ; * Output: The corrected osc constant in EEPROM ; * ; **************************************************************************** ; update_EEPROM ; bsf STATUS,RP0 ; Switch to bank 1 movlw EEstartup_adr ; Get startup address location movwf EEADR ; and set up for start of EEPROM writes bcf STATUS,RP0 ; Back to bank 0 movf freq_0,w ; Get the first freq byte to write (Bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; First freq byte to EEPROM Write register call write_EEPROM ; Write it (Bank 1) bcf STATUS,RP0 ; Back to bank 0 movf freq_1,w ; Get the second freq byte to write (Bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Second freq byte to EEPROM Write register call write_EEPROM ; Write it (Bank 1) bcf STATUS,RP0 ; Back to bank 0 movf freq_2,w ; Get the third freq byte to write (Bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Third freq byte to EEPROM Write register call write_EEPROM ; Write it (Bank 1) bcf STATUS,RP0 ; Back to bank 0 movf freq_3,w ; Get the fourth freq byte to write (Bank 0) bsf STATUS,RP0 ; Switch to bank 1 movwf EEDATA ; Fourth freq byte to EEPROM Write register call write_EEPROM ; Write it (Bank 1) bcf STATUS,RP0 ; Back to bank 0 return ; ; ; ***************************************************************************** ; * * ; * Purpose: Multiply the 32 bit number for oscillator frequency times the * ; * 32 bit number for the displayed frequency. * ; * * ; * * ; * Input: The reference oscillator value in osc_3 ... osc_0 and the * ; * current frequency stored in freq_3 ... freq_0. The reference * ; * oscillator value is treated as a fixed point real, with a 24 * ; * bit mantissa. * ; * * ; * Output: The result is stored in AD9851_3 ... AD9851_0. * ; * * ; ***************************************************************************** ; calc_dds_word #ifdef MULTBY2 movf freq_0,w ; Move movwf freq_0A ; freq movf freq_1,w ; bytes movwf freq_1A ; to movf freq_2,w ; temporary movwf freq_2A ; locations movf freq_3,w ; movwf freq_3A ; ; Multiply freq by 2 by rotating left once. Store in temp locations ; (freq_3A is most significant byte, freq_0A is least significant byte) bcf STATUS,C ; Clear carry bit rlf freq_0A,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlf freq_1A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_2A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_3A,f ; Rotate left, Carry->LS bit, MS bit->Carry #endif #ifdef MULTBY4 movf freq_0,w ; Move movwf freq_0A ; freq movf freq_1,w ; bytes movwf freq_1A ; to movf freq_2,w ; temporary movwf freq_2A ; locations movf freq_3,w ; movwf freq_3A ; ; Multiply freq by 4 by rotating left twice. Store in temp locations ; (freq_3A is most significant byte, freq_0A is least significant byte) bcf STATUS,C ; Clear carry bit rlf freq_0A,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlf freq_1A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_2A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_3A,f ; Rotate left, Carry->LS bit, MS bit->Carry bcf STATUS,C ; Clear carry bit rlf freq_0A,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlf freq_1A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_2A,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_3A,f ; Rotate left, Carry->LS bit, MS bit->Carry #endif clrf AD9851_0 ; Clear the AD9850/AD9851 control word bytes clrf AD9851_1 ; clrf AD9851_2 ; clrf AD9851_3 ; clrf AD9851_4 ; movlw 0x20 ; Set count to 32 (4 osc bytes of 8 bits) movwf mult_count ; Keep running count movf osc_0,w ; Move the four osc bytes movwf osc_temp_0 ; to temporary storage for this multiply movf osc_1,w ; (Don't disturb original osc bytes) movwf osc_temp_1 ; movf osc_2,w ; movwf osc_temp_2 ; movf osc_3,w ; movwf osc_temp_3 ; mult_loop bcf STATUS,C ; Start with Carry clear btfss osc_temp_0,0 ; Is bit 0 (Least Significant bit) set? goto noAdd ; No, don't need to add freq term to total movf freq_0,w ; Get the "normal" freq_0 term #ifdef MULTBY2 movf freq_0A,w ; Get the "multiplied" freq_0 term #endif #ifdef MULTBY4 movf freq_0A,w ; Get the "multiplied" freq_0 term #endif addwf AD9851_1,f ; Add it in to total btfss STATUS,C ; Does this addition result in a carry? goto add7 ; No, continue with next freq term incfsz AD9851_2,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incfsz AD9851_3,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incf AD9851_4,f ; Yes, add one and continue add7 movf freq_1,w ; Get the "normal" freq_0 term #ifdef MULTBY2 movf freq_1A,w ; Get the "multiplied" freq_1 term #endif #ifdef MULTBY4 movf freq_1A,w ; Get the "multiplied" freq_1 term #endif addwf AD9851_2,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add8 ; No, continue with next freq term incfsz AD9851_3,f ; Yes, add one and check for another carry goto add8 ; No, continue with next freq term incf AD9851_4,f ; Yes, add one and continue add8 movf freq_2,w ; Get the "normal" freq_2 term #ifdef MULTBY2 movf freq_2A,w ; Get the "multiplied" freq_2 term #endif #ifdef MULTBY4 movf freq_2A,w ; Get the "multiplied" freq_2 term #endif addwf AD9851_3,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add9 ; No, continue with next freq term incf AD9851_4,f ; Yes, add one and continue add9 movf freq_3,w ; Get the "normal" freq_3 term #ifdef MULTBY2 movf freq_3A,w ; Get the "multiplied" freq_3 term #endif #ifdef MULTBY4 movf freq_3A,w ; Get the "multiplied" freq_3 term #endif addwf AD9851_4,f ; Add freq term to total in correct position noAdd rrf AD9851_4,f ; Shift next multiplier bit into position rrf AD9851_3,f ; Rotate bits to right from byte to byte rrf AD9851_2,f ; rrf AD9851_1,f ; rrf AD9851_0,f ; rrf osc_temp_3,f ; Shift next multiplicand bit into position rrf osc_temp_2,f ; Rotate bits to right from byte to byte rrf osc_temp_1,f ; rrf osc_temp_0,f ; decfsz mult_count,f ; One more bit has been done. Are we done? goto mult_loop ; No, go back to use this bit #ifdef AD9850 movlw 0x00 ; No clock multiplier (AD9850) #endif #ifdef AD9851 movlw 0x01 ; Turn on 6x clock multiplier (AD9851) #endif movwf AD9851_4 ; Last byte to be sent ; Mult answer is in bytes _3 .. _0 return ; Done. ; ; ***************************************************************************** ; * * ; * Purpose: This routine sends the AD9850/AD9851 control word to the DDS * ; * using a serial data transfer. * ; * * ; * Input: AD9851_4 ... AD9851_0 * ; * * ; * Output: The DDS chip register is updated. * ; * * ; ***************************************************************************** ; send_dds_word movlw AD9851_0 ; Point FSR at Least Significant Byte movwf FSR ; next_byte movf INDF,w ; movwf byte2send ; movlw 0x08 ; Set counter to 8 movwf bit_count ; next_bit rrf byte2send,f ; Test if next bit is 1 or 0 btfss STATUS,C ; Was it zero? goto send0 ; Yes, send zero bsf PORTB,DDS_dat ; No, send one bsf PORTB,DDS_clk ; Toggle write clock bcf PORTB,DDS_clk ; goto break ; send0 bcf PORTB,DDS_dat ; Send zero bsf PORTB,DDS_clk ; Toggle write clock bcf PORTB,DDS_clk ; break decfsz bit_count,f ; Has the whole byte been sent? goto next_bit ; No, keep going. incf FSR,f ; Start the next byte unless finished movlw AD9851_4+1 ; Next byte (past the end) subwf FSR,w ; btfss STATUS,C ; goto next_byte ; bsf PORTB,DDS_load ; Send load signal to the AD9850/AD9851 bcf PORTB,DDS_load ; return ; ; ; ***************************************************************************** ; * * ; * Purpose: This subroutine converts a 32 bit binary number to a 10 digit * ; * BCD number. The input value taken from freq(0 to 3) is * ; * preserved. The output is in BCD(0 to 4), each byte holds => * ; * (hi_digit,lo_digit), most significant digits are in BCD_4. * ; * This routine is a modified version of one described in * ; * MicroChip application note AN526. * ; * * ; * Input: The value in freq_0 ... freq_3 * ; * * ; * Output: The BCD number in BCD_0 ... BCD_4 * ; * * ; ***************************************************************************** ; bin2BCD movlw 0x20 ; Set loop counter movwf BCD_count ; to 32 clrf BCD_0 ; Clear output clrf BCD_1 ; " " clrf BCD_2 ; " " clrf BCD_3 ; " " clrf BCD_4 ; " " bin_loop bcf STATUS,C ; Clear carry bit in STATUS ; ; Rotate bits in freq bytes. Move from LS byte (freq_0) to next byte (freq_1). ; Likewise, move from freq_1 to freq_2 and from freq_2 to freq_3. ; rlf freq_0,f ; Rotate left, 0 -> LS bit, MS bit -> Carry rlf freq_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf freq_3,f ; Rotate left, Carry->LS bit, MS bit->Carry btfsc STATUS,C ; Is Carry clear? If so, skip next instruction bsf freq_0,0 ; Carry is set so wrap and set bit 0 in freq_0 ; ; Build BCD bytes. Move into LS bit of BCD bytes (LS of BCD_0) from MS bit of ; freq_3 via the Carry bit. ; rlf BCD_0,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_1,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_2,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_3,f ; Rotate left, Carry->LS bit, MS bit->Carry rlf BCD_4,f ; Rotate left, Carry->LS bit, MS bit->Carry decf BCD_count,f ; Decrement loop count btfss STATUS,Z ; Is loop count now zero? goto adjust ; No, go to adjust return ; Yes, EXIT ; ============================================================================ adjust ; Internal subroutine, called by bin2BCD main loop only ; ; As BCD bytes are being built, make sure the nibbles do not grow larger than 9. ; If a nibble gets larger than 9, increment to next higher nibble. ; (If the LS nibble of a byte overflows, increment the MS nibble of that byte.) ; (If the MS nibble of a byte overflows, increment the LS nibble of next byte.) ; movlw BCD_0 ; Get pointer to BCD_0 movwf FSR ; Put pointer in FSR for indirect addressing call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_1 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_2 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_3 call adj_BCD ; incf FSR,f ; Move indirect addressing pointer to BCD_4 call adj_BCD ; goto bin_loop ; Back to main loop of bin2BCD ; ============================================================================ adj_BCD ; Internal subroutine, called by adjust only movlw 3 ; Add 3 addwf INDF,w ; to LS digit movwf BCD_temp ; Save in temp btfsc BCD_temp,3 ; Is LS digit + 3 > 7 (Bit 3 set) movwf INDF ; Yes, save incremented value as LS digit movlw 0x30 ; Add 3 addwf INDF,w ; to MS digit movwf BCD_temp ; Save as temp btfsc BCD_temp,7 ; Is MS digit + 3 > 7 (Bit 7 set) movwf INDF ; Yes, save incremented value as MS digit return ; Return to adjust subroutine ; ; ***************************************************************************** ; * * ; * Purpose: Display the frequency setting on the LCD. * ; * If a 1x8 LCD display so display freq in Hz - e.g. 14025000 * ; * If a 1x16 LCD display so display freq kHz - e.g 14,025.000 kHz * ; * * ; * Input: The values in BCD_4 ... BCD_0 * ; * * ; * Output: The number displayed on the LCD * ; * * ; * NOTE: * ; * When using 8x1 LCD: * ; * Line 1 (left) addresses are 0x00 to 0x07 (0x80 to 0x87) * ; * When using a 16x1 LCD (actually 2 8-char lines, side by side): * ; * Line 1 (left) addresses are 0x00 to 0x07 (0x80 to 0x87) * ; * Line 1 (right) addresses are 0x40 to 0x47 (0xC0 to 0xC7) * ; * When using a 16x2 LCD: * ; * Line 1 addresses are 0x00 to 0x0F (0x80 to 0x8F) * ; * Line 2 addresses are 0x40 to 0x4F (0xC0 to 0xCF) * ; * * ; ***************************************************************************** ; show_freq movlw 0x80 ; Point the LCD to first LCD digit location call cmnd2LCD ; Send starting digit location to LCD #IF LCDCHAR == 16 movlw ' ' ; Send a space call data2LCD ; to position 1 of LCD #ENDIF ; ; Running 4-bit mode, so need to send Most Significant Nibble first. ; ; Extract and send "XXXX" from byte containing "XXXXYYYY" ; - Swap halves to get YYYYXXXX ; - Mask with 0x0F to get 0000XXXX ; - Add ASCII bias (0030XXXX) ; swapf BCD_3,w ; Swap 10MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; ; Extract and send "YYYY" from byte containing "XXXXYYYY" ; - Mask with 0x0F to get 0000YYYY ; - Add offset for ASCII character set in LCD (0030YYYY) ; movf BCD_3,w ; Put 1MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; #IF LCDCHAR == 16 movlw ',' ; Get a comma call data2LCD ; Send byte in W to LCD #ENDIF ; swapf BCD_2,w ; Swap 100KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; movf BCD_2,w ; Put 10KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; swapf BCD_1,w ; Swap 1KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; #IF LCDCHAR == 16 movlw '.' ; Get a period call data2LCD ; Send byte in W to LCD #IFDEF LCD2LINES ; if 16x2 LCD movlw 0x88 ; Point to LCD digit number nine #ELSE ; if 16x1 LCD movlw 0xC0 ; Point to LCD digit number nine #ENDIF call cmnd2LCD ; Send command byte in W to LCD #ENDIF ; movf BCD_1,w ; Put 100 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send data byte in W to LCD ; swapf BCD_0,w ; Swap 10 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send data byte in W to LCD ; movf BCD_0,w ; Put 1 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; #IF LCDCHAR == 16 movlw ' ' ; Send a space call data2LCD ; to position 12 of LCD ; movlw 'k' ; Send a 'k' call data2LCD ; to position 13 of LCD ; movlw 'H' ; Send an "H" call data2LCD ; to position 14 of LCD ; movlw 'z' ; Send a 'z' call data2LCD ; to position 15 of LCD ; movlw ' ' ; Send a space call data2LCD ; to position 16 of LCD #ENDIF ; return ; ; ; ***************************************************************************** ; * * ; * Purpose: Check if LCD is done with the last operation. * ; * This subroutine polls the LCD busy flag to determine if * ; * previous operations are completed. * ; * * ; * Input: None * ; * * ; * On exit: PORTB set as: RB3 input * ; * all others outputs * ; ***************************************************************************** ; busy_check clrf PORTB ; Clear all outputs on PORTB bsf STATUS,RP0 ; Switch to bank 1 for Tristate operation movlw b'00001000' ; Set RB3 input, others outputs movwf TRISB ; via Tristate bcf STATUS,RP0 ; Switch back to bank 0 bcf PORTB,LCD_rs ; Set up LCD for Read Busy Flag (RS = 0) bsf PORTB,LCD_rw ; Set up LCD for Read (RW = 1) movlw 0xFF ; Set up constant 255 movwf timer1 ; for timer loop counter LCD_is_busy bsf PORTB,LCD_e ; Set E high movf PORTB,w ; Read PORTB into W movwf LCD_read ; Save W for later testing bcf PORTB,LCD_e ; Drop E again nop ; Wait a nop ; while bsf PORTB,LCD_e ; Pulse E high (dummy read of lower nibble), nop ; wait, bcf PORTB,LCD_e ; and drop E again decf timer1,f ; Decrement loop counter btfsc STATUS,Z ; Is loop counter down to zero? goto not_busy ; If yes, return regardless ;OLD btfsc LCD_read,7 ; Is Busy Flag (RB7) in save byte clear? btfsc LCD_read,LCD_busy ; Busy Flag (RB3) in save byte clear? goto LCD_is_busy ; If not, it is busy so jump back not_busy return ; ; ; ***************************************************************************** ; * Purpose: Send Command or Data byte to the LCD * ; * Entry point cmnd2LCD: Send a Command to the LCD * ; * Entry Point data2LCD: Send a Data byte to the LCD * ; * * ; * Input: W has the command or data byte to be sent to the LCD. * ; * * ; * Output: None * ; ***************************************************************************** ; cmnd2LCD ; ****** Entry point ****** movwf LCD_char ; Save byte to write to LCD clrf rs_value ; Remember to clear RS (clear rs_value) bcf PORTB,LCD_rs ; Set RS for Command to LCD goto write2LCD ; Go to common code data2LCD ; ****** Entry point ******** movwf LCD_char ; Save byte to write to LCD bsf rs_value,0 ; Remember to set RS (set bit 0 of rs_value) bsf PORTB,LCD_rs ; Set RS for Data to LCD write2LCD call busy_check ; Check to see if LCD is ready for new data clrf PORTB ; Clear all of Port B (inputs and outputs) bsf STATUS,RP0 ; Switch to bank 1 for Tristate operation movlw 0x00 ; Set up to enable PORTB data pins movwf TRISB ; All pins (RB7..RB0) are back to outputs bcf STATUS,RP0 ; Switch to bank 0 bcf PORTB,LCD_rw ; Set LCD back to Write mode (RW = 0) bcf PORTB,LCD_rs ; Guess RS should be clear btfsc rs_value,0 ; Should RS be clear? (is bit 0 == 0?) bsf PORTB,LCD_rs ; No, set RS ; ; Transfer Most Significant nibble (XXXX portion of XXXXYYYY) ; movlw 0xF0 ; Set up mask andwf PORTB,f ; Keep RB7..RB4 but clear old RB3..RB0 swapf LCD_char,w ; Put byte into W (reverse nibbles) andlw 0x0F ; Mask to give 0000XXXX in W iorwf PORTB,f ; To RB3..RB0 with RB7..RB4 unchanged bsf PORTB,LCD_e ; Pulse the E line high, nop ; wait, bcf PORTB,LCD_e ; and drop it again ; ; Transfer Least Significant nibble (YYYY portion of XXXXYYYY) ; movlw 0xF0 ; Set up mask andwf PORTB,f ; Clear old RB3..RB0 movf LCD_char,w ; Move LS nibble of into W andlw 0x0F ; Mask to give 0000YYYY in W iorwf PORTB,f ; To RB3..RB0 with RB7..RB4 unchanged bsf PORTB,LCD_e ; Pulse the E line high, nop ; wait, bcf PORTB,LCD_e ; and drop it again return ; ; ***************************************************************************** ; * * ; * Purpose: Write the byte of data at EEDATA to the EEPROM at address * ; * EEADR. * ; * * ; * Input: The values at EEDATA and EEADR. * ; * * ; * Output: The EEPROM value is updated. * ; * * ; * NOTE: ALL IN BANK 1 * ; ***************************************************************************** ; write_EEPROM bsf EECON1,WREN ; Set the EEPROM write enable bit ; Start required sequence bcf INTCON,GIE ; Disable interrupts movlw 0x55 ; Write 0x55 and 0xAA to EECON2 movwf EECON2 ; control register, as required movlw 0xAA ; movwf EECON2 ; bsf EECON1,WR ; Set WR bit to begin write ; End required sequence bit_check btfsc EECON1,WR ; Has the write completed? goto bit_check ; No, keep checking bsf EECON1,GIE ; Enable interrupts bcf EECON1,WREN ; Clear the EEPROM write enable bit incf EEADR,f ; Increment the EE write address return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: Read a byte of EEPROM data at address EEADR into EEDATA. * ; * * ; * Input: The address EEADR. * ; * * ; * Output: The value in EEDATA. * ; * * ; * NOTE: All in BANK 1 * ; ***************************************************************************** ; read_EEPROM ; bsf EEDATA,RD ; Request the read bsf EECON1,RD ; Request the read movf EEDATA,W ; Get the data incf EEADR,f ; Increment the read address return ; Return to the caller ; ; ***************************************************************************** ; * * ; * Purpose: Wait for a specified number of milliseconds. * ; * * ; * Entry point wait_a_sec: Wait for 1 second * ; * Entry point wait_256ms: Wait for 256 msec * ; * Entry point wait_128ms: Wait for 128 msec * ; * Entry point wait_64ms : Wait for 64 msec * ; * Entry point wait_32ms : Wait for 32 msec * ; * Entry point wait_16ms : Wait for 16 msec * ; * Entry point wait_8ms : Wait for 8 msec * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; ***************************************************************************** ; wait_a_sec ; ****** Entry point ****** call wait_256ms ; call wait_256ms ; call wait_256ms ; call wait_256ms ; return wait_256ms ; ****** Entry point ****** call wait_128ms ; call wait_128ms ; return wait_128ms ; ****** Entry point ****** movlw 0xFF ; Set up outer loop movwf timer1 ; counter to 255 goto outer_loop ; Go to wait loops wait_64ms ; ****** Entry point ****** movlw 0x80 ; Set up outer loop movwf timer1 ; counter to 128 goto outer_loop ; Go to wait loops wait_32ms ; ****** Entry point ****** movlw 0x40 ; Set up outer loop movwf timer1 ; counter to 64 goto outer_loop ; Go to wait loops wait_16ms ; ****** Entry point ****** movlw 0x20 ; Set up outer loop movwf timer1 ; counter to 32 goto outer_loop ; Go to wait loops wait_8ms ; ****** Entry point ****** movlw 0x10 ; Set up outer loop movwf timer1 ; counter to 16 wait_1ms movlw 0x2 movwf timer1 ; Fall through into wait loops ; ; Wait loops used by other wait routines ; - 1 microsecond per instruction (with a 4 MHz microprocessor crystal) ; - 510 instructions per inner loop ; - (Timer1 * 514) instructions (.514 msec) per outer loop ; - Round off to .5 ms per outer loop ; outer_loop movlw 0xFF ; Set up inner loop counter movwf timer2 ; to 255 inner_loop decfsz timer2,f ; Decrement inner loop counter goto inner_loop ; If inner loop counter not down to zero, ; then go back to inner loop again decfsz timer1,f ; Yes, Decrement outer loop counter goto outer_loop ; If outer loop counter not down to zero, ; then go back to outer loop again return ; Yes, return to caller ; ; ***************************************************************************** ; END