;***************************************************************************************** ; ; 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_628 23.Juli-2005 Ein Versuch ; ; ; Umgebaut von Tomtom DL1MFK ; Der PIC 16F628 wird nur mit dem internen RC-Oszillator betrieben. ; das PLL.DAT-File und das PLL628.ASM-File ist so angepasst, daß eine ; Schrittweite von 125kHz entsprechend dem SP5055 (mit 4MHz Quarz) eingehalten wird ; Es ist eine stark gekürzte Version, die für einen ver 10-Facher für 24 GHz nur ; insoweit geändert ist, daß der Dezimalpunkt sich nach rechts verschiebt, die Frequenz- ; anzeige sich ganz links befindet und ein 0 am ende hinzugefügt wird ; ; 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 INTCON SET 0X0B OPTION_REG SET 0X01 CMCON EQU H'001F' EEDATA EQU H'001A' EEADR EQU H'001B' EECON1 EQU H'001C' EECON2 EQU H'001D' PIR1 EQU H'000C' GIE EQU H'0007' #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 PIR1,7 ; CONFIGURATION BITS _WDT_OFF EQU H'3FFB' _LVP_OFF EQU H'3F7F' _INTRC_OSC_NOCLKOUT EQU H'3FFC' PROCESSOR 16F628 ;SELECT PROCCESOR, AND ASSEMBLER OPTIONS. LIST F=INHX8M,N=0 RADIX DEC __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF ;INT OSCILLATOR (4MHz), NO WATCHDOG, NO LOW VOLTAGE PROGRAMMING ;***************************************************************************************** ; GENERAL DEFINITIONS ;***************************************************************************************** #DEFINE SCL PORTA,0 ;I2C CLOCK O/P LINE. #DEFINE SDA PORTA,1 ;I2C DATA O/P LINE. #DEFINE BUTTON_P PORTA,4 ;FREQ + BUTTON. #DEFINE BUTTON_N PORTB,0 ;FREQ - BUTTON. #DEFINE BUTTON_S PORTB,1 ;SHIFT BUTTON. #DEFINE E PORTB,2 ;LCD Enable 0=aus 1=an #DEFINE RS PORTB,3 ;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 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 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 = 50KHz 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_24Ghz_628.dat" ;FILE TO SET THE INITIAL PLL & DISPLAY ; FREQ RANGE INTO EEPROM. ;***************************************************************************************** ;***************************************************************************************** ; RAM VARIABLES 68 BYTES MAX ;***************************************************************************************** CBLOCK H'20' ;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'21' ;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. UN: EQU $ - ST DT "X",0 LOCKED: EQU $ - ST DT "L",0 ONE_SPACE: EQU $ - ST DT " ",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. MOVLW 7 MOVWF CMCON BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00010100' MOVWF PORTA ;RA0,RA1,RA3 = OUTPUTS, RA2,RA4 INPUT. MOVLW B'00000011' MOVWF PORTB ;RB2,RB7 = OUTPUT, RB0,RB1 = 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. 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. MOVLW H'8F' CALL LCD_CMD ;POSITION THE CURSOR, LINE 1 POS 16 MOVLW LOCKED ;DISPLAY 'LOCKED'. CALL LCD_TEXT CALL DISP_MHZ ;DISPLAY ' MHz ' ON LINE 1 POS 11. MAIN_LOOP: BTFSC TIMEOUT ;HAS THE DISPLAY TIMEOUT BEEN REACHED? GOTO MAIN_DISP ;Y. REDRAW THE MAIN DISPLAY. BSF EEPROM_UPDATE CALL UPDATE_EEPROM ;SEE IF EEPROM UPDATE IS REQUIRED. MOVLW H'8E' CALL LCD_CMD ;GOTO THE START OF LINE 1. 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 ONE_SPACE ;Y. DISPLAY ' '. PLL_NOT_LOCKED: CALL LCD_TEXT ;N. DISPLAY 'X'. 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. CALL COPY_FROM_ARG SKIP_BUTTONS: CALL UPDATE_PLL ;UPDATE PLL AND RETURN THE DISPLAY FREQ. CALL DISPLAY_FREQ ;DISPLAY THE VARIABLE. GOTO MAIN_LOOP ;N. CONTINUE UPDATING THE 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'8A' 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. ;----------------------------------------------------------------------------------------- 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 ;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 CARRY RLF ARG,F RLF ARG,F ADDWF ARG,F MOVF ARG,W MOVWF ARG BCF CARRY 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'80' ;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. MOVLW '0' ;DISPLAY '0'. CALL LCD_CHR RETURN ;----------------------------------------------------------------------------------------- UPDATE_PLL: CALL GET_PLL_STATUS ;UPDATE THE STATUS BYTE. (PLL LOCKED ?) 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. 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 ' ' 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 ;----------------------------------------------------------------------------------------- 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 ;----------------------------------------------------------------------------------------- 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 INTCON,GIE BSF RP0 ;SELECT PAGE 1 FOR TRIS REG ACCESS. MOVLW B'00000011' 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'00001100' ;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'00001100' ;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. MOVLW 75 ;WAIT 250uS. LCD EXECUTION TIME = 120uS. CALL DELAY ;(3.3uS x 75) bsf INTCON,GIE RETURN ;----------------------------------------------------------------------------------------- ;----------------------------------------------------------------------------------------- ; 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'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. 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 PORTA,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. 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: bsf RP0 CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. bcf RP0 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. bsf RP0 INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. bcf RP0 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. bsf RP0 CLRF EEADR ;POINT TO THE FIRST BYTE IN EEPROM. bcf RP0 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: bsf RP0 INCF EEADR,F ;PREPARE TO READ THE NEXT BYTE. bcf RP0 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. MOVF EEDATA,W ;GET THE READ BYTE. BCF RP0 ;RETURN TO PAGE 0 FOR PORT ACCESS. RETURN ;----------------------------------------------------------------------------------------- WRITE_EEPROM: BCF EEIF ;CLEAR THE EEPROM WRITE DONE FLAG. BSF RP0 MOVWF EEDATA ;PUT DATA TO BE WRITEN IN THE BUFFER. BSF WREN ;ENABLE EEPROM WRITE. bcf INTCON,GIE MOVLW H'55' ;PERFORM REQUIRED SAFETY STEPS. MOVWF EECON2 MOVLW H'AA' MOVWF EECON2 BSF WR ;BEGIN THE WRITE. bsf INTCON,GIE bcf RP0 WRITE_LOOP: BTFSS 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 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