;***************************************************************************************** ; ; PROGRAM: SYNTH.ASM & INCLUDE FILE, SETTINGS.DAT ; ; ASSEMBLER: MPASMWIN v02.30 ; ; AUTHOR: S. JONES EMAIL stevejones@picknowl.com.au ; ; ; REVISION HISTORY: ; ; VERSION: DATE COMMENTS ; 1.00 05-02-2000 FIRST RELEASE. ; ; ; DESCRIPTION: ; ; Programmable synthesizer using a PIC 16F84, TSA5511 PLL and a 2x16 lcd. ; RC clock for pic (22pF & 5.6K = approx 3.6MHz, = 900KHz instruction cycle time) ; ; PLL frequency range of 50 to 1400MHz for TX and RX. ; Frequency Steps of 50Khz with a crystal reference of 3.2MHz. ; RX freq display range. 0 to 1900MHz. ; TX freq display range. 0 to 1900MHz. ; RX Display to VCO offset +/-500MHz. ; TX Display to VCO offset +/-500MHz. ; Display multiplier. (Multiplies all frequency displays on the lcd by N) ; ; User adjustable settings. ( Via setup screens. Accessible if shift ; button is pressed during power up ) ; 1) Display multiplier. 1 to 20 ; 2) Minimum RX display. 0 to 1900MHz ; 3) Maximum RX display. 0 to 1900MHz ; 4) RX VCO offset. -500 to 500MHz ; 5) Minimum TX display. 0 to 1900MHz ; 6) Maximum TX display. 0 to 1900MHz ; 7) TX VCO offset. -500 to 500MHz ; ; GENERAL COMMENTS ; ; A SINGLE PRESS OF THE FREQ + OR FREQ - BUTTONS WILL CHANGE THE PLL BY ONE STEP OF ; 50KHz. ; HOLDING A BUTTON DOWN FOR A FEW SECONDS WILL CHANGE THE PLL BY MULTIPLE STEPS ; OF 100KHz. ; ; IF THE SHIFT BUTTON IS HELD, A SINGLE PRESS OF THE FREQ + OR FREQ - BUTTONS WILL ; CHANGE THE PLL BY ONE STEP OF 1MHz. ; IF THE SHIFT BUTTON IS HELD, HOLDING A BUTTON DOWN FOR A FEW SECONDS WILL CHANGE ; THE PLL BY MULTIPLE STEPS OF 10MHz. ; ; IF THE PTT LEAD CHANGES STATE WHILE MODIFYING THE FREQUENCY, THE FREQ + AND ; FREQ - BUTTONS MUST BE RELEASED FOR ONE SECOND BEFORE THE FREQUENCY CAN BE ; MODIFIED AGAIN. (IE, PREVENTS CHANGING TX FREQ INADVERTANTLY IF THE PTT LEAD IS ; OPPERATED WHILE SETTING THE RX FREQUENCY) ; ; ANY CHANGES TO RX OR TX FREQUENCY ARE UPDATED IN EEPROM 2 SECONDS AFTER RELEASE OF ; THE FREQ + AND FREQ - BUTTONS. ONLY BYTES THAT HAVE CHANGED ARE REPROGRAMMED. ; ; TO GAIN ACCESS TO THE SETUP SCREENS, HOLD THE SHIFT BUTTON DOWN DURING POWER-UP, ; THEN A DOUBLE CLICK OF THE SHIFT BUTTON WILL TAKE YOU TO THE SETUP SCREENS. ; ; WHILE IN THE SETUP SCREENS, IF THERE ARE NO BUTTONS PRESSED FOR 10 SECONDS, YOU ; WILL BE RETURNED TO THE MAIN DISPLAY. ; ; CHANGES MADE TO THE SETUP, ARE UPDATED IN EEPROM UPON RETURN TO THE MAIN DISPLAY. ; ; TO MIMIMISE DISTERBANCE TO THE PLL, THE DIVIDER INFO THAT SETS THE PLL, IS SENT ; BY THE PIC TO THE PLL ONLY IF THE FREQUENCY NEEDS TO BE CHANGED. ; (IE OPPERATION OF THE PTT LEAD OR DURING MANUAL FREQUENCY CHANGES) ; ; WHEN THE PLL FREQUENCY IS CHANGED, IT IS ASSUMED THAT THE PLL IS UNLOCKED. THE PIC ; CHECKS THE PLL STATUS CONTINUOUSLY UNTIL THE PLL LOCKS. ONCE LOCKED, STATUS CHECKS ; ARE MADE EVERY SECOND. IF THE PLL GOES OUT OF LOCK, CONTINUOUS CHECKS ARE STARTED ; AGAIN. (TO DISABLE THE CHECKS EVERY SECOND, CHANGE THE 'LOCK_TM' EQUATE TO 255) ; ; IF AN ATTEMPT IS MADE TO SET THE PLL BELOW 50MHz, 'UNLOCKED' WILL BE DISPLAYED ; ON THE LCD. AND THE PLL WILL BE SET TO 50MHz. ; (IE, IF RX DISPLAY = 400MHz AND THE RX VCO OFFSET IS -500MHz) ; IF AN ATTEMPT IS MADE TO SET THE PLL ABOVE 1400MHz, 'UNLOCKED' WILL BE DISPLAYED ; ON THE LCD. AND THE PLL WILL BE SET TO 1400MHz. ; (IE, IF TX DISPLAY = 1000MHz AND THE TX VCO OFFSET IS 500MHz) ; ; THE LCD USED WITH THIS PROGRAM SHOULD 2 LINE x 16 CHARACTER. ; ; TWO METHODS OF MAKING SURE THE LCD IS NOT BUSY ARE AVALIABLE. ; 1) READING THE BUSY FLAG FROM THE LCD. ; 2) WAITING LONGER THAN THE MAXIMUM LCD EXECUTION TIME. ; IF THE LCD DOES NOT HAVE A R/W PIN, SET THE "LCD_USES_RW" EQUATE TO FALSE. THEN ; THERE WILL BE NO BUSY CHECKS MADE ON THE LCD. (SLIGHTLY SLOWER) ; A LCD DISPLAY THAT HAS THE R/W PIN CAN USE EITHER METHOD. SETTING THE ; "LCD_USES_RW" EQUATE TO TRUE IS SLIGHTLY FASTER BUT USES MORE CODE SPACE. ; THE SLOWER OPPERATION IS NOT NOTICABLE IN THIS APPLICATION. ; ;***************************************************************************************** ;***************************************************************************************** ; REGISTER DEFINITIONS ;***************************************************************************************** W EQU 0 F EQU 1 INDF SET 0X00 TMR0 SET 0X01 PCL SET 0X02 STATUS SET 0X03 FSR SET 0X04 PORTA SET 0X05 PORTB SET 0X06 EEDATA SET 0X08 EEADR SET 0X09 INTCON SET 0X0B OPTION_REG SET 0X01 EECON1 SET 0X08 EECON2 SET 0X09 #DEFINE CARRY STATUS,0 #DEFINE ZERO STATUS,2 #DEFINE RP0 STATUS,5 #DEFINE T0IF INTCON,2 #DEFINE RD EECON1,0 #DEFINE WR EECON1,1 #DEFINE WREN EECON1,2 #DEFINE WRERR EECON1,3 #DEFINE EEIF EECON1,4 ; CONFIGURATION BITS _CP_ON EQU H'3FEF' _CP_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FFF' _PWRTE_OFF EQU H'3FF7' _WDT_OFF EQU H'3FFB' _RC_OSC EQU H'3FFF' _HS_OSC EQU 0x3FFE PROCESSOR 16F84 ;SELECT PROCCESOR, AND ASSEMBLER OPTIONS. LIST F=INHX8M,N=0 RADIX DEC __CONFIG _HS_OSC & _WDT_OFF ;XTal OSCILLATOR BUT NO WATCHDOG. __MAXRAM H'CF' __BADRAM H'07', H'50'-H'7F', H'87' ;***************************************************************************************** ; GENERAL DEFINITIONS ;***************************************************************************************** #DEFINE SCL PORTA,0 ;FREQ + BUTTON. #DEFINE SDA PORTA,1 ;FREQ - BUTTON. #DEFINE RW PORTA,2 ;SHIFT BUTTON. #DEFINE PTT PORTA,2 ;SHIFT BUTTON. #DEFINE BUTTON_P PORTA,3 ;TX ENABLE OUTPUT PIN. 0 = ENABLED. #DEFINE BUTTON_N PORTA,4 ;PTT INPUT PIN. 0 = TX FREQ, 1 = RX FREQ #DEFINE BUTTON_S PORTB,0 ;I2C DATA O/P LINE. #DEFINE TX_EN PORTB,1 ;I2C CLOCK O/P LINE. #define E PORTB,2 ; LCD Enable 0=aus 1=an #define RS PORTB,3 ; LCD Register Select 0=CMD 1=Daten #define D4 PORTB,4 ; LCD Daten-Leitungen #define D5 PORTB,5 ; LCD Daten-Leitungen #define D6 PORTB,6 ; LCD Daten-Leitungen #define D7 PORTB,7 ; LCD Daten-Leitungen ; PORTB,4-7 ;LCD 4 BIT DATA BUS. #DEFINE LEADING_FLAG FLAGS,0 ;LEADING ZERO INDICATOR. #DEFINE NEGATIVE FLAGS,1 ;NUMBER TO DISPLAY IS NEGATIVE. #DEFINE PLL_VALID FLAGS,2 ;PLL DIVIDER NUMBER IS VALID. #DEFINE TIMEOUT FLAGS,3 ;IF DISP TIMEOUT SET, GO TO MAIN DISPLAY. #DEFINE EEPROM_UPDATE FLAGS,4 ;IF SET, UPDATE THE EEPROM. #DEFINE UNLOCKED FLAGS,5 ;IF SET ENABLE ACCESS TO SETUP DISPLAYS. #DEFINE PTT_COPY FLAGS,6 ;COPY OF CURRENT PTT STATE. #DEFINE PTT_CHANGED FLAGS,7 ;SET IF PTT HAS CHANGED. #DEFINE MAX_MIN_MOD FLAG2,1 ;SET IF MAXIMUM OR MINIMUM CHANGED ARG. #DEFINE PRESSED RESULT,0 ;RESULT FLAG FROM BUTTON PRESSED CALL. ;***************************************************************************************** ; GENERAL EQUATES ;***************************************************************************************** RESETVECTOR EQU H'00' ;PIC16F84 RESET VECTOR. INTVECTOR EQU H'04' ;PIC16F84 INTERUPT VECTOR. TRUE EQU H'FF' FALSE EQU H'00' LCD_USES_RW EQU FALSE ;TRUE IF USING LCD WITH R/W LEAD. ;FALSE IF USING LCD WITH NO R/W LEAD. DP EQU H'A5' ;LCD DECIMAL POINT. MIN_ON EQU 5 ;MIN VALID BUTTON PRESSED TIME. (5 x 9mS) MAX_ON EQU 60 ;MAX VALID BUTTON PRESSED TIME. (60 x 9mS) REPEAT EQU 30 ;REPEAT PERIOD. (30 x 9mS) MAX_ON_S EQU 30 ;MAX VALID SHIFT PRESSED TIME. (30 x 9mS) DOUBLE_TIME EQU 25 ;DOUBLE SHIFT WITHIN THIS TIME.(25 x 9mS) TIMEOUT_NUM EQU 1100 ;DISPLAY TIMEOUT. (1100 x 9mS) LOCK_TM EQU 110 ;TIME BETWEEN STATUS CHECKS. (110 x 9mS) ;255 = NO CHECKS ONCE IN LOCK. PLL_ADDRESS EQU B'11000010' ;PLL CHIP ADDRESS. ;PLL_ADDRESS EQU B'01000000' ;BUS EXPANDER CHIP ADDRESS. STEP_SIZE EQU 125 ;PLL FREQUENCY STEPS = 50KHz MAX_OFFSET EQU 50000 ;PLL MAXIMUM TX OR RX OFFSET = 500MHz PLL_MIN_FREQ EQU 2390000 ;PLL MINIMUM FREQUENCY = 50MHz PLL_MAX_FREQ EQU 2430000 ;PLL MAXIMUM FREQUENCY = 1400MHz TIMEOUT_NUM_X EQU 0 - TIMEOUT_NUM TIMEOUT_NUM_H EQU HIGH ( H'FFFF' & TIMEOUT_NUM_X ) TIMEOUT_NUM_L EQU LOW ( H'FFFF' & TIMEOUT_NUM_X ) PLL_SPAN EQU ( PLL_MAX_FREQ - PLL_MIN_FREQ )/ STEP_SIZE PLL_SPAN_H EQU HIGH PLL_SPAN PLL_SPAN_L EQU LOW PLL_SPAN OFFSET_MAX EQU MAX_OFFSET / STEP_SIZE ;MAXIMUM TX OR RX OFFSET = 500MHz OFFSET_MAX_H EQU HIGH OFFSET_MAX OFFSET_MAX_L EQU LOW OFFSET_MAX OFFSET_MIN EQU 0 - (MAX_OFFSET / STEP_SIZE) ;MINIMUM TX OR RX OFFSET = -500MHz OFFSET_MIN_H EQU HIGH ( H'FFFF' & OFFSET_MIN) OFFSET_MIN_L EQU LOW OFFSET_MIN PLL_DIV_MAX EQU PLL_MAX_FREQ / STEP_SIZE ;DIVIDER NUM IS INVALID IF GREATER PLL_DIV_MAX_H EQU HIGH PLL_DIV_MAX ;THAN 1400MHz. PLL_DIV_MAX_L EQU LOW PLL_DIV_MAX PLL_DIV_MIN EQU PLL_MIN_FREQ / STEP_SIZE ;DIVIDER NUM IS INVALID IF LESS PLL_DIV_MIN_H EQU HIGH PLL_DIV_MIN ;THAN 50MHz. PLL_DIV_MIN_L EQU LOW PLL_DIV_MIN DISP_MAX EQU (PLL_MAX_FREQ / STEP_SIZE) + OFFSET_MAX DISP_MAX_H EQU HIGH DISP_MAX DISP_MAX_L EQU LOW DISP_MAX ;MAXIMUM TX OR RX DISP = 1900MHz. ;***************************************************************************************** ORG H'2100' INCLUDE "13cm.DAT" ;FILE TO SET THE INITIAL PLL & DISPLAY ; FREQ RANGE INTO EEPROM. ;***************************************************************************************** ;***************************************************************************************** ; RAM VARIABLES 68 BYTES MAX ;***************************************************************************************** CBLOCK H'0C' ;RAM STARTS AT H'0C' COUNT ;GENERAL COUNTER VARIABLES. COUNT1 COUNT2 SAVE_STATUS ;FOR INTERUPT SERVIVE ROUTINE. SAVE_W_REG ; " BUT_P_CNT ;BUTTON PRESSED TIME COUNT. (x 9mS) BUT_N_CNT ; " BUT_S_CNT ; " BUT_P_OLD ;STATE OF OLD BUTTON COUNT. BUT_N_OLD ; " BUT_S_OLD ; " S_TIMER ;TIMER FOR DOUBLE PRESS OF SHIFT BUTTON. LOCK_TIMER ;TIMER FOR CHECKING PLL STATUS. RESULT ;RESULT OF A BUTTON CHECK CALL. TIMEOUT_H ;16 BIT DISPLAY TIMEOUT DOWN COUNTER, TIMEOUT_L ;RETURN TO MAIN DISPLAY WHEN = 0. EEPROM_WRITE ;EEPROM WRITE UPDATE DOWN COUNTER. FLAGS ;8 MISC FLAGS. FLAG2 ; " PLL_STATUS ;STATUS BYTE FROM PLL READ. TEMP ;TEMPORARY STORAGE. TEMP_H ; " TEMP_L ; " MULT_NUM ;COPY OF CURRENT SETUP DATA FROM EEPROM. RX_DISP_FREQ:2 ; " TX_DISP_FREQ:2 ; " MIN_RX_DISP_FR:2 ; " MAX_RX_DISP_FR:2 ; " RX_VCO_OFFSET:2 ; " MIN_TX_DISP_FR:2 ; " MAX_TX_DISP_FR:2 ; " TX_VCO_OFFSET:2 ; " PLL_OLD:2 ;PREVIOUS PLL DIVIDER NUMBER. ARG ;8 BIT BINARY BUFFER (MATHS). ARG_H ;16 BIT BINARY BUFFER (MATHS). ARG_L ; " ARG2_H ;16 BIT BINARY BUFFER (MATHS). ARG2_L ; " ARG3_H ;16 BIT BINARY BUFFER (MATHS). ARG3_L ; " RESULT_H ;24 BIT BINARY BUFFER (MATHS). RESULT_M ; " RESULT_L ; " NEG_POSITION ;LOCATION OF THE - SIGN IN THE DEC BUFFER. DEC_0 ;8 DIGIT DECIMAL BUFFER. MSD DEC_1 ; " DEC_2 ; " DEC_3 ; " DEC_4 ; " DEC_5 ; " DECIMAL_POINT ; " DECIMAL POINT. DEC_6 ; " DEC_7 ; " LSD ENDC ;***************************************************************************************** ; ; INITIALISE THE HARDWARE. ; ;***************************************************************************************** ORG RESETVECTOR CALL LONG_DELAY ;WAIT UNTIL LCD AND PLL HAVE INITIALISED. MOVLW H'0D' ;POINT TO START OF RAM. (JUST PAST COUNT) MOVWF FSR GOTO CONTINUE ;JUMP OVER INTERUPT ROUTINES. ;***************************************************************************************** ; INTERRUPT SERVICE ROUTINE ; ; SERVICE THE TMR0 INTERUPT (EVERY 9mS) ; ; INCREMENT THE BUTTON PRESSED COUNTERS IF THE CORROSPONDING BUTTON ; IS PRESSED & SET THE DISPLAY TIMEOUT & EEPROM WRITE DOWN COUNTER. ; ELSE SET THE BUTTON COUNTER TO 0. ; ; DEC THE 8 BIT EEPROM WRITE, DOWN COUNTER. WHEN IT REACHES 0, SET THE ; EEPROM WRITE FLAG. (INDICATES WHEN TO UPDATE THE EEPROM, IE 2 SECONDS ; AFTER RELEASING THE FREQ + OR - BUTTON WRITE ANY CHANGES TO EEPROM) ; ; DECREMENT THE 16 BIT DISPLAY DOWN COUNTER. WHEN IT REACHES 0, SET THE ; TIMEOUT FLAG. (IF NO BUTTONS HAVE BEEN PRESSED FOR A WHILE. INDICATES ; WHEN TO RETURN TO THE MAIN DISPLAY) ; ; DEC THE 8 BIT SHIFT BUTTON DOUBLE PRESS, DOWN COUNTER. WONT DEC PAST 0. ; (THE FIRST PRESS SETS THE COUNTER. A DOUBLE PRESS IS VALID IF THE SECOND ; PRESS OCCURS WHILE THE COUNTER IS NON ZERO) ; ; DEC THE 8 BIT LOCK TIMER, DOWN COUNTER. WONT DEC PAST 0. ; WHEN EQUAL TO 0 ITS TIME TO CHECK IF THE PLL IS LOCKED. ;***************************************************************************************** ORG INTVECTOR BTFSS T0IF ;WAS THE INTERUPT CAUSED BY TMR0 ? RETFIE ;N. JUST RETURN & RE-ENABLE INTERUPTS. MOVWF SAVE_W_REG ;Y. SAVE WREG & STATUS REGS. SWAPF STATUS,W MOVWF SAVE_STATUS BCF RP0 ;MAKE SURE WE ARE ADDRESSING BANK 0. ;----------------------------------------------------------------------------------------- CHK_BUT_P: BTFSS BUTTON_P ;IS FREQ + BUTTON PRESSED? GOTO INC_P_CNT CLRF BUT_P_CNT ;N. CLEAR THE COUNT. GOTO CHK_BUT_N INC_P_CNT: INCFSZ BUT_P_CNT,W ;Y. INC THE COUNT. MOVWF BUT_P_CNT ; BUT DONT INC PAST 255. CALL SET_TIMEOUT ; SET THE DISPLAY TIMEOUT DOWN COUNTER. MOVLW H'FF' ; SET THE EEPROM WRITE DOWN COUNTER. MOVWF EEPROM_WRITE ;----------------------------------------------------------------------------------------- CHK_BUT_N: BTFSS BUTTON_N ;IS FREQ - BUTTON PRESSED? GOTO INC_N_CNT CLRF BUT_N_CNT ;N. CLEAR THE COUNT. GOTO CHK_BUT_S INC_N_CNT: INCFSZ BUT_N_CNT,W ;Y. INC THE COUNT. MOVWF BUT_N_CNT ; BUT DONT INC PAST 255. CALL SET_TIMEOUT ; SET THE DISPLAY TIMEOUT DOWN COUNTER. MOVLW H'FF' ; SET THE EEPROM WRITE DOWN COUNTER. MOVWF EEPROM_WRITE ;----------------------------------------------------------------------------------------- CHK_BUT_S: BTFSS BUTTON_S ;IS SHIFT BUTTON PRESSED? GOTO INC_S_CNT CLRF BUT_S_CNT ;N. CLEAR THE COUNT. GOTO CHK_TIMEOUT INC_S_CNT: INCFSZ BUT_S_CNT,W ;Y. INC THE COUNT. MOVWF BUT_S_CNT ; BUT DONT INC PAST 255. CALL SET_TIMEOUT ; SET THE DISPLAY TIMEOUT DOWN COUNTER. ;----------------------------------------------------------------------------------------- CHK_TIMEOUT: MOVF TIMEOUT_H,W ;GET THE DISPLAY TIMEOUT. IORWF TIMEOUT_L,W BTFSC ZERO ;IS IT = 0? GOTO T_DONE ;Y. DONT DO ANYTHING. INCFSZ TIMEOUT_L,F ;INC LOW BYTE. IS IT = 0 ? GOTO T_DONE ;N. ALL DONE. INCFSZ TIMEOUT_H,F ;Y. INC THE HIGH BYTE. IS IT ALSO = 0 ? GOTO T_DONE ; N. ALL DONE. BSF TIMEOUT ; Y. SET THE TIMEOUT FLAG. T_DONE: ;----------------------------------------------------------------------------------------- MOVF S_TIMER,W ;GET THE DOUBLE PRESSED SHIFT COUNTER. BTFSS ZERO ;IS IT = 0? DECF S_TIMER,F ;N. DEC THE COUNTER. ;----------------------------------------------------------------------------------------- INCF LOCK_TIMER,W ;GET THE LOCK TIME COUNTER. BTFSC ZERO ;IS IT = 255? GOTO NO_LOCK_TM_DEC MOVF LOCK_TIMER,W ;N. GET THE LOCK TIME COUNTER. BTFSS ZERO ; IS IT = 0? DECF LOCK_TIMER,F ; N. DEC THE COUNTER. NO_LOCK_TM_DEC: ;Y. DONT DEC THE COUNTER. ;----------------------------------------------------------------------------------------- MOVF EEPROM_WRITE,W ;GET THE EEPROM WRITE TIMEOUT COUNTER. BTFSC ZERO ;IS IT = 0? GOTO EEPROM_WR_DONE ;Y. DONT DO ANYTHING. DECFSZ EEPROM_WRITE,F ;N. DEC THE COUNTER. COUNT = 0? GOTO EEPROM_WR_DONE ; N. ALL DONE. BSF EEPROM_UPDATE ; Y. SET THE FLAG. (UPDATE THE EEPROM) EEPROM_WR_DONE: ;----------------------------------------------------------------------------------------- RS_INT_STATUS: SWAPF SAVE_STATUS,W MOVWF STATUS ;RESTORE STATUS REG. SWAPF SAVE_W_REG,F ;SAVE WREG. SWAPF SAVE_W_REG,W ;RESTORE WREG. BCF T0IF ;CLEAR TMR0 INTERUPT FLAG. RETFIE ;----------------------------------------------------------------------------------------- SET_TIMEOUT: MOVLW TIMEOUT_NUM_H ;SET THE TIMEOUT COUNTER. MOVWF TIMEOUT_H MOVLW TIMEOUT_NUM_L MOVWF TIMEOUT_L RETURN ;----------------------------------------------------------------------------------------- ;***************************************************************************************** ; TEXT TABLE (RETLW 'X') ; ; PLACED IN PAGE 0, SO WE DONT HAVE TO BOTHER WITH PAGE BITS. ;***************************************************************************************** TABLE: ADDWF PCL,F ;JUMP TO CHAR POINTED TO IN W REG. ST: ;LABEL THE START OF TEXT STRINGS. TX: EQU $ - ST DT "TX",0 RX: EQU $ - ST DT "RX",0 UN: EQU $ - ST DT "X",0 LOCKED: EQU $ - ST DT "L",0 TWO_SPACES: EQU $ - ST DT " ",0 DISP: EQU $ - ST DT " DISPLAY ",0 MULT: EQU $ - ST DT "MULT",0 MAX: EQU $ - ST DT "MAX",0 MIN: EQU $ - ST DT "MIN ",0 VCO: EQU $ - ST DT " VCO OFFSET ",0 MHZ: EQU $ - ST DT "MHz",0 TABLE_END: ;***************************************************************************************** ; ; INITIALISE THE HARDWARE. (CONTINUED) ; ;***************************************************************************************** CONTINUE: MOVLW 67 ;67 BYTES OF RAM TO CLEAR. MOVWF COUNT CLEAR_LOOP: CLRF INDF ;CLEAR THE RAM BYTE. INCF FSR,F ;INC THE RAM ADDRESS POINTER. DECFSZ COUNT,F ;DEC THE COUNT. GOTO CLEAR_LOOP ;LOOP UNTIL ALL BYTES DONE. BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00011000' MOVWF PORTA ;RA0,RA1,RA2,RA4 = INPUTS, RA3 OUTPUT. MOVLW B'00000011' MOVWF PORTB ;RB1-RB7 = OUTPUT, RB0 = INPUT. MOVLW B'00000100' ;SET PRESCALER TO TMR0. MOVWF OPTION_REG ;TMR0 RATE = 1/32 OF INSTRUCTION CLOCK. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. CLRF TMR0 ;SETS 1ST INTERUPT TO 9mS. MOVLW B'10100000' ;ENABLE TMR0 OVERFLOW INTERUPT. MOVWF INTCON MOVLW B'11111110' ;SET RB1-7 HIGH, RB0 WILL BE LOW MOVWF PORTB ;WHEN SET AS AN OUTPUT. BSF TX_EN ;SET TX_EN PIN HIGH, PLL NOT LOCKED YET. CALL INIT_LCD ;SETUP THE LCD DISPLAY. BTFSS BUTTON_S ;IS THE SHIFT BUTTON PRESSED? BSF UNLOCKED ;Y. ENABLE ACCESS TO THE SETUP DISPLAYS. CALL GET_SETUP ;GET SETUP INFO FROM EEPROM. ;***************************************************************************************** ; MAIN DISPLAY ;***************************************************************************************** MAIN_DISP: CALL SET_TIMEOUT ;SET THE DISPLAY TIMEOUT DOWN COUNTER. BCF TIMEOUT ;CLEAR THE DISPLAY TIMEOUT FLAG. MOVLW H'C7' CALL LCD_CMD ;POSITION THE CURSOR, LINE 2 POS 8. MOVLW LOCKED ;DISPLAY 'L'. CALL LCD_TEXT CALL DISP_MHZ ;DISPLAY ' MHz ' ON LINE 2 POS 3. MAIN_LOOP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. REDRAW THE MAIN DISPLAY. CALL UPDATE_EEPROM ;SEE IF EEPROM UPDATE IS REQUIRED. BTFSS PTT ;CHECK PTT LEAD. 0 = TX, 1 = RX. GOTO PTT_IS_TX BTFSS PTT_COPY ;HAS PTT LEAD GONE HIGH SINCE LAST PASS. BSF PTT_CHANGED ;Y. SET THE PTT CHANGED FLAG. BSF PTT_COPY ;KEEP A COPY OF CURRENT PTT HIGH STATE. ;MOVLW 'R' ;IF PTT IS HIGH, DISPLAY 'RX '. GOTO PTT_DONE PTT_IS_TX: BTFSC PTT_COPY ;HAS PTT LEAD GONE LOW SINCE LAST PASS. BSF PTT_CHANGED ;Y. SET THE PTT CHANGED FLAG. BCF PTT_COPY ;KEEP A COPY OF CURRENT PTT LOW STATE. ;MOVLW 'T' ;PREPARE TO DISPLAY 'TX '. PTT_DONE: ;CALL LCD_CHR ;DISPLAY THE 'T' OR 'R'. MOVLW H'C6' CALL LCD_CMD ;POSITION THE CURSOR, LINE 2 POS 7. MOVLW UN ;PREPARE TO DISPLAY 'X'. BTFSS PLL_VALID ;IS THE PLL DIVIDER SETTING VALID GOTO PLL_NOT_LOCKED BTFSS PLL_STATUS,6 ;AND, IS THE PLL LOCKED? GOTO PLL_NOT_LOCKED MOVLW TWO_SPACES ;Y. DISPLAY ' '. PLL_NOT_LOCKED: CALL LCD_TEXT ;N. DISPLAY 'UN'. BTFSS PTT_CHANGED ;HAS THE PTT CHANGED? GOTO NO_PTT_CHANGE BTFSC EEPROM_WRITE,7 ;Y. HAS FREQ +/- BEEN RELEASED FOR A SEC? GOTO SKIP_BUTTONS ; N. DONT INC/DEC FREQUENCY. BCF PTT_CHANGED ; Y. CLEAR THE PTT CHANGED FLAG. ;N. ITS OK TO INC/DEC THE FREQUENCY. NO_PTT_CHANGE: BTFSS PTT_COPY ;CHECK THE OLD PTT LEAD. 0 = TX, 1 = RX. GOTO OLD_PTT_IS_TX MOVLW RX_DISP_FREQ ;OLD PTT IS HIGH SO, CALL MOVE_TO_ARG ;PREPARE TO INC/DEC RX FREQ. CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. MOVLW MIN_RX_DISP_FR ;MAKE SURE WE DONT GO BELOW THE MINIMUM CALL MOVE_TO_ARG2 ;SET BY MIN_RX_DISP_FR. CALL MAXIMUM MOVLW MAX_RX_DISP_FR ;MAKE SURE WE DONT GO ABOVE THE MAXIMUM CALL MOVE_TO_ARG2 ;SET BY MAX_RX_DISP_FR. CALL MINIMUM MOVLW RX_DISP_FREQ ;SAVE THE RX FREQ. GOTO OLD_PTT_DONE OLD_PTT_IS_TX: MOVLW TX_DISP_FREQ ;OLD PTT IS LOW SO, CALL MOVE_TO_ARG ;PREPARE TO INC/DEC TX FREQ. CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. MOVLW MIN_TX_DISP_FR ;MAKE SURE WE DONT GO BELOW THE MINIMUM CALL MOVE_TO_ARG2 ;SET BY MIN_TX_DISP_FR. CALL MAXIMUM MOVLW MAX_TX_DISP_FR ;MAKE SURE WE DONT GO ABOVE THE MAXIMUM CALL MOVE_TO_ARG2 ;SET BY MAX_TX_DISP_FR. CALL MINIMUM MOVLW TX_DISP_FREQ ;SAVE THE TX FREQ. OLD_PTT_DONE: CALL COPY_FROM_ARG SKIP_BUTTONS: CALL UPDATE_PLL ;UPDATE PLL AND RETURN THE DISPLAY FREQ. CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. BTFSS UNLOCKED ;IS ACCESS TO SETUP DISPLAYS ALLOWED? GOTO MAIN_LOOP ;N. CONTINUE UPDATING THE DISPLAY. CALL CHK_S_DOUBLE BTFSS PRESSED ;SHIFT BUTTON PRESSED BRIEFLY TWICE? GOTO MAIN_LOOP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; MULTIPLIER MENU ;***************************************************************************************** CALL LCD_CLEAR ;CLEAR THE DISPLAY. MOVLW DISP CALL LCD_TEXT ;DISPLAY ' DISPLAY '. MOVLW MULT CALL LCD_TEXT ;DISPLAY 'MULT'. MULT_DISP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. CALL CHK_P_MULT ;IF THE FREQ + BUTTON IS PRESSED. BTFSC PRESSED INCF MULT_NUM,F ;INC THE MULTIPLIER BY 1. CALL CHK_N_MULT ;IF THE FREQ - BUTTON IS PRESSED. BTFSC PRESSED DECF MULT_NUM,F ;DEC THE MULTIPLIER BY 1. MOVLW 1 SUBWF MULT_NUM,W BTFSC CARRY ;WAS NUMBER LESS THAN 1. GOTO MULT_0 ;N. IT WAS 1 OR ABOVE. MOVLW 1 MOVWF MULT_NUM ;Y. SET IT BACK TO 1. MULT_0: MOVLW 21 SUBWF MULT_NUM,W BTFSS CARRY ;WAS NUMBER GREATER THAN 20. GOTO BIN_DEC ;N. IT WAS 20 OR BELOW. MOVLW 20 MOVWF MULT_NUM ;Y. SET IT BACK TO 20. BIN_DEC: CLRF DEC_1 ;CLEAR THE MSD. MOVF MULT_NUM,W MOVWF DEC_2 ;PUT NUM TO CONVERT IN LSD. TENTH MOVLW 10 SUBWF DEC_2,W ;SUBTRACT 10 FROM NUM. BTFSS CARRY ;IS THE RESULT NEGATIVE? GOTO DISP_NUMBER ;Y. ALL DONE. MOVWF DEC_2 INCF DEC_1,F ;N. INC THE 10'S GOTO TENTH ; LOOP UNTIL DONE. DISP_NUMBER: MOVLW H'8E' ;POSITION THE CURSOR, LINE 1 POS 15. CALL LCD_CMD MOVF DEC_1,W ;DISPLAY THE NUMBER IN ASCII FORM. ADDLW H'30' CALL LCD_CHR MOVF DEC_2,W ADDLW H'30' CALL LCD_CHR CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO MULT_DISP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; MINIMUM RX DISPLAY FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW MIN CALL LCD_TEXT ;DISPLAY 'MIN '. MOVLW RX CALL LCD_TEXT ;DISPLAY 'RX'. MOVLW DISP CALL LCD_TEXT ;DISPLAY ' DISPLAY '. CALL DISP_MHZ ;DISPLAY ' MHz ' ON LINE 2 POS 11. MIN_RX_DISP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW MIN_RX_DISP_FR ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL UP_DOWN_MIN ;CHECK +/- BUTTONS, & UPPER/LOWWER LIMIT. MOVLW MIN_RX_DISP_FR ;SAVE THE VARIABLE THAT WAS INC/DEC. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO MIN_RX_DISP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; MAXIMUM RX DISPLAY FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW MAX CALL LCD_TEXT ;DISPLAY 'MAX'. MAX_RX_DISP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW MAX_RX_DISP_FR ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL MOVE_TO_ARG CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. MOVLW MIN_RX_DISP_FR ;GET THE MIN RX DISPLAY FREQ. CALL MAX_CHECK ;MAKE SURE WE DONT GO BELOW THAT MINIMUM. MOVLW MAX_RX_DISP_FR ;SAVE THE MODIFIED INC/DEC VARIABLE. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO MAX_RX_DISP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; RX VCO OFFSET FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW RX ;DISPLAY 'RX'. CALL LCD_TEXT MOVLW VCO CALL LCD_TEXT ;DISPLAY ' VCO OFFSET '. RX_VCO_OFF: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW RX_VCO_OFFSET ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL UP_DOWN_OFFSET ;INC/DEC THE OFFSET WITHIN LIMITS. MOVLW RX_VCO_OFFSET ;SAVE THE MODIFIED INC/DEC VARIABLE. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO RX_VCO_OFF ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; MINIMUM TX DISPLAY FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW MIN CALL LCD_TEXT ;DISPLAY 'MIN '. MOVLW TX CALL LCD_TEXT ;DISPLAY 'TX'. MOVLW DISP CALL LCD_TEXT ;DISPLAY ' DISPLAY '. MIN_TX_DISP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW MIN_TX_DISP_FR ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL UP_DOWN_MIN ;CHECK +/- BUTTONS, & UPPER/LOWWER LIMIT. MOVLW MIN_TX_DISP_FR ;SAVE THE MODIFIED INC/DEC VARIABLE. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO MIN_TX_DISP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; MAXIMUM TX DISPLAY FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW MAX CALL LCD_TEXT ;DISPLAY 'MAX'. MAX_TX_DISP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW MAX_TX_DISP_FR ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL MOVE_TO_ARG CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. MOVLW MIN_TX_DISP_FR ;GET THE MIN TX DISPLAY FREQ. CALL MAX_CHECK ;MAKE SURE WE DONT GO BELOW THAT MINIMUM. MOVLW MAX_TX_DISP_FR ;SAVE THE MODIFIED INC/DEC VARIABLE. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO MAX_TX_DISP ;N. CONTINUE UPDATING THE DISPLAY. ;Y. GOTO NEXT MENU. ;***************************************************************************************** ; TX VCO OFFSET FREQUENCY ;***************************************************************************************** CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW TX ;DISPLAY 'TX'. CALL LCD_TEXT MOVLW VCO CALL LCD_TEXT ;DISPLAY ' VCO OFFSET '. TX_VCO_OFF: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. RETURN TO THE MAIN DISPLAY. CALL UPDATE_PLL ;UPDATE PLL AND TX_EN LEAD. MOVLW TX_VCO_OFFSET ;MOVE THE VARIABLE TO INC/DEC TO ARG. CALL UP_DOWN_OFFSET ;INC/DEC THE OFFSET WITHIN LIMITS. MOVLW TX_VCO_OFFSET ;SAVE THE MODIFIED INC/DEC VARIABLE. CALL COPY_FROM_ARG CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. CALL CHK_S_BRIEF BTFSS PRESSED ;SHIFT PRESSED BRIEFLY? GOTO TX_VCO_OFF ;N. CONTINUE UPDATING THE DISPLAY. GOTO MAIN_DISP ;Y. GO BACK TO THE MAIN DISPLAY. ;***************************************************************************************** ; MISC ROUTINES USED BY THE DISPLAY ROUTINES. ; ; DISP_MHZ DISPLAY ' MHz ' ON LINE 2 POS 11. ;. ; DISPLAY_FREQ DISPLAY A NUMBER ON LINE 2 OF LCD. ; NUM x 5 x DISP_MULT. ; ; UP_DOWN_OFFSET INC/DEC THE RX/TX VCO OFFSET, ; AND MAKES SURE THE OFFSET IS NOT TO BIG. ; ; UP_DOWN_MIN USED BY MIN TX & RX MENU DISPLAYS. ; ; MAX_CHECK USED BY MAX TX & RX MENU DISPLAYS. ; ; UPDATE_PLL CHECK THE STATE OF PTT, SELECT RX/TX FREQ, ; ADD ANY OFFSET & SET THE PLL DIVIDER. ; ALSO CHECKS PLL STATUS. ;***************************************************************************************** DISP_MHZ: MOVLW H'C2' CALL LCD_CMD ;POSITION THE CURSOR, LINE 2 POS 11. MOVLW MHZ CALL LCD_TEXT ;DISPLAY ' MHz'. RETURN ;----------------------------------------------------------------------------------------- MAX_CHECK: CALL MOVE_TO_ARG2 ;GET MIN_RX_DISP_FR OR MIN_TX_DISP_FR. GOTO U_D_MIN ;MAKE SURE THE RESULT IS NOT TO LARGE. ;----------------------------------------------------------------------------------------- UP_DOWN_MIN: CALL MOVE_TO_ARG CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. CLRF ARG2_H ;MAKE SURE THE RESULT IS NOT TO SMALL. CLRF ARG2_L U_D_MIN: CALL MAXIMUM MOVLW DISP_MAX_H ;MAKE SURE THE RESULT IS NOT TO LARGE. MOVWF ARG2_H MOVLW DISP_MAX_L MOVWF ARG2_L CALL MINIMUM RETURN ;----------------------------------------------------------------------------------------- DISPLAY_FREQ: BCF NEGATIVE ;CLEAR THE NEGATIVE FLAG. COMF ARG_H,W ;NUM BETWEEN 49151 AND 65535(C000 - FFFF) ANDLW B'11000000' ;WE CLASS AS A NEGATIVE NUMBER. BTFSS ZERO ;IS IT A NEGATIVE NUMBER? GOTO DISP_FREQ ;N. JUST CONTINUE. BSF NEGATIVE ;Y. SET THE NEGATIVE FLAG. COMF ARG_H,F ; CONVERT IT TO A + NUMBER. COMF ARG_L,F ; COMPLIMENT ARG, THEN ADD 1. MOVLW 1 ADDWF ARG_L,F BTFSC CARRY INCF ARG_H,F DISP_FREQ: MOVF MULT_NUM,W ;GET THE DISPLAY MULTIPLIER NUMBER. MOVWF ARG BCF CARRY ;Teiler anpassen auch für 10 GHz ;RLF ARG_H,F ;Teiler * 2 ;BCF CARRY ;Multiplikator = 250 !!! ;RLF ARG_L,F ;BTFSC CARRY ;INCF ARG_H,F ;BCF CARRY RLF ARG,F ;Multiplikator für 2,5 GHz =1 !!! RLF ARG,F ;SIZE OF 50KHz. (125kHz /25kHz = ADDWF ARG,F MOVF ARG,W MOVWF ARG BCF CARRY ;MULTIPLY THE MULTIPLIER BY 5 RLF ARG,F ;TO TAKE INTO ACCOUNT THE PLL STEP RLF ARG,F ;SIZE OF 50KHz. ADDWF ARG,F MOVF ARG,W MOVWF ARG BCF CARRY ;MULTIPLY THE MULTIPLIER BY 5 RLF ARG,F ;TO TAKE INTO ACCOUNT THE PLL STEP RLF ARG,F ;SIZE OF 50KHz. ADDWF ARG,F CALL MULTIPLY ;ARG x 125 x MULT_NUM. CALL BIN_TO_DEC ;CONVERT THE NUMBER TO ASCII DECIMAL. MOVLW H'80' ;POSITION THE CURSOR, LINE 2 POS 2(C1). CALL LCD_CMD MOVLW DEC_0 ;SET THE POINTER TO THE START MOVWF FSR ;OF THE DECIMAL BUFFER. MOVLW 8 ;8 DIGIT + DECIMAL POINT TO DISPLAY. MOVWF COUNT DISP_LOOP: MOVF INDF,W ;GET CHR POINTED TO BY FSR. CALL LCD_CHR ;DISPLAY THE CHR. INCF FSR,F ;INC THE POINTER. DECFSZ COUNT,F ;ALL DONE? GOTO DISP_LOOP ;N. CONTINUE. MOVLW H'C0' ;POSITION THE CURSOR, LINE 2 POS 1 CALL LCD_CMD MOVF INDF,W ;GET letzte Stelle CALL LCD_CHR ;DISPLAY THE CHR. RETURN ;----------------------------------------------------------------------------------------- UP_DOWN_OFFSET: CALL MOVE_TO_ARG MOVLW B'10000000' ;ADD H'8000' TO ARG. MAKES IT EASY TO XORWF ARG_H,F ;DEAL WITH NEGATIVE NUMBERS. CALL UP_DOWN ;CHECK UP DOWN BUTTONS FOR INC/DEC. MOVLW OFFSET_MAX_H ^ H'80' MOVWF ARG2_H ;MAKE SURE THE RESULT IS NOT TO LARGE. MOVLW OFFSET_MAX_L ;ARG2 = +500MHz. (+ H'8000') MOVWF ARG2_L CALL MINIMUM ;RETURN THE MINIMUM OF ARG & ARG2. MOVLW OFFSET_MIN_H ^ H'80' MOVWF ARG2_H ;MAKE SURE THE RESULT IS NOT TO SMALL. MOVLW OFFSET_MIN_L ;ARG2 = -500MHz. (+ H'8000') MOVWF ARG2_L CALL MAXIMUM ;RETURN THE MAXIMUM OF ARG & ARG2. MOVLW B'10000000' ;REMOVE THE H'8000' OFFSET. XORWF ARG_H,F RETURN ;----------------------------------------------------------------------------------------- UPDATE_PLL: CALL GET_PLL_STATUS ;UPDATE THE STATUS BYTE. (PLL LOCKED ?) BTFSS PTT ;CHECK PTT LEAD. 0 = TX, 1 = RX. GOTO PTT_TX MOVLW RX_DISP_FREQ ;RX. MOVE RX DISPLAY FREQ TO ARG. CALL MOVE_TO_ARG MOVLW TEMP_H ;RX. SAVE RX DISP FREQ FOR LATER ON. CALL COPY_FROM_ARG MOVLW RX_VCO_OFFSET ;RX. GET THE RX VCO OFFSET. GOTO DONE_PTT PTT_TX: MOVLW TX_DISP_FREQ ;TX. MOVE TX DISPLAY FREQ TO ARG. CALL MOVE_TO_ARG MOVLW TEMP_H ;TX. SAVE TX DISP FREQ FOR LATER ON. CALL COPY_FROM_ARG MOVLW TX_VCO_OFFSET ;TX. GET THE RX VCO OFFSET. DONE_PTT: CALL MOVE_TO_ARG2 CALL ADD ;ADD THE VCO OFFSET. CALL SET_PLL ;SEND THE DIVIDER NUMBER TO THE PLL. MOVLW TEMP_H ;RECOVER THE TX/RX DISP FREQ. CALL MOVE_TO_ARG RETURN ;***************************************************************************************** ; LCD ROUTINES. LCD_CHR SEND A CHARACTER TO THE LCD DISPLAY. ; LCD_CMD SEND A COMMAND INSTRUCTION TO THE LCD. ; LCD_TEXT SEND A TEXT STRING TO THE LCD. ; LCD_L1_P2 POSITION CURSOR LINE 1, POS 2. ; LCD_CLEAR CLEAR THE LCD, GOTO THE START OF LINE 1. ; LCD_SPACE DISPLAY A SPACE ON THE LCD. ; LCD_2_SPACES DISPLAY TWO SPACES ON THE LCD. ; LCD_3_SPACES DISPLAY THREE SPACES ON THE LCD. ;***************************************************************************************** LCD_3_SPACES CALL LCD_SPACE LCD_2_SPACES CALL LCD_SPACE LCD_SPACE: MOVLW ' ' IF LCD_USES_RW ;ASSEMBLE IF USING STANDARD LCD. LCD_CHR: CALL LCD_BUSY_? ;WAIT UNTIL THE LCD IS NOT BUSY. BSF RS ;SELECT DISPLAY DATA REGISTER. GOTO LCD_BYTE ELSE LCD_CHR: MOVWF TEMP_H ;SAVE THE BYTE TO SEND TO THE LCD. MOVWF TEMP_L BSF RS ;SELECT DISPLAY DATA REGISTER. GOTO LCD_BYTE ENDIF ;----------------------------------------------------------------------------------------- IF LCD_USES_RW ;ASSEMBLE IF USING STANDARD LCD. LCD_CLEAR: MOVLW H'01' ;CLEAR THE DISPLAY. GOTO LCD_CMD LCD_L1_P2: MOVLW H'81' ;POSITION THE CURSOR, LINE 1 POS 2. LCD_CMD: CALL LCD_BUSY_? ;WAIT UNTIL THE LCD IS NOT BUSY. BCF RS ;SELECT INSTRUCTION REGISTER. CALL LCD_BYTE ;SEND THE BYTE TO THE LCD. BSF RS ;RESTORE THE RS (SCL)TO ITS NORMAL STATE. RETURN ELSE ;ASSEMBLE IF USING LCD WITH NO R/W LEAD. LCD_CLEAR: MOVLW H'01' ;CLEAR THE DISPLAY. CALL LCD_CMD MOVLW 24 ;WAIT 20mS. LCD EXECUTION TIME = 5mS. CALL DELAY2 ;(845uS x 24) RETURN LCD_L1_P2: MOVLW H'81' ;POSITION THE CURSOR, LINE 1 POS 2. LCD_CMD: MOVWF TEMP_H ;SAVE THE BYTE TO SEND TO THE LCD. MOVWF TEMP_L BCF RS ;SELECT INSTRUCTION REGISTER. CALL LCD_BYTE ;SEND THE BYTE TO THE LCD. BSF RS ;RESTORE THE RS (SCL)TO ITS NORMAL STATE. RETURN ENDIF ;----------------------------------------------------------------------------------------- LCD_TEXT: MOVWF TEMP ;TEMP HOLDS TEXT START ADDRESS. CALL TABLE ANDLW H'FF' BTFSC ZERO ;AT END OF MESSAGE? (0 RETURNED AT END) RETURN ;Y. ALL DONE. CALL LCD_CHR ;N. DISPLAY CHARACTER. MOVF TEMP,W ; POINT TO NEXT CHARACTER. ADDLW 1 GOTO LCD_TEXT ;----------------------------------------------------------------------------------------- LCD_BYTE: BCF RW ;SET LCD FOR WRITE FROM PIC. BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00000001' MOVWF PORTB ;SET UPPER 7 BITS AS OUTPUTS. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. BSF E ;SET THE LCD CLOCK. MOVLW B'11110000' ANDWF TEMP_H,F ;STRIP LOW NIBBLE FROM THE BYTE TO SEND. MOVF PORTB,W ;GET CURRENT PORT SETINGS. ANDLW B'00001110' ;KEEP THE LOW NIBBLE UNCHANGED. IORWF TEMP_H,W ;COMBINE THE TWO NIBBLES. MOVWF PORTB ;SEND NIBBLE TO LCD. BCF E ;CLOCK THE NIBLE INTO THE LCD. BSF E ;SET THE LCD CLOCK. SWAPF TEMP_L,F ;SWAP THE UPPER AND LOWER NIBBLES. MOVLW B'11110000' ANDWF TEMP_L,F ;STRIP LOW NIBBLE FROM THE BYTE TO SEND. MOVF PORTB,W ;GET CURRENT PORT SETINGS. ANDLW B'00001110' ;KEEP THE LOW NIBBLE UNCHANGED. IORWF TEMP_L,W ;COMBINE THE TWO NIBBLES. MOVWF PORTB ;SEND NIBBLE TO LCD. BCF E ;CLOCK THE NIBLE INTO THE LCD. IF LCD_USES_RW == FALSE ;ASSEMBLE IF USING LCD WITH NO R/W LEAD. MOVLW 75 ;WAIT 250uS. LCD EXECUTION TIME = 120uS. CALL DELAY ;(3.3uS x 75) ENDIF RETURN ;----------------------------------------------------------------------------------------- IF LCD_USES_RW ;ASSEMBLE IF USING STANDARD LCD. LCD_BUSY_? MOVWF TEMP_H ;SAVE THE BYTE TO SEND TO THE LCD. MOVWF TEMP_L BCF RS ;SELECT INSTRUCTION REGISTER. BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'11110001' ;SET HI 4 BITS AS INPUT. MOVWF PORTB BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. BSF RW ;SET LCD FOR READ FROM PIC. BUSY_LOOP: BSF E ;CLOCK THE LCD. MOVF PORTB,W ;READ THE HIGH NIBBLE. ANDLW B'10000000' ;ISOLATE THE BUSY BIT. BCF E ;CLOCK THE LCD. BSF E ;CLOCK THE LCD. ;IGNORE THE LOWER NIBBLE. BCF E ;CLOCK THE LCD. BTFSS ZERO GOTO BUSY_LOOP ;LOOP UNTIL BUSY BIT IS CLEAR. RETURN ENDIF ;----------------------------------------------------------------------------------------- ; INITIALISE THE LCD IN 4 BIT MODE. ;----------------------------------------------------------------------------------------- INIT_LCD: CLRF PORTB ;SET RS,RW,E & SDA LOW. CALL LCD_RESET ;RESET THE LCD. CALL LCD_RESET ;RESET THE LCD. CALL LCD_RESET ;RESET THE LCD. MOVLW B'00101000' ;LCD 4 BIT MODE COMMAND. MOVWF PORTB ;ALSO E HIGH. BCF E CALL LONG_DELAY MOVLW B'00101000' ;SET LCD TO 2 LINE, 5x7 DOT. CALL LCD_CMD MOVLW B'00001000' ;TURN DISPLAY OFF. CALL LCD_CMD CALL LCD_CLEAR ;CLEAR DISPLAY. MOVLW B'00000110' ;CURSOR INCREMENTS, NO DISPLAY SHIFT. CALL LCD_CMD MOVLW B'00001100' ;TURN LCD ON. GOTO LCD_CMD ;RETURN VIA LCD_CMD. ;----------------------------------------------------------------------------------------- LCD_RESET: CALL LONG_DELAY MOVLW B'00111000' ;LCD RESET COMMAND. MOVWF PORTB ;ALSO E HIGH. BCF E RETURN ;***************************************************************************************** ; I2C ROUTINES. SET_PLL GIVEN THE DIVIDER NUMBER IN ARG, CHECKS ; THAT THE NUMBER IS VALID THEN SENDS IT ; TO THE PLL. ; GET_PLL_STATUS GET THE STATUS BYTE FROM THE PLL, ; AND SETS THE TX_EN LEAD. ; I2C_START SEND AN I2C START SIGNAL ON THE BUS. ; I2C_TX_BYTE SEND THE BYTE IN W TO THE BUS. ; I2C_STOP SEND AN I2C STOP SIGNAL ON THE BUS. ;***************************************************************************************** SET_PLL: BCF MAX_MIN_MOD ;CLEAR THE ARG CHANGED FLAG. BSF PLL_VALID ;SET THE VALID FLAG. CALL TRIM_NEG ;IF DIVIDER NUMBER IS NEGATIVE, MAKE IT 0. MOVLW PLL_DIV_MIN_H MOVWF ARG2_H ;MAKE SURE THE DIVIDER NUMBER IS NOT MOVLW PLL_DIV_MIN_L ;BELOW 50MHz. MOVWF ARG2_L CALL MAXIMUM MOVLW PLL_DIV_MAX_H MOVWF ARG2_H ;MAKE SURE THE DIVIDER NUMBER IS NOT MOVLW PLL_DIV_MAX_L ;ABOVE 1400MHz. MOVWF ARG2_L CALL MINIMUM MOVLW PLL_OLD ;GET THE OLD PLL DIVIDER NUMBER. CALL MOVE_TO_ARG2 CALL CMP ;SAME AS THE OLD DIVIDER NUMBER? BTFSC ZERO GOTO DONT_SEND ;Y. NO NEED TO RE-SEND THE NUMBER. MOVLW PLL_OLD ;N. SAVE THE NEW DIVIDER NUMBER. CALL COPY_FROM_ARG ; TO PLL_OLD. CALL I2C_START MOVLW PLL_ADDRESS ; SEND THE PLL ADDRESS. CALL I2C_TX_BYTE MOVF ARG_H,W ; SEND MSD BYTE OF THE PLL DIVIDER. CALL I2C_TX_BYTE MOVF ARG_L,W ; SEND LSB BYTE OF THE PLL DIVIDER. CALL I2C_TX_BYTE MOVLW B'10001110' ; SET CHARGE PUMP CURRENT TO 50uA, CALL I2C_TX_BYTE ; P6=REF, P7=F/DIV. MOVLW B'00000000' ; OUTPUT PINS ACTIVE. CALL I2C_TX_BYTE CALL I2C_STOP ; SEND THE STOP COMMAND. BCF PLL_STATUS,6 ; THE PLL IS NOT LOCKED NOW. DONT_SEND: BTFSC MAX_MIN_MOD ;DID MIN OR MAX CHANGE THE DIVIDER NUM? BCF PLL_VALID ;Y. CLEAR THE VALID FLAG. (INVALID) RETURN ;N. THE DIVIDER NUMBER WAS VALID. ;----------------------------------------------------------------------------------------- GET_PLL_STATUS: BTFSS PLL_STATUS,6 ;WAS PLL LOCKED WHEN LAST CHECKED? GOTO GET_STATUS ;N. GET THE CURRENT STATUS. MOVF LOCK_TIMER,W ;GET THE CURRENT PLL LOCKED TIMER. BTFSS ZERO ;HAS THE COUNTER REACHED 0 ? GOTO UPDATE_TX_EN ;N. JUST UPDATE THE TX ENABLE PIN. CALL RS_LOCKED_TMR ;Y. RESET THE LOCKED TIMER. GET_STATUS: CALL I2C_START ;SEND AN I2C START SIGNAL. MOVLW PLL_ADDRESS + 1 ;I2C ADDRESS & READ BIT. CALL I2C_TX_BYTE ;SEND THE ADDRESS BYTE TO THE PLL. MOVLW 8 MOVWF COUNT ;SET THE NUMBER OF BITS TO RECEIVE. RX_BIT_LOOP: CALL I2C_DELAY BSF SCL CALL I2C_DELAY RRF PORTB,W ;MOVE THE SDA (BIT 0) INTO THE CARRY BIT. RLF PLL_STATUS,F ;SAVE THE REVEIVED BIT. BCF SCL DECFSZ COUNT,F GOTO RX_BIT_LOOP ;LOOP UNTIL 8 BITS RECEIVED. CALL LET_SDA_HIGH ;SEND AN I2C NEVATIVE ACKNOWLEDGE. CALL I2C_CLOCK ;(SDA HIGH) CALL I2C_STOP ;SEND AN I2C STOP SIGNAL. BTFSC PLL_STATUS,6 ;IS PLL LOCKED NOW? CALL RS_LOCKED_TMR ;Y. RESET THE LOCKED TIMER. UPDATE_TX_EN: BTFSS PTT ;IF PTT IS HIGH, DONT ENABLE TX_EN LEAD. BTFSS PLL_STATUS,6 ;IS THE PLL LOCKED? GOTO NO_TX_EN BTFSS PLL_VALID ;Y. IS THE PLL DIVIDER SETING VALID? GOTO NO_TX_EN BCF TX_EN ; Y. ENABLE THE TX_EN LEAD. RETURN NO_TX_EN: BSF TX_EN ;N. DONT ENABLE THE TX_EN LEAD. RETURN RS_LOCKED_TMR: MOVLW LOCK_TM ;RESET THE PLL LOCKED TIMER. MOVWF LOCK_TIMER RETURN ;----------------------------------------------------------------------------------------- I2C_TX_BYTE: MOVWF TEMP ;SAVE THE BYTE TO TX IN TEMP. MOVLW 8 ;SET THE NUMBER OF BITS TO TRANSMIT. MOVWF COUNT TX_BIT_LOOP: RLF TEMP,F ;MOVE THE MSB INTO THE CARRY. BTFSC CARRY CALL LET_SDA_HIGH ;IF CARRY IS SET, SET THE SDA. BTFSS CARRY CALL PULL_SDA_LOW ;IF CARRY IS CLEAR, CLEAR THE SDA. CALL I2C_CLOCK ;LATCH THE DATA INTO THE PLL. DECFSZ COUNT,F GOTO TX_BIT_LOOP ;LOOP UNTIL ALL BITS ARE SENT. CALL LET_SDA_HIGH ;LET THE SDA FLOAT HIGH. CALL I2C_CLOCK ;SEND AN I2C ACKNOWLEGE SIGNAL. RETURN ;----------------------------------------------------------------------------------------- I2C_START: BSF SCL ;SEND AN I2C START SIGNAL. CALL LET_SDA_HIGH CALL I2C_DELAY CALL PULL_SDA_LOW GOTO I2C_CLOCK2 ;----------------------------------------------------------------------------------------- I2C_STOP: CALL PULL_SDA_LOW ;SEND AN I2C STOP SIGNAL. CALL I2C_DELAY BSF SCL CALL I2C_DELAY CALL LET_SDA_HIGH CALL I2C_DELAY RETURN ;----------------------------------------------------------------------------------------- I2C_CLOCK: CALL I2C_DELAY BSF SCL ;PULSE THE SCL LINE I2C_CLOCK2: CALL I2C_DELAY BCF SCL ; " RETURN ;----------------------------------------------------------------------------------------- PULL_SDA_LOW: BCF SDA ;SDA WILL BE LOW WHEN SET AS O/P. BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BCF SDA ;SET SDA AS AN OUTPUT. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. RETURN ;----------------------------------------------------------------------------------------- LET_SDA_HIGH: BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BSF SDA ;SET SDA AS AN INPUT. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. RETURN ;----------------------------------------------------------------------------------------- I2C_DELAY MOVLW 15 DELAY: MOVWF COUNT1 ; 15 LOOPS (3CLOCKS) = 50 uS. I2C_LOOP: DECFSZ COUNT1,F GOTO I2C_LOOP RETURN ;***************************************************************************************** ; BIN_TO_DEC CONVERT A 24 BIT BINARY NUMBER TO A 8 DIGIT ASCII DECIMAL NUMBER. ; ; FIRST THE 24 BIT BINARY NUMBER IS CONVERTED TO 8 DIGIT BCD, TWO DIGITS PER BYTE IN ; EACH OF DEC_1, DEC_3, DEC_5 AND DEC_7. ; THE BCD IS THEN UNPACKED TO ONE DIGIT PER BYTE IN DEC_0 TO DEC_7. ; IT IS THEN CONVERTED TO ASCII, INCLUDING NEGATIVE SIGN, DECIMAL POINT AND LEADING ; ZERO BLANKING, READY FOR DISPLAY ON THE LCD. ; ; THE 24 BIT BINARY NUMBER IS INPUT IN LOCATIONS RESULT_H, RESULT_M AND ; RESULT_L WITH THE HIGH BYTE IN RESULT_H. ; THE 8 DIGIT DECIMAL NUMBER IS RETURNED IN DEC_0 - DEC_7 WITH DEC_0 ; CONTAINING THE MSD. ;***************************************************************************************** BIN_TO_DEC: BCF CARRY ;CLEAR THE CARRY BIT. MOVLW 24 MOVWF COUNT ;24 BITS TO PROCESS. CLRF DEC_1 ;BCD MSD DIGITS. CLRF DEC_3 CLRF DEC_5 CLRF DEC_7 ;BCD LSD DIGITS. LOOP24: RLF RESULT_L, F ;SHIFT 24 BIT NUMBER LEFT BY ONE BIT RLF RESULT_M, F ;INTO BCD. RLF RESULT_H, F RLF DEC_7, F RLF DEC_5, F RLF DEC_3, F RLF DEC_1, F DECFSZ COUNT, F GOTO ADJ_DEC ;ADJUST BCD DIGITS AND LOOP TO LOOP24. ;----------------------------------------------------------------------------------------- SWAPF DEC_7,W ;BINARY TO BCD CONVERSION DONE. MOVWF DEC_6 ;UNPACK THE BCD TO ONE CHR PER BYTE. SWAPF DEC_5,W MOVWF DEC_4 SWAPF DEC_3,W MOVWF DEC_2 SWAPF DEC_1,W MOVWF DEC_0 BCF LEADING_FLAG ;BLANK LEADING ZERO,S UNTIL THE FLAG IS MOVLW DEC_0 ;SET BY A NON ZERO NUMBER. CALL ADJ_ASCII MOVLW DEC_1 CALL ADJ_ASCII MOVLW DEC_2 CALL ADJ_ASCII MOVLW DEC_3 CALL ADJ_ASCII MOVLW DEC_4 CALL ADJ_ASCII BSF LEADING_FLAG ;ALWAYS DISPLAY CHR BEFORE DECIMAL POINT. MOVLW DEC_5 CALL ADJ_ASCII MOVLW DP MOVWF DECIMAL_POINT ;INSERT A DECIMAL POINT. MOVLW DEC_6 CALL ADJ_ASCII MOVLW DEC_7 CALL ADJ_ASCII BTFSS NEGATIVE ;WAS THIS A NEGATIVE NUMBER? RETURN ;N. JUST RETURN. MOVF NEG_POSITION,W ;Y. GET POSITION OF LAST BLANK. MOVWF FSR MOVLW '-' ; PLACE A - SIGN IN FRONT OF THE NUMBER. MOVWF INDF RETURN ;----------------------------------------------------------------------------------------- ADJ_DEC: MOVLW DEC_7 ;CALL ADJ_BCD TO ADJUST EACH OF CALL ADJ_BCD ;THE 4 BCD DIGITS. MOVLW DEC_5 CALL ADJ_BCD MOVLW DEC_3 CALL ADJ_BCD MOVLW DEC_1 CALL ADJ_BCD GOTO LOOP24 ;----------------------------------------------------------------------------------------- ADJ_BCD: MOVWF FSR ;MAKE SURE THE BCD DIGITS ARE VALID. MOVLW 3 ;CHECK LOW NIBBLE (LSD). ADDWF INDF,W ;IF LSD + 3 > 7 THEN LSD = LSD + 3. MOVWF TEMP ;ADD 3 TO LSD. BTFSC TEMP,3 ;IS RESULT > 7 ? MOVWF INDF ;Y. LSD = LSD + 3. MOVLW H'30' ;CHECK HIGH NIBBLE (MSD). ADDWF INDF,W ;IF MSD + 3 > 7 THEN MSD = MSD + 3. MOVWF TEMP ;ADD 3 TO MSD. BTFSC TEMP,7 ;IS RESULT > 7 ? MOVWF INDF ;Y. MSD = MSD + 3. RETURN ;----------------------------------------------------------------------------------------- ADJ_ASCII: MOVWF FSR MOVLW B'00001111' ;CLEAR THE HIGH NIBBLE. ANDWF INDF,W BTFSC ZERO GOTO BLANK NO_BLANK ADDLW H'30' ;CONVERT TO ASCII. MOVWF INDF BSF LEADING_FLAG RETURN BLANK: BTFSC LEADING_FLAG ;IF FLAG IS SET, DONT BLANK ZERO'S. GOTO NO_BLANK MOVLW ' ' ;IF FLAG IS NOT SET, BLANK ZERO'S. MOVWF INDF MOVF FSR,W ;SAVE THE LOCATION OF THE LAST BLANK MOVWF NEG_POSITION ;SO THAT A NEGATIVE SIGN CAN BE INSERTED. RETURN ;***************************************************************************************** ; ; MULTIPLY 16x8 BIT UNSIGNED FIXED POINT MULTIPLY 16x8 -> 24 ; ; Input: 16 BIT UNSIGNED FIXED POINT MULTIPLICAND IN ARG_H, ARG_L ; 8 BIT UNSIGNED FIXED POINT MULTIPLIER IN ARG ; ; Output: 24 BIT UNSIGNED FIXED POINT PRODUCT IN RESULT_H-M-L ; ;***************************************************************************************** MULTIPLY: CLRF RESULT_L ; CLEAR PARTIAL PRODUCT. MOVF ARG_H,W MOVWF RESULT_H MOVF ARG_L,W MOVWF RESULT_M MOVLW 8 MOVWF COUNT ;8 BITS TO PROCESS. MULT_LOOP1: RRF ARG, F ;SHIFT THE 8 BIT ARG RIGHT. LSD TO CARRY. BTFSC CARRY ;IS THE CARRY BIT = 1? GOTO MULT_1 DECFSZ COUNT, F GOTO MULT_LOOP1 CLRF RESULT_H CLRF RESULT_M RETURN MULT_1: BCF CARRY GOTO MULT_2 MULT_LOOP2: RRF ARG, F BTFSS CARRY GOTO MULT_2 MOVF ARG_L,W ;Y. ADD MULTIPLICAND TO HIGH ORDER ADDWF RESULT_M, F ; PARTIAL PRODUCT. MOVF ARG_H,W BTFSC CARRY INCFSZ ARG_H,W ADDWF RESULT_H, F MULT_2: RRF RESULT_H, F ;RIGHT SHIFT PARTIAL PRODUCT. RRF RESULT_M, F RRF RESULT_L, F DECFSZ COUNT, F GOTO MULT_LOOP2 RETURN ;***************************************************************************************** ; ; ADD 16 BIT ADDITION. ARG + ARG2 = ARG (ARG 2 NOT CHANGED) ; ; INPUT: 16 BIT ARGMENT 1 IN ARG_H, ARG_L. ; 16 BIT ARGMENT 2 IN ARG2_H, ARG2_L. ; ; OUTPUT: 16 BIT RESULT IN ARG_H, ARG_L. ; CARRY FLAG SET IF RESULT OVERFLOWED. ; ;***************************************************************************************** ADD: MOVF ARG2_L,W ADDWF ARG_L,F ;ADD THE LOW BYTES. MOVF ARG2_H,W BTFSC CARRY ;WAS THERE WAS A CARRY FROM THE LOW BYTE? INCFSZ ARG2_H,W ;Y. INC THE HIGH BYTE. ADDWF ARG_H,F ;ADD THE HIGH BYTES. RETURN ;***************************************************************************************** ; ; SUB 16 BIT SUBTRACTION. ARG - ARG2 = ARG (ARG 2 NOT CHANGED) ; ; INPUT: 16 BIT ARGMENT 1 IN ARG_H, ARG_L. ; 16 BIT ARGMENT 2 IN ARG2_H, ARG2_L. ; ; OUTPUT: 16 BIT RESULT IN ARG_H, ARG_L. ; (CARRY FLAG CLEAR (BORROW) IF RESULT WAS NEGATIVE.) ; ;***************************************************************************************** SUB: MOVF ARG2_L,W SUBWF ARG_L,F ;SUB THE LOW BYTES. MOVF ARG2_H,W BTFSS CARRY ;WAS THERE WAS A BORROW FROM THE LOW BYTE? INCFSZ ARG2_H,W ;Y. INC THE HIGH BYTE OF ARG 2. SUBWF ARG_H,F ;SUB THE HIGH BYTES. RETURN ;***************************************************************************************** ; ; CMP 16 BIT COMPARE. ARG - ARG2 (ARG & ARG 2 NOT CHANGED) ; ; INPUT: 16 BIT ARGMENT 1 IN ARG_H, ARG_L. ; 16 BIT ARGMENT 2 IN ARG2_H, ARG2_L. ; ; OUTPUT: CARRY FLAG CLEAR (BORROW) IF RESULT WAS NEGATIVE. ; ZERO FLAG SET IF RESULT IS ZERO. ; ;***************************************************************************************** CMP: MOVF ARG2_L,W SUBWF ARG_L,W ;SUB THE LOW BYTES. MOVWF TEMP ;SAVE THE LOW BYTE FOR ZERO CHECK. MOVF ARG2_H,W BTFSS CARRY ;WAS THERE WAS A BORROW FROM THE LOW BYTE? INCF ARG2_H,W ;Y. INC THE HIGH BYTE OF ARG 2. SUBWF ARG_H,W ;SUB THE HIGH BYTES. IORWF TEMP,W ;CHECK FOR ZERO RESULT. RETURN ;***************************************************************************************** ; ; MINIMUM 16 BIT UNSIGNED MIN. RETURN THE SMALLEST OF TWO ARGMENTS. ; ; INPUT: 16 BIT ARGMENT 1 IN ARG_H, ARG_L. ; 16 BIT ARGMENT 2 IN ARG2_H, ARG2_L. ; ; OUTPUT: THE SMALLEST ARGMENT IS RETURNED IN ARG_H, ARG_L. ; ;***************************************************************************************** MINIMUM: MOVF ARG2_H,W ;SAVE ARG 2. MOVWF ARG3_H MOVF ARG2_L,W MOVWF ARG3_L MOVLW 1 ;INC THE ARG2 BY 1. ADDWF ARG2_L,F BTFSC CARRY INCF ARG2_H,F CALL CMP BTFSS CARRY ;IS ARG 1 > ARG 2 ? RETURN ;N. JUST RETURN. MOVLW ARG3_H ;Y. SET ARG 1 THE SAME AS ARG 2. GOTO RETURN_ARG ; INDICATE THAT ARG HAS BEEN CHANGED. ;***************************************************************************************** ; ; MAXIMUM 16 BIT UNSIGNED MAX. RETURN THE LARGEST OF TWO ARGMENTS. ; ; INPUT: 16 BIT ARGMENT 1 IN ARG_H, ARG_L. ; 16 BIT ARGMENT 2 IN ARG2_H, ARG2_L. ; ; OUTPUT: THE LARGEST ARGMENT IS RETURNED IN ARG_H, ARG_L. ; ;***************************************************************************************** MAXIMUM: CALL CMP BTFSC CARRY ;IS ARG 1 > ARG 2 ? RETURN ;Y. NO CHANGE REQUIRED. MOVLW ARG2_H ;N. SET ARG 1 THE SAME AS ARG 2. RETURN_ARG: CALL MOVE_TO_ARG BSF MAX_MIN_MOD ; INDICATE THAT ARG HAS BEEN CHANGED. RETURN ;***************************************************************************************** ; MISC ROUTINES. MOVE_TO_ARG MOVE THE DOUBLE BYTE POINTED TO BY INDF ; REGISTER TO THE DOUBLE BYTE ARG VARIABLE. ; MOVE_TO_ARG2 MOVE THE DOUBLE BYTE POINTED TO BY INDF ; REGISTER TO THE DOUBLE BYTE ARG2 VARIABLE. ; COPY_FROM_ARG MOVE THE DOUBLE BYTE ARG VARIABLE TO THE ; ADDRESS POINTED TO BY INDF REGISTER. ; LONG_DELAY WAIT 100mS. ;***************************************************************************************** MOVE_TO_ARG: MOVWF FSR MOVF INDF,W MOVWF ARG_H INCF FSR,F MOVF INDF,W MOVWF ARG_L RETURN ;----------------------------------------------------------------------------------------- COPY_FROM_ARG: MOVWF FSR MOVF ARG_H,W MOVWF INDF INCF FSR,F MOVF ARG_L,W MOVWF INDF RETURN ;----------------------------------------------------------------------------------------- MOVE_TO_ARG2: MOVWF FSR MOVF INDF,W MOVWF ARG2_H INCF FSR,F MOVF INDF,W MOVWF ARG2_L RETURN ;----------------------------------------------------------------------------------------- LONG_DELAY: MOVLW 118 DELAY2: MOVWF COUNT1 ; 118 LOOPS OF 845 uS = 100 mS. CLRF COUNT2 ; 256 LOOPS (3CLOCKS) x 1.1uS = 845 uS. LD_LOOP: DECFSZ COUNT2,F GOTO LD_LOOP DECFSZ COUNT1,F GOTO LD_LOOP RETURN ;***************************************************************************************** ; UP_DOWN INC OR DEC THE VARIABLE IN ARG. (ARG_H, ARG_L) ; ; BY 1 WITH A QUICK PRESS OF FREQ + OR -. (SHIFT NOT PRESSED) ; BY MULTIPLE 2'S WITH A LONG PRESS OF FREQ + OR -. (SHIFT NOT PRESSED) ; BY 20 WITH A QUICK PRESS OF FREQ + OR -. (SHIFT PRESSED) ; BY MULTIPLE 200'S WITH A LONG PRESS OF FREQ + OR -. (SHIFT PRESSED) ;***************************************************************************************** UP_DOWN: CLRF ARG2_H MOVLW 1 ;PREPARE TO INC/DEC BY 1. (NO SHIFT) BTFSS BUTTON_S ;IS THE SHIFT BUTTON PRESSED? MOVLW 20 ;Y. PREPARE TO INC/DEC BY 20. MOVWF ARG2_L ; (SHIFT PRESSED) CALL CHK_P_BRIEF BTFSC PRESSED ;IF FREQ + PRESSED BRIEFLY CALL ADD ;INC BY 1 OR 20. CALL CHK_N_BRIEF BTFSC PRESSED ;IF FREQ - PRESSED BRIEFLY CALL SUB ;DEC BY 1 OR 20. MOVLW 2 ;PREPARE TO INC/DEC BY 2. (NO SHIFT) BTFSS BUTTON_S ;IS THE SHIFT BUTTON PRESSED? MOVLW 200 ;Y. PREPARE TO INC/DEC BY 200. MOVWF ARG2_L ; (SHIFT PRESSED) CALL CHK_P_MULT BTFSC PRESSED ;IF FREQ + PRESSED FOR A LONG TIME CALL ADD ;MULTIPLE INC BY 2 OR 200. CALL CHK_N_MULT BTFSC PRESSED ;IF FREQ - PRESSED FOR A LONG TIME CALL SUB ;MULTIPLE DEC BY 2 OR 200. TRIM_NEG: COMF ARG_H,W ;ANY NUMBER BETWEEN 49151 AND 65535. ANDLW B'11000000' ;(C000 - FFFF) BTFSS ZERO ;ARE INVALID SO SET ARG TO 0. RETURN CLRF ARG_H CLRF ARG_L RETURN ;***************************************************************************************** ; EEPROM ROUTINES. GET_SETUP MOVE THE 17 BYTES OF SETUP INFO ; FROM EEPROM TO RAM. ; UPDATE_EEPROM IF THE EEPROM WRITE UPDATE FLAG IS SET, ; PROGRAM ANY SETUP BYTES THAT HAVE CHANGED. ; READ_EEPROM READ A BYTE FROM EEPROM. ASSUMES THE ; EEPROM ADDRESS HAS BEEN PLACED IN EEADR. ; WRITE_EEPROM WRITE THE BYTE IN W TO EEPROM. ASSUMES THE ; EEPROM ADDRESS HAS BEEN PLACED IN EEADR. ;***************************************************************************************** GET_SETUP: CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. MOVLW MULT_NUM ;GET BASE ADDRESS OF RAM BUFFER. MOVWF FSR ;PLACE IT IN THE INDIRECT REGISTER. MOVLW 17 MOVWF COUNT ;17 BYTES TO READ. R_EEPROM_LOOP: CALL READ_EEPROM ;GET A BYTE FROM EEPROM. MOVWF INDF ;PLACE IT IN RAM. INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. INCF FSR,F DECFSZ COUNT,F GOTO R_EEPROM_LOOP ;LOOP UNTIL ALL BYTES READ. RETURN ;----------------------------------------------------------------------------------------- UPDATE_EEPROM: BTFSS EEPROM_UPDATE ;IS THE UPDATE REQUIRED FLAG SET? RETURN ;N. ALL DONE. BCF EEPROM_UPDATE ;CLEAR THE FLAG. CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. MOVLW MULT_NUM ;GET BASE ADDRESS OF RAM BUFFER. MOVWF FSR ;PLACE IT IN THE INDIRECT REGISTER. MOVLW 17 MOVWF COUNT ;17 BYTES TO PROGRAM. W_EEPROM_LOOP: CALL READ_EEPROM ;GET A BYTE FROM EEPROM. SUBWF INDF,W BTFSC ZERO ;IS IT THE SAME AS IT IS IN RAM. GOTO NO_CHANGE ;Y. NO NEED TO UPDATE THIS BYTE. MOVF INDF,W ;N. COPY THE BYTE FROM RAM TO EEPROM. CALL WRITE_EEPROM NO_CHANGE: INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. INCF FSR,F DECFSZ COUNT,F GOTO W_EEPROM_LOOP ;LOOP UNTIL ALL BYTES UPDATED. RETURN ;----------------------------------------------------------------------------------------- READ_EEPROM: BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BSF RD ;PERFORM AN EEPROM READ. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. MOVF EEDATA,W ;GET THE READ BYTE. RETURN ;----------------------------------------------------------------------------------------- WRITE_EEPROM: MOVWF EEDATA ;PUT DATA TO BE WRITEN IN THE BUFFER. BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BCF EEIF ;CLEAR THE EEPROM WRITE DONE FLAG. BSF WREN ;ENABLE EEPROM WRITE. MOVLW H'55' ;PERFORM REQUIRED SAFETY STEPS. MOVWF EECON2 MOVLW H'AA' MOVWF EECON2 BSF WR ;BEGIN THE WRITE. WRITE_LOOP: BTFSS EEIF ;LOOP UNTIL THE WRITE IS COMPLETED. GOTO WRITE_LOOP BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. RETURN ;***************************************************************************************** ; BUTTON ROUTINES. CHK_S_BRIEF RETURN A TRUE FLAG IN RESULT, IF THE ; CHK_P_BRIEF BUTTON WAS PRESSED BRIEFLY. ; CHK_N_BRIEF (SHIFT, + FREQ OR - FREQ) ; ; CHK_P_MULT RETURN MULTIPLE TRUE FLAGS IN RESULT, IF ; CHK_N_MULT THE BUTTON HAS BEEN PRESSED A WHILE. ; ; CHK_S_DOUBLE RETURN A TRUE FLAG IN RESULT, IF THE ; BUTTON HAS BEEN PRESSED TWICE BRIEFLY. ;***************************************************************************************** CHK_S_BRIEF: MOVLW MAX_ON_S ;GET THE UPPER TIME LIMIT FOR USE MOVWF TEMP ;BY CHK_BRIEF. MOVF BUT_S_OLD,W ;GET THE OLD BUTTON STATE. MOVWF RESULT ;SAVE THE OLD BUTTON STATE. SUBWF BUT_S_CNT,W ;COMPARE CURRENT AND SAVED BUTTON STATE. CALL CHK_BRIEF ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX. MOVF BUT_S_CNT,W MOVWF BUT_S_OLD ;SAVE THE CURRENT BUTTON STATE. RETURN ;----------------------------------------------------------------------------------------- CHK_P_MULT: MOVLW MAX_ON + 10 + REPEAT ;HAS THE BUTTON BEEN PRESSED A LONG WHILE? SUBWF BUT_P_CNT,W BTFSS CARRY GOTO CHK_P_BRIEF MOVLW MAX_ON + 10 ;Y. FOOL CHK_BRIEF INTO THINKING THAT MOVWF BUT_P_CNT ; THE BUTTON HAS BEEN RELEASED. MOVLW MAX_ON + 12 + REPEAT ; GET A LARGER UPPER TIME LIMIT FOR USE GOTO CHK_P_BRIEF_1 ; BY CHK_BRIEF. CHK_P_BRIEF: MOVLW MAX_ON ;N. USE THE NORMAL UPPER LIMIT. CHK_P_BRIEF_1: MOVWF TEMP MOVF BUT_P_OLD,W ;GET THE OLD BUTTON STATE. MOVWF RESULT ;SAVE THE OLD BUTTON STATE. SUBWF BUT_P_CNT,W ;COMPARE CURRENT AND SAVED BUTTON STATE. CALL CHK_BRIEF ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX. MOVF BUT_P_CNT,W MOVWF BUT_P_OLD ;SAVE THE CURRENT BUTTON STATE. RETURN ;----------------------------------------------------------------------------------------- CHK_N_MULT: MOVLW MAX_ON + 10 + REPEAT ;HAS THE BUTTON BEEN PRESSED A LONG WHILE? SUBWF BUT_N_CNT,W BTFSS CARRY GOTO CHK_N_BRIEF ;N. USE CHK_N_BRIEF TO RETURN THE FLAGS. MOVLW MAX_ON + 10 ;Y. FOOL CHK_BRIEF INTO THINKING THAT MOVWF BUT_N_CNT ; THE BUTTON HAS BEEN RELEASED. MOVLW MAX_ON + 12 + REPEAT ; GET A LARGER UPPER TIME LIMIT FOR USE GOTO CKH_N_BRIEF_1 ; BY CHK_BRIEF. CHK_N_BRIEF: MOVLW MAX_ON ;N. USE THE NORMAL UPPER LIMIT. CKH_N_BRIEF_1: MOVWF TEMP MOVF BUT_N_OLD,W ;GET THE OLD BUTTON STATE. MOVWF RESULT ;SAVE THE OLD BUTTON STATE. SUBWF BUT_N_CNT,W ;COMPARE CURRENT AND SAVED BUTTON STATE. CALL CHK_BRIEF ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX. MOVF BUT_N_CNT,W MOVWF BUT_N_OLD ;SAVE THE CURRENT BUTTON STATE. RETURN ;----------------------------------------------------------------------------------------- CHK_BRIEF: BTFSC CARRY ;HAS THE BUTTON BEEN RELEASED? GOTO FALSE_FLAG ;N. RETURN A FALSE FLAG. MOVF RESULT,W ;Y. GET THE OLD BUTTON STATE. SUBLW MIN_ON ; WAS IT ON LESS THAN THE MIN PERIOD? BTFSC CARRY ; (OLD-MIN=NEGATIVE, CARRY = 0) GOTO FALSE_FLAG ; Y. RETURN A FALSE FLAG. MOVF RESULT,W ;GET THE OLD BUTTON STATE. SUBWF TEMP,W ; WAS IT ON LONGER THAN THE MAX PERIOD? RETURN_FLAG: MOVLW H'FF' ;Y. PREPARE A TRUE FLAG. BTFSS CARRY FALSE_FLAG: CLRW ;N. PREPARE A FALSE FLAG. MOVWF RESULT ;RETURN THE FLAG. RETURN ;----------------------------------------------------------------------------------------- CHK_S_DOUBLE: CALL CHK_S_BRIEF BTFSS PRESSED ;WAS THE SHIFT PRESSED BRIEFLY? RETURN ;N. RETURN THE FALSE FLAG. MOVF S_TIMER,W BTFSC ZERO ;Y. WAS BUTTON PRESSED A SHORT WHILE AGO?. GOTO FIRST_PRESS RETURN ; Y. RETURN THE TRUE FLAG. FIRST_PRESS: MOVLW DOUBLE_TIME ; N. THIS IS THE FIRST PRESS. MOVWF S_TIMER ; SET THE S_TIMER DOWN COUNTER. GOTO FALSE_FLAG ; RETURN A FALSE FLAG. ;----------------------------------------------------------------------------------------- END