;***************************************************************************************** ; ; 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. ; ; 1.00_16F88 14.August-2005 Ein Versuch ; ; ; Umgebaut von Tomtom DL1MFK ; Der PIC 16F88 wird nur mit dem internen RC-Oszillator betrieben. ; das PLL.DAT-File und das PLL88.ASM-File ist so angepasst, daß eine ; Schrittweite von 125kHz entsprechend dem SP5055 (mit 4MHz Quarz) eingehalten wird ; es ist (noch) nicht möglich mit dieser Programm-Version den Faktor auf 4 ; zu setzen und damit 10 GHz anzuzeigen. ; ; 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. ; ;***************************************************************************************** ; CONFIGURATION BITS _WDT_OFF EQU H'3FFB' _LVP_OFF EQU H'3F7F' _INTRC_OSC_NOCLKOUT EQU H'3FFC' #include "P16F88.INC" PROCESSOR 16F88 ;SELECT PROCCESOR, AND ASSEMBLER OPTIONS. LIST F=INHX8M,N=0 RADIX DEC __CONFIG _CONFIG1, _CP_OFF & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF ;INT OSCILLATOR (4MHz), NO WATCHDOG, NO LOW VOLTAGE PROGRAMMING ;***************************************************************************************** ; GENERAL DEFINITIONS ;***************************************************************************************** #DEFINE SCL PORTB,4 ;I2C CLOCK O/P LINE. #DEFINE SDA PORTB,1 ;I2C DATA O/P LINE. #DEFINE PTT PORTB,5 ;PTT INPUT PIN. 0 = TX FREQ, 1 = RX FREQ #DEFINE TX_EN PORTB,6 ;TX ENABLE OUTPUT PIN. 0 = ENABLED. #DEFINE RW PORTB,7 ;Bei Verwendung von RW-Pin am LCD #DEFINE BUTTON_P PORTB,2 ;FREQ + BUTTON. #DEFINE BUTTON_N PORTB,0 ;FREQ - BUTTON. #DEFINE BUTTON_S PORTB,3 ;SHIFT BUTTON. #DEFINE E PORTA,7 ;LCD Enable 0=aus 1=an #DEFINE RS PORTA,6 ;LCD Register Select 0=CMD 1=Daten #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 "." ;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 1000 ;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 = 125KHz MAX_OFFSET EQU 50000 ;PLL MAXIMUM TX OR RX OFFSET = 500MHz PLL_MIN_FREQ EQU 500000 ;PLL MINIMUM FREQUENCY = 50MHz PLL_MAX_FREQ EQU 3000000 ;PLL MAXIMUM FREQUENCY = 3000MHz 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 = 3000MHz. ;***************************************************************************************** ORG H'2100' INCLUDE "pll.dat" ;FILE TO SET THE INITIAL PLL & DISPLAY ; FREQ RANGE INTO EEPROM. ;***************************************************************************************** ;***************************************************************************************** ; RAM VARIABLES 68 BYTES MAX ;***************************************************************************************** CBLOCK H'20' ;RAM STARTS AT H'20' COUNT ;GENERAL COUNTER VARIABLES. COUNT1 ; " COUNT2 ; " SAVE_STATUS ;FOR INTERUPT SERVIVE ROUTINE. SAVE_W_REG SAVE_PCLATH 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 ; " DECIMAL_POINT ; " DECIMAL POINT. DEC_5 ; " DEC_6 ; " DEC_7 ; " LSD ENDC ;***************************************************************************************** ; ; INITIALISE THE HARDWARE. ; ;***************************************************************************************** ORG RESETVECTOR GOTO INIT ;***************************************************************************************** ; 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 INTCON,TMR0IF ;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 CLRF STATUS ;MAKE SURE WE ARE ADDRESSING BANK 0. MOVWF SAVE_STATUS MOVF PCLATH, W ;Only required if using page 1 MOVWF SAVE_PCLATH ;Save PCLATH into W CLRF PCLATH ;Page zero, regardless of current page ;----------------------------------------------------------------------------------------- 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 STATUS,Z ;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 STATUS,Z ;IS IT = 0? DECF S_TIMER,F ;N. DEC THE COUNTER. ;----------------------------------------------------------------------------------------- INCF LOCK_TIMER,W ;GET THE LOCK TIME COUNTER. BTFSC STATUS,Z ;IS IT = 255? GOTO NO_LOCK_TM_DEC MOVF LOCK_TIMER,W ;N. GET THE LOCK TIME COUNTER. BTFSS STATUS,Z ; 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 STATUS,Z ;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: MOVF SAVE_PCLATH, W ;Restore PCLATH MOVWF PCLATH ;Move W into PCLATH SWAPF SAVE_STATUS,W MOVWF STATUS ;RESTORE STATUS REG. MOVF SAVE_W_REG,F ;SAVE WREG. MOVF SAVE_W_REG,W ;RESTORE WREG. BCF INTCON,TMR0IF ;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 "UN",0 LOCKED: EQU $ - ST DT "LOCKED",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) ; ;***************************************************************************************** INIT: BANKSEL OSCCON MOVLW B'01100000' ;Oszillator auf 4MHz Stellen MOVWF OSCCON BANKSEL CMCON MOVLW 7 MOVWF CMCON BANKSEL ANSEL CLRF ANSEL CALL LONG_DELAY ;WAIT UNTIL LCD AND PLL HAVE INITIALISED. MOVLW H'21' ;POINT TO START OF RAM. (JUST PAST COUNT) MOVWF FSR MOVLW 68 ;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. BCF STATUS,RP1 BSF STATUS,RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00110000' MOVWF PORTA ;RA0-RA3,RA6,RA7 = OUTPUTS, RA4,RA5 INPUT. MOVLW B'00101101' MOVWF PORTB ;RB2,RB4,RB6,RB7 = OUTPUT ;RB0,RB2,RB3,RB5 = INPUT. MOVLW B'00000100' ;SET PRESCALER TO TMR0. MOVWF OPTION_REG ;TMR0 RATE = 1/32 OF INSTRUCTION CLOCK. ;PULL-UPS ON BCF STATUS,RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. 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. CLRF TMR0 ;SETS 1ST INTERUPT TO 9mS. MOVLW B'10100000' ;ENABLE TMR0 OVERFLOW INTERUPT. MOVWF INTCON ;***************************************************************************************** ; MAIN DISPLAY ;***************************************************************************************** MAIN_DISP: CALL SET_TIMEOUT ;SET THE DISPLAY TIMEOUT DOWN COUNTER. BCF TIMEOUT ;CLEAR THE DISPLAY TIMEOUT FLAG. CALL LCD_L1_P2 ;POSITION THE CURSOR, LINE 1 POS 2. MOVLW 'X' CALL LCD_CHR ;DISPLAY 'X'. ;CALL LCD_3_SPACES ;CALL LCD_3_SPACES ;DISPLAY ' ' MOVLW H'84' CALL LCD_CMD ;POSITION THE CURSOR, LINE 1 POS 11. MOVLW LOCKED ;DISPLAY 'LOCKED'. CALL LCD_TEXT ;MOVLW H'C0' ;CALL LCD_CMD ;POSITION THE CURSOR, LINE 2 POS 1. ;CALL LCD_SPACE ;CALL DISP_MHZ ;DISPLAY ' MHz ' ON LINE 2 POS 11. 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. MOVLW H'80' CALL LCD_CMD ;GOTO THE START OF LINE 1. 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'82' CALL LCD_CMD ;POSITION THE CURSOR, LINE 1 POS 7. MOVLW UN ;PREPARE TO DISPLAY 'UN'. 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 STATUS,C ;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 STATUS,C ;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 STATUS,C ;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'CA' CALL LCD_CMD ;POSITION THE CURSOR, LINE 2 POS 11. MOVLW MHZ CALL LCD_TEXT ;DISPLAY ' MHz'. CALL LCD_2_SPACES 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 STATUS,Z ;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 STATUS,C INCF ARG_H,F DISP_FREQ: MOVF MULT_NUM,W ;GET THE DISPLAY MULTIPLIER NUMBER. MOVWF ARG BCF STATUS,C ;MULTIPLY THE MULTIPLIER BY 125 kHz RLF ARG,F ;Multiplikator für 2,5 GHz =1 !!! RLF ARG,F ADDWF ARG,F MOVF ARG,W MOVWF ARG BCF STATUS,C RLF ARG,F RLF ARG,F ADDWF ARG,F MOVF ARG,W MOVWF ARG BCF STATUS,C RLF ARG,F RLF ARG,F ADDWF ARG,F CALL MULTIPLY ;ARG x MULT_NUM. CALL BIN_TO_DEC ;CONVERT THE NUMBER TO ASCII DECIMAL. MOVLW H'C0' ;POSITION THE CURSOR, LINE 2 POS 2. CALL LCD_CMD MOVLW DEC_0 ;SET THE POINTER TO THE START MOVWF FSR ;OF THE DECIMAL BUFFER. MOVLW 9 ;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. 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 STATUS,Z ;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 INTCON,GIE BCF RW ;SET LCD FOR WRITE FROM PIC. BSF STATUS,RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00110000' MOVWF PORTA ;SET LOWER 4 BITS AS OUTPUTS. BCF STATUS,RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. BSF E ;SET THE LCD CLOCK. SWAPF TEMP_L,F ;SWAP THE UPPER AND LOWER NIBBLES. MOVLW B'00001111' ANDWF TEMP_L,F ;STRIP LOW NIBBLE FROM THE BYTE TO SEND. MOVF PORTA,W ;GET CURRENT PORT SETINGS. ANDLW B'11110000' ;KEEP THE LOW NIBBLE UNCHANGED. IORWF TEMP_L,W ;COMBINE THE TWO NIBBLES. MOVWF PORTA ;SEND NIBBLE TO LCD. BCF E ;CLOCK THE NIBLE INTO THE LCD. BSF E ;SET THE LCD CLOCK. MOVLW B'00001111' ANDWF TEMP_H,F ;STRIP LOW NIBBLE FROM THE BYTE TO SEND. MOVF PORTA,W ;GET CURRENT PORT SETINGS. ANDLW B'11110000' ;KEEP THE LOW NIBBLE UNCHANGED. IORWF TEMP_H,W ;COMBINE THE TWO NIBBLES. MOVWF PORTA ;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 bsf INTCON,GIE 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 STATUS,RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'11110011' ;SET HI 4 BITS AS INPUT. MOVWF PORTB BCF STATUS,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 STATUS,Z GOTO BUSY_LOOP ;LOOP UNTIL BUSY BIT IS CLEAR. RETURN ENDIF ;----------------------------------------------------------------------------------------- ; INITIALISE THE LCD IN 4 BIT MODE. ;----------------------------------------------------------------------------------------- INIT_LCD: CLRF PORTA ;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'01000010' ;LCD 4 BIT MODE COMMAND. MOVWF PORTA ;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'01000011' ;LCD RESET COMMAND. MOVWF PORTA ;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 STATUS,Z 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'10101110' ; SET CHARGE PUMP CURRENT TO 50uA, CALL I2C_TX_BYTE ; P6=REF, P7=F/DIV. MOVLW B'11111111' ; 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 STATUS,Z ;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. RRF PORTB,W ;MOVE THE SDA (BIT 1) 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 STATUS,C CALL LET_SDA_HIGH ;IF CARRY IS SET, SET THE SDA. BTFSS STATUS,C 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 STATUS,RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BCF SDA ;SET SDA AS AN OUTPUT. BCF STATUS,RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. RETURN ;----------------------------------------------------------------------------------------- LET_SDA_HIGH: BSF STATUS,RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. BSF SDA ;SET SDA AS AN INPUT. BCF STATUS,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 STATUS,C ;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 STATUS,Z 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 STATUS,C ;IS THE CARRY BIT = 1? GOTO MULT_1 DECFSZ COUNT, F GOTO MULT_LOOP1 CLRF RESULT_H CLRF RESULT_M RETURN MULT_1: BCF STATUS,C GOTO MULT_2 MULT_LOOP2: RRF ARG, F BTFSS STATUS,C GOTO MULT_2 MOVF ARG_L,W ;Y. ADD MULTIPLICAND TO HIGH ORDER ADDWF RESULT_M, F ; PARTIAL PRODUCT. MOVF ARG_H,W BTFSC STATUS,C 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 STATUS,C ;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 STATUS,C ;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 STATUS,C ;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 STATUS,C INCF ARG2_H,F CALL CMP BTFSS STATUS,C ;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 STATUS,C ;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 STATUS,Z ;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: banksel EEADR CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. MOVLW MULT_NUM ;GET BASE ADDRESS OF RAM BUFFER. BCF STATUS,RP0 BCF STATUS,RP1 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. banksel EEADR INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. BCF STATUS,RP0 BCF STATUS,RP1 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. BANKSEL EEADR CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. BCF STATUS,RP0 BCF STATUS,RP1 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 STATUS,Z ;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: banksel EEADR INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. BCF STATUS,RP0 BCF STATUS,RP1 INCF FSR,F DECFSZ COUNT,F GOTO W_EEPROM_LOOP ;LOOP UNTIL ALL BYTES UPDATED. RETURN ;----------------------------------------------------------------------------------------- READ_EEPROM: banksel EECON1 BSF EECON1,RD ;PERFORM AN EEPROM READ. banksel EEDATA MOVF EEDATA,W ;GET THE READ BYTE. BCF STATUS,RP0 BCF STATUS,RP1 RETURN ;----------------------------------------------------------------------------------------- WRITE_EEPROM: BCF PIR1,EEIF ;CLEAR THE EEPROM WRITE DONE FLAG. banksel EEDATA MOVWF EEDATA ;PUT DATA TO BE WRITEN IN THE BUFFER. banksel EECON1 BSF EECON1,WREN ;ENABLE EEPROM WRITE. banksel INTCON bcf INTCON,GIE banksel EECON2 MOVLW H'55' ;PERFORM REQUIRED SAFETY STEPS. MOVWF EECON2 MOVLW H'AA' MOVWF EECON2 BSF EECON1,WR ;BEGIN THE WRITE. BCF STATUS,RP0 BCF STATUS,RP1 bsf INTCON,GIE WRITE_LOOP: BTFSS PIR1,EEIF ;LOOP UNTIL THE WRITE IS COMPLETED. GOTO WRITE_LOOP 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 STATUS,C 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 STATUS,C 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 STATUS,C ;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 STATUS,C ; (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 STATUS,C 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 STATUS,Z ;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