;Sept 26, 1999 NOTES FOR THOSE WHO RECEIVE THIS SOURCE ;(C) 1999 WiNK Radio ;I'm strictly a software amateur, blessed more with stamina than talent: ;this software can probably be improved and modified to suit your needs ;(Hooray for homebrewing!). However do NOT distribute it in modified form ;without (1) changing the file name and (2) putting a note with YOUR name ;and brief change description. ;This software was assembled by the Microchip assembler. It is self contained, ;with all port and pin references included. Note that some of those references ;are different than Microchip standard. I make misteaks when using bit ;instructions, sometimes screwing up and using a mnemonic for a register ;instead of the bit numer. So I changed bit references to start with ;lower-case "b" to reduce this kind of error. ;The hardware part of this project was described in Spring 1988 issue of ;Communications Quarterly. Copies, if still available, from CQ Communications ;(516) 681-2922. To summarize it is a two-chip computing counter that first ;counts the xmit carrier oscillator once (or BFO, which will result in a ;display of zero beat receive frequency), thereafter counts the LO slightly ;less than 5 times per second and displays either the sum or the difference on ;seven 7-segment LEDs with 10 Hz resolution. ;The LED display is a personal preference: I can read the frequency from ;across the room and my eyes instantly recognize the frequency with green MHz, ;red KHz, and green 100 and 10 Hz digits. If you operate in sunlight you would ;undoubtedly prefer one of the LCD counters! ;It counts dc to the limit of the PIC counter, rated to 50 MHz. The 25-bit ;arithmetic fails at 83 MHz, and some PICs go that high: all 16C62A-10S/P ;I've tried counted to over 75 MHz. ;The time base requires a precise 10 MHz PIC clock so rather than a canned osc ;I use a Digikey xtal and a trimmer to adjust it to exactly 10 MHz against WWV. ;The counter is two stacked boards to minimize panel space. Board sets were ;available from FAR Circuits at publication time for $7 set plus P&H. I'm sure ;the boards are still available, probably the same price, but contact Fred at ;(847) 836-9148 ;There are several things going on simultaneously when counting: ;First, the gate time is determined by TMR2 counting the PIC 10 MHz clock. ;The gate is opened in the interrupt the first time TMR2 overflows, 124 ;subsequent overflows are counted, then the gate is closed the 125th time ;TMR2 overflows. Opening and closing are done with the same number of clock ;cycles into the interrupt service routine so the gate is exactly 0.2 seconds. ;Second, the signal connected to , TOCKI, is counted by the prescalar ;and TMR1. Software counts overflows of TMR1 so it can compute the total ;number of counts after it detects the gate is closed. ;Third, software multiplexes data and digit drivers to operate the 7-segment ;display. ;The state of the gate, presence of TMR1 overflows, and "I have 124 TMR2 ;overflows, and I'm waiting for the 125th" are carried in appropriately ;labelled flags so the different functions know what's going on. ;After the gate is closed the total number of counts is found by first flushing ;counts out of the prescalar, similar to Microchip application note AN592). I ;operate a 74AC151 that performs not only input switching between LO and ;carrier inputs, but also between Vcc and ground to perform that flushing. ;The count is rounded off and converted to BCD with 10 Hz resolution. ;Pushbuttons are multiplexed along with the LEDs: ; The DIM button changes the LED duty cycle to reduce LED brightness ; if desired. ; ; The ZERO button stores the next counter reading and displays future ; counts with respect to that. This is useful after a CQ when you are ; tuning around using RIT. A second push on ZERO restores the actual ; frequency. ; ; The LOCK button stores the next counter reading and compares subsequent ; counts to it: if higher or lower pulses are generated on or ; . These pulses are integrated by an external op amp and filter ; to produce a correction voltage to return the LO to the original ; frequency. This is similar to "huffen puff" frequency locking made ; famous by Klass Spaargaren, PA0KSB, described in QEX Feb 1996, and does ; a great job of keeping a rig EXACTLY on frequency for days or weeks. ; ; The RECAL button resets the PIC: it remeasures the xmit carrier (or ; BFO if that's all you have). Useful if you switch BFO xtals for USB ; LSB selection, or change bands. ; ; The add/subtract input determines whether the display is the sum of ; BFO and LO, or the difference. Example: 5 MHz LO, 9 MHz IF on 80m ; requires subtracting LO from the BFO to display 4 MHz, on 20m it ; requires adding the LO to the BFO to display 14 MHz. ; ; Bill Carver, W7AAZ, ; PAGE LIST P=PIC16C62 ;---------------------------------------------------------------------------------------------- ; History ; ;July 26, 1997 synced dimming to 7-digit scan. Last bug? ;July 28, 1997 added test for TMR0 overflow in GET_LSB ; ;Jan 7, 1998 multiplexed switches with display ;Jan 18, 1998 LOCK working, adds all decimal points when locked ; moved position of ROUND in the RUN loop. Added ZERO button ; but no ZERO functions are implemented ;Jan 19, 1998 ROUND changed to LSB ; first cut at ZERO function ; merged computed interrupt jump, and actual interrupt svc ;Jan 31, 1998 COUNT13 : ZERO function debugged ;Aug 25, 1998 COUNT14 : correct on-board-switch EQUates ; add NOPs after driving switches ;---------------------------------------------------------------------------------------------- __CONFIG H'3F3A' ;configuration bits : no code protection, no watchdog timer ; ; P16C63.INC Standard Header File, Version 1.01 Microchip Technology, Inc. ; ;---------------------------------------------------------------------------------------------- ; Verify Processor ;---------------------------------------------------------------------------------------------- IFNDEF __16C62 MESSG "Processor-header file mismatch. Verify selected processor." ENDIF ;---------------------------------------------------------------------------------------------- ; Register Definitions ;---------------------------------------------------------------------------------------------- W EQU H'0' F EQU H'1' ;------Register Files-------------------------------------------------------------------------- INDF EQU H'0' TMR0 EQU H'01' PCL EQU H'02' STATUS EQU H'03' FSR EQU H'04' PORTA EQU H'05' PORTB EQU H'06' PORTC EQU H'07' PCLATH EQU H'0A' INTCON EQU H'0B' PIR1 EQU H'0C' TMR1L EQU H'0E' TMR1H EQU H'0F' T1CON EQU H'010' TMR2 EQU H'011' T2CON EQU H'012' OPTION_REG EQU H'81' TRISA EQU H'85' TRISB EQU H'86' TRISC EQU H'87' PIE1 EQU H'8C' PCON EQU H'8E' PR2 EQU H'92' ;------STATUS Bits----------------------------------------------------------------------------- B_IRP EQU H'07' B_RP1 EQU H'06' B_RP0 EQU H'05' B_NOT_TO EQU H'04' B_NOT_PD EQU H'03' B_Z EQU H'02' B_DC EQU H'01' B_C EQU H'0' ;------INTCON Bits----------------------------------------------------------------------------- ;hex value NOT bit position GIE EQU H'80' PEIE EQU H'40' T0IF EQU H'04' ;bit positions B_GIE EQU 7 B_PEIE EQU 6 B_T0IF EQU 2 ;------PIR1 Bits------------------------------------------------------------------------------- ;hex value (not bit position) TMR2IF EQU H'02' TMR1IF EQU H'01' ;bit positions B_TMR2IF EQU 1 B_TMR1IF EQU 0 ;------T1CON Bits------------------------------------------------------------------------------ TMR1ON EQU H'01' ;hex value B_TMR1ON EQU 0 ;bit position ;------T2CON Bits------------------------------------------------------------------------------ ;modified to hex value not bit position so assembler can compute values TOUTPS3 EQU H'40' TOUTPS2 EQU H'20' TOUTPS1 EQU H'10' TOUTPS0 EQU H'08' TMR2ON EQU H'04' T2CKPS1 EQU H'02' T2CKPS0 EQU H'01' B_TMR2ON EQU H'2' ;------OPTION Bits----------------------------------------------------------------------------- ;modified to hex value not bit position so assembler can compute values NOT_RBPU EQU H'80' INTEDG EQU H'40' T0CS EQU H'20' T0SE EQU H'10' PSA EQU H'08' PS2 EQU H'04' PS1 EQU H'02' PS0 EQU H'01' ; B_NOT_RBPU EQU 7 B_INTEDG EQU 6 B_T0CS EQU 5 B_T0SE EQU 4 B_PSA EQU 3 B_PS2 EQU 2 B_PS1 EQU 1 B_PS0 EQU 0 ;------PIE1 bits------------------------------------------------------------------------------ TMR2IE EQU H'02' ;enable timer 2 interrupts ;---------------------------------------------------------------------------------------------- ; RAM Definition ;---------------------------------------------------------------------------------------------- __MAXRAM H'FF' __BADRAM H'08'-H'09', H'1E'-H'1F' __BADRAM H'88'-H'89', H'8F'-H'91', H'95'-H'97', H'9A'-H'9F' ;---------------------------------------------------------------------------------------------- ; Configuration Bits ;---------------------------------------------------------------------------------------------- _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _CP_ALL EQU H'0CF' _CP_75 EQU H'15DF' _CP_50 EQU H'2AEF' _CP_OFF EQU H'3FFF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' ;---------------------------------------------------------------------------------------------- ;STATUS bit definitions LSB EQU 0 ;---------------------------------------------------------------------------------------------- LIST ;---------------------------------------------------------------------------------------------- ; PCB HARDWARE CONNECTIONS ;---------------------------------------------------------------------------------------------- ; PORT A ;---------------------------------------------------------------------------------------------- ;pin 6 of 16C62 = counter input signal = H'10' = RA4/TOCKI ;all rest are outputs so load TRISA with H'13' ;INPUT MULTIPLEXER ;pin 4 of 16C62 = pin 11 of 74AC151 A select line = ;pin 5 of 16C62 = pin 10 of 74AC151 B select line = ; 74AC151 C select line = ground ;pin 7 of 16C62 = pin 7 of 74AC151 chip enable (GATE control) = ;pin 1 of 74AC151 = D3 = Vdd (+5) ;pin 3 of 74AC151 = D1 = F1 input ;pin 4 of 74AC151 = D0 = spare input ;pin 12 of 74AC151 = D7 = F2 input : selectable only if PORTB<7> = 1 ;pins 2,13,14,15 of 74AC151 = D2,D4,D5,D6 = Vss (ground) ;input select equates: F3 EQU H'00' ;spare RF input F2 EQU H'0C' ;RB7 sets MSB of 74AC151 = carrier osc = F2 F1 EQU H'04' ;local oscillator = F1 B_F1 EQU 2 VSS EQU H'08' ;gnd (Vss) B_VSS EQU 3 VDD EQU H'0C' ;+5 (Vdd) NOT_GATE EQU H'20' ;binary value of GATE bit, set = closed B_NOT_GATE EQU 5 ;bit position of GATE bit, set = closed B_UP EQU 0 ;tuning bits in PORTA B_DOWN EQU 1 ;---------------------------------------------------------------------------------------------- ; PORT B ;---------------------------------------------------------------------------------------------- ;all port B pins are outputs, so TRISB=H'0' ;PORTB drives seven PNP transistors to drive one common cathode LED at a time LED_OFF EQU H'7F' ;all digits off, F2 off DIGIT0 EQU H'3F' ;PORTB<6>, pin 27 : 10 MHz digit DIGIT1 EQU H'5F' ;PORTB<5>, pin 26 DIGIT2 EQU H'6F' ;PORTB<4>, pin 25 DIGIT3 EQU H'77' ;PORTB<3>, pin 24 DIGIT4 EQU H'7B' ;PORTB<2>, pin 23 DIGIT5 EQU H'7D' ;PORTB<1>, pin 22 DIGIT6 EQU H'7E' ;PORTB<0>, pin 21 : 10 Hz digit ;PORTB<7> = pin 28 : keys the F2 (transmit oscillator) for calibration ;PORTB<7>=1, F2 ON : also sets MSB of 74AC151 to select carrier F2 input (D7) B_F2_ON EQU 7 ;---------------------------------------------------------------------------------------------- ; PORT C ;---------------------------------------------------------------------------------------------- ;PORTC outputs drive seven LED anode segments through 220 ohms ;bit-to-segment wiring connections : bseg EQU H'01' ;PORTC<0>, pin 11 aseg EQU H'02' ;PORTC<1>, pin 12 fseg EQU H'04' ;PORTC<2>, pin 13 gseg EQU H'08' ;PORTC<3>, pin 14 eseg EQU H'10' ;PORTC<4>, pin 15 dseg EQU H'20' ;PORTC<5>, pin 16 dpseg EQU H'40' ;PORTC<6>, pin 17 : binary value of decimal point B_dpseg EQU 6 ;bit position of decimal point cseg EQU H'80' ;PORTC<7>, pin 18 ;7-segment character definitions SEG0 EQU aseg+bseg+cseg+dseg+eseg+fseg ; 0 SEG1 EQU bseg+cseg ; 1 SEG2 EQU aseg+bseg+dseg+eseg+gseg ; 2 SEG3 EQU aseg+bseg+cseg+dseg+gseg ; 3 SEG4 EQU bseg+cseg+fseg+gseg ; 4 SEG5 EQU aseg+cseg+dseg+fseg+gseg ; 5 SEG6 EQU aseg+cseg+dseg+eseg+fseg+gseg ; 6 SEG7 EQU aseg+bseg+cseg ; 7 SEG8 EQU aseg+bseg+cseg+dseg+eseg+fseg+gseg ; 8 SEG9 EQU aseg+bseg+cseg+dseg+fseg+gseg ; 9 NOSEG EQU 0 ; index in with H'0A' MINUSEG EQU gseg ; index in with H'0B' ;bits 2 and 3 drive switches (always OUT) B_LOCKDIM EQU 2 ;PORTC<2>, pin 13 B_SUBZSW EQU 3 ;PORTC<3>, pin 14 ;bits 0,1 & multiplexed between switch (IN) and driving LED segments (OUT) B_SUBIN EQU 0 ;PORTC<0>, pin 11 B_DIMIN EQU 0 B_LOCKIN EQU 1 ;PORTC<1>, pin 12 B_ZEROIN EQU 1 B_SPAREIN EQU 4 ;PORTC<4>, pin 15 spare for 2 more switches ;---------------------------------------------------------------------------------------------- ; register usage ;---------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------- ; SWITCH variables ;---------------------------------------------------------------------------------------------- ;switch variable addresses. PREVIOUS_SWITCH EQU H'51' ;last times pattern of switches CURRENT_SWITCH EQU H'50' ;this times pattern of switches DEBOUNCD_SWITCH EQU H'4F' ;debounced switch pattern BOUNCE EQU H'4E' ;debounce down counter ;flag bit positions in all switch variables B_SUBTRACT EQU 0 ;bit 0 = SUBTRACT switch set B_DIM EQU 1 ;bit 1 = DIM switch set B_LOCK EQU 2 ;bit 2 = LOCK switch set B_ZERO EQU 3 ;bit 3 = ZERO switch set ;---------------------------------------------------------------------------------------------- ; DISPLAY brightness control constants and variables ;---------------------------------------------------------------------------------------------- DIMMEST EQU D'5' ;preset value of dim to start at dimmest BRIGHTNESS EQU H'4D' ;LED duty cycle: 1=100% duty cycle = brightest ;DIMMEST = lowest brightness LZ_CK EQU H'4C' ;LSB = leading zero check flag ;flag set when MSD will be displayed next ;flag is reset by first nonzero data BLINK EQU H'4B' ;when BLINK=0 the digit will be displayed D_FSR_SV EQU H'4A' ;divide FSR storage VECTOR EQU H'49' ;offset for software vectored interrupt ;0 = waiting for first TMR2 interrupt to open gate ;1 = gate open, waiting for last TMR2 interrupt to close the gate ;---------------------------------------------------------------------------------------------- ; LOCKING and ZERO variables and flags ;---------------------------------------------------------------------------------------------- TUNE_PATTERN EQU H'48' TUNE_UP EQU 0 ;PORTA = xxxxxx00 to tune up TUNE_DOWN EQU 3 ;PORTA = xxxxxx11 to tune down NO_TUNE EQU 1 ;PORTA = xxxxxx01 lock on, no tuning LOCK EQU H'47' ; xxxxxx00 = lock OFF ; xxxxxx01 = lock ON (test LSB, B_UP=1 for lock status) B_FIRST_LOCK EQU 4 ;B_FIRST_LOCK flag ; xxxxx1xx = pending change of LOCK ON/OFF B_FIRST_ZERO EQU 5 ;B_FIRST_ZERO flag ; xxxx1xxx = pending change of ZERO ON/OFF B_MINUS EQU 6 ;set when result of ZERO subtraction is negative ;---------------------------------------------------------------------------------------------- ; FZ is the 24 bit ZERO frequency, to subtract dial readings from ;---------------------------------------------------------------------------------------------- FZ_L EQU H'46' ;LS byte of lock frequency FZ_M EQU H'45' ;middle byte of lock frequency FZ_H EQU H'44' ;MS byte of lock frequency ;---------------------------------------------------------------------------------------------- ; FL is the 24 bit LOCK frequency storage, target for subsequent COUNTs ;---------------------------------------------------------------------------------------------- FL_L EQU H'43' ;LS byte of lock frequency FL_M EQU H'42' ;middle byte of lock frequency FL_H EQU H'41' ;MS byte of lock frequency ;---------------------------------------------------------------------------------------------- ; FA, FB and FC are source and destination registers for computations ;---------------------------------------------------------------------------------------------- FA_H EQU H'40' FA_M EQU H'3F' FA_L EQU H'3E' ; FB_H EQU H'3D' FB_M EQU H'3C' FB_L EQU H'3B' ; FC_H EQU H'3A' FC_M EQU H'39' FC_L EQU H'38' ;---------------------------------------------------------------------------------------------- ; COUNT is the 24 bit counter, part-hardware, part-software derived ;---------------------------------------------------------------------------------------------- COUNT_H EQU H'37' COUNT_M EQU H'36' COUNT_L EQU H'35' ;---------------------------------------------------------------------------------------------- ; F2 is the stored 24 bit carrier oscillator count ;---------------------------------------------------------------------------------------------- F2_H EQU H'34' F2_M EQU H'33' F2_L EQU H'32' ;---------------------------------------------------------------------------------------------- ;8 digits of BCD freq ;note : location H'31' was reserved during design in case pointer goes too far BCD7 EQU H'30' ; 10 x MHz (most signifigant digit) BCD6 EQU H'2F' ; 1 x MHz BCD5 EQU H'2E' ;100 x KHz BCD4 EQU H'2D' ; 10 x KHz BCD3 EQU H'2C' ; 1 x KHz BCD2 EQU H'2B' ;100 x Hz BCD1 EQU H'2A' ; 10 x Hz (least signifigant digit) DIGIT EQU H'29' ;digit to display: value = 0 to 6 ;---------------------------------------------------------------------------------------------- ; DIVIDE variables ;---------------------------------------------------------------------------------------------- DIV_H EQU H'28' ;most significant byte of divisor DIV_L EQU H'27' REMDR_H EQU H'26' ;most signifigant byte of remainder REMDR_L EQU H'25' LOOPCOUNT EQU H'24' ;inner loop counter for binary-->BCD digit DIGITLOOPS EQU H'23' ;outer loop count for #BCD digits ;---------------------------------------------------------------------------------------------- ; MISC software counters ;---------------------------------------------------------------------------------------------- COUNT1 EQU H'22' COUNT2 EQU H'21' COUNT3 EQU H'20' ;---------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------- PAGE ORG 0 GOTO BEGIN ;---------------------------------------------------------------------------------------------- ORG 4 ;hardware interrupt location ;---------------------------------------------------------------------------------------------- ;computed equal-time-duration interrupts MOVFW VECTOR ;VECTOR is either 0 or 2 ADDWF PCL,F ;index into one of these two interrupt routines ;timer 2 interrupt when VECTOR=0 : GATE_OPEN BCF PORTA,B_NOT_GATE ;open gate by dropping pin 7 of 74AC151 RETURN ;return with interrupts OFF ;timer 2 interrupt when VECTOR=2 : GATE CLOSE BSF PORTA,B_NOT_GATE ;close gate by raising pin 7 of 74AC151 RETURN ;return with interrupts OFF ;---------------------------------------------------------------------------------------------- DISPLAY ; TIMER 1 counts fclk/4 to time periodic checks of the switches ; and multiplexing digits to the LED display ;---------------------------------------------------------------------------------------------- ;CALL to DISPLAY will return immediately if TMR1 has NOT timed out BTFSS PIR1,B_TMR1IF RETURN ;TMR1 did time out : read/process switches and display ;-------------------------------------------------------------------------------------- ; READ SWITCH HARDWARE ;-------------------------------------------------------------------------------------- MOVLW LED_OFF ;turn off digit drivers MOVWF PORTB CLRF PORTC ;turn off LED segments BSF STATUS,B_RP0 ;select bank 1 MOVLW 3 MOVWF TRISC ;PC0 and PC1 are input pins BCF STATUS,B_RP0 ;select bank 0 CLRF CURRENT_SWITCH ;reset switch storage bits so the following ;code only has to SET bits ;-------------------------------------------------------------------------------------- ;check LOCK and DIM switches and set flags in CURRENT_SWITCH ;-------------------------------------------------------------------------------------- ; BSF PORTB,B_F2_ON ;scope trigger to view switch read operation ; BCF PORTB,B_F2_ON BSF PORTC,B_LOCKDIM ;turn drive on to LOCK and DIM switches NOP ;let switch drive settle NOP NOP NOP ;if LOCK switch is closed set B_LOCK bit in CURRENT_SWITCH BTFSC PORTC,B_LOCKIN BSF CURRENT_SWITCH,B_LOCK ;set B_LOCK bit in the LOCK byte ;if DIM switch is closed set B_DIM bit in CURRENT_SWITCH BTFSC PORTC,B_DIMIN BSF CURRENT_SWITCH,B_DIM ;-------------------------------------------------------------------------------------- ;check SUBTRACT and ZERO switches and set flags in CURRENT_SWITCH ;-------------------------------------------------------------------------------------- BCF PORTC,B_LOCKDIM ;turn drive off to LOCK and DIM switches BSF PORTC,B_SUBZSW ;turn drive on to SUBTRACT and ZERO switches NOP ;let switch drive settle NOP NOP NOP ;if SUBTRACT switch is closed set B_SUBTRACT bit in CURRENT_SWITCH BTFSC PORTC,B_SUBIN BSF CURRENT_SWITCH,B_SUBTRACT ;if ZERO switch is closed set B_ZERO bit in CURRENT_SWITCH BTFSC PORTC,B_ZEROIN BSF CURRENT_SWITCH,B_ZERO ;return port hardware to LED-driving condition CLRF PORTC ;turn off switch driving outputs BSF STATUS,B_RP0 ;select bank one CLRF TRISC ;switch port C back to being segment drivers BCF STATUS,B_RP0 ;select bank zero ; show switch pattern on bars a-b-f-g of display MSD ; disable timer interrupts ; BCF PIE1,TMR2IE ; MOVF CURRENT_SWITCH,W ; MOVWF PORTC ; MOVLW DIGIT0 ; MOVWF PORTB ; CLRF COUNT1 ; CLRF COUNT2 ;TEST1 DECFSZ COUNT1,F ; GOTO TEST1 ; DECFSZ COUNT2,F ; GOTO TEST1 ; BSF PIE1,TMR2IE ;-------------------------------------------------------------------------------------- ; PROCESS LED display ;-------------------------------------------------------------------------------------- ;display only turns digits on if BLINK = 0 (variable brightness) MOVF BLINK,W ;if BLINK is zero SKPZ ;skip over to get the digit GOTO MPX1 ;BLINK nonzero, blank digit by stuff in NOSEG MOVF DIGIT,W ;if DIGIT... SUBLW 2 ;... = 2 (100's of KHz) then... SKPNZ BCF LZ_CK,0 ;...turn off leading zero suppression ;(if not already off) MOVLW BCD1 ;start with the location of the first BCD digit ADDWF DIGIT,W ;add the digit number to it MOVWF FSR ;and get the BCD value stored at that address MOVF INDF,W ;NOTE: if you start fooling with the code you can commment out the ;next "SKPZ" to disable leading zero blanking, so you can see what's ;REALLY in those BCD storage locations SKPZ ;skip clearing leading zero ck flag if BCD = 0 BCF LZ_CK,0 ;clear leading zero check flag BTFSC LZ_CK,0 ;if leading zero flag not set skip to display ;If leading zero check flag is set... MPX1 MOVLW H'0A' ;jam pointer to NOSEG into W for blanking ;W contains index into the seven segment table. Either a 0-9 BCD number ;"NOSEG" (H'0A') for blanked digit, or "SEGMINUS" (H'0B') for a dash. ;index into the seven segment table, send segment pattern to port C ;index into the digit table, send digit-select pattern to port B CALL SEVENSEG ;retrieve segments from segment table for BCD MOVWF PORTC ;write W to segment register BTFSC LOCK,B_UP ;if LOCK is on... BSF PORTC,B_dpseg ;...then turn on this (any) decimal point MOVF DIGIT,W ;if DIGIT ... SUBLW 2 ;is number two then... SKPNZ BSF PORTC,B_dpseg ;...turn on decimal point ;the following check for DIGIT always being between 0 and 6 was found ;necessary to eliminate some vague flickering in the display. darned ;if I could figure out what was happening, but next 4 lines fixed it MOVF DIGIT,W ;check value of DIGIT... SUBLW 6 ;...to make sure digit is 6 or less SKPC ; CLRF DIGIT ;SHOULD NEVER get here MOVF DIGIT,W ;get digit number again CALL CATHODE ;get bit pattern from table to select that digit MOVWF PORTB ;write to cathode transistors to turn digit on ;set up the digit number for the next display scan MOVLW H'1' ;decrement to the next digit, SUBWF DIGIT,F ;if DIGIT was zero now = H'FF', setting carry SKPNC ;drop through to setting digit if get a carry GOTO MPX4 ;goto MPX4 when last digit was displayed ;last digit displayed last time: set up for new display, scanning ;downward from leftmost digit (digit=6) MOVLW H'6' ;set digit=6 MOVWF DIGIT BSF LZ_CK,0 ;turn leading zero check flag ON ;variable brightness function. LED will be turned on when BLINK = 0 ;blank/unblank only a scan of all seven digits MPX2 MOVF BLINK,W ;skip to preset if BLINK is zero SKPZ GOTO MPX3 ;if BLINK is nonzero decrement it MOVF BRIGHTNESS,W ;preset BLINK to current brightness setting MOVWF BLINK ; MPX3 DECF BLINK,F ;decrement ;set up 819.6 uS TMR1 display interval while TMR1 is still counting MPX4 CLRF TMR1L ;clear LSB first so carry to TMR1H will... MOVLW H'F8' ;...not affect this preset in TMR1H MOVWF TMR1H ;and TMR1 just keeps on counting BCF PIR1,B_TMR1IF ;clear display timer flag ;-------------------------------------------------------------------------------------- ;PROCESS SWITCH CLOSURES now that the diplay is back on ;-------------------------------------------------------------------------------------- ;are switches the same as last time? MOVF CURRENT_SWITCH,W SUBWF PREVIOUS_SWITCH,W SKPNZ GOTO SAME ;switch pattern is NOT the same so (re)start debounce process MOVLW H'41' ;initialize debounce count to decimal 65 MOVWF BOUNCE ;it'll decrement to 64 at next instruction ;switch pattern is the same as last time so decrement debounce SAME DECFSZ BOUNCE,F GOTO SAV_SWITCHES ;if debounce not zero go to display process ;debounce count went to zero. Set debounce count back to 1 ;so won't repeat when a switch is held INCF BOUNCE,F ;is it a new debounced switch pattern, or same as previously ;debounced? If it's the same pattern, don't need to check it again MOVF CURRENT_SWITCH,W SUBWF DEBOUNCD_SWITCH,W SKPNZ GOTO SAV_SWITCHES ;-------------------------------------------------------------------------------------- ;DO SWITCHES ;a new switch pattern has been debounced. ;not all patterns require action : for example 1-->0 (switch released) ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- ;check for 0-->1 (closure of) LOCK switch ;-------------------------------------------------------------------------------------- ;skip to ZEROCHK if no LOCK bit in the current switch closure pattern BTFSS CURRENT_SWITCH,B_LOCK GOTO ZEROCHECK ;if LOCK bit in DEBOUNCD pattern set then nothing new, skip to ZEROCHK BTFSC DEBOUNCD_SWITCH,B_LOCK GOTO ZEROCHECK ;since no LOCK bit in DEBOUNCD this is a new LOCK request ;set FIRST_LOCK bit in LOCK as a lock request flag to LOCKPROC BSF LOCK,B_FIRST_LOCK ;-------------------------------------------------------------------------------------- ;check for 0-->1 (closure of) ZERO switch ;-------------------------------------------------------------------------------------- ZEROCHECK ;skip if no ZERO bit in the current switch closure pattern BTFSS CURRENT_SWITCH,B_ZERO GOTO DIMCHECK ;if ZERO bit was in DEBOUNCD pattern (nothing new) goto DIM check BTFSC DEBOUNCD_SWITCH,B_ZERO GOTO DIMCHECK ;since no ZERO bit in DEBOUNCD, this is a new ZERO request ;set FIRST_ZERO bit in LOCK as a zero request flag to ZEROPROC BSF LOCK,B_FIRST_ZERO DIMCHECK ;-------------------------------------------------------------------------------------- ;check for 0-->1 (closure of) DIM switch ;-------------------------------------------------------------------------------------- BTFSS CURRENT_SWITCH, B_DIM GOTO DIM_EXIT BTFSC DEBOUNCD_SWITCH, B_DIM GOTO DIM_EXIT ;DIM process INCF BRIGHTNESS,F ;higher BRIGHTNESS = dimmer display MOVF BRIGHTNESS,W ;check if we're at minimum brightness SUBLW DIMMEST SKPZ GOTO DIM_EXIT ;skip to end of LED display process ADDLW 1 ;since it was zero, this makes W=1... MOVWF BRIGHTNESS ;...which makes LED BRIGHTNESS maximum DIM_EXIT ;Last check for switch closure has been performed ;Save this debounced switch pattern as the new DEBOUNCD MOVF CURRENT_SWITCH,W MOVWF DEBOUNCD_SWITCH ;Before exiting always save CURRENT as PREVIOUS switch. SAV_SWITCHES MOVF CURRENT_SWITCH,W ;save the current switch pattern MOVWF PREVIOUS_SWITCH ; as the new previous pattern ;If LOCK is ON skip to TUNING BTFSS LOCK,B_UP ;skip to sending TUNE_PATTERN if LOCK is on GOTO SW_EXIT ;exit without wasting time if LOCK is not ;-------------------------------------------------------------------------------------- ;TUNING process ;the CALL to DISPLAY terminates here. Not good practice, but to save ;time there are three returns from this routine, one each from tuning ;up, tuning down, or no tuning action at all ;-------------------------------------------------------------------------------------- MOVF TUNE_PATTERN,W ;if pattern is 01 will decrement to zero ADDLW H'FF' ;add -1 SKPNZ ;if it was NO_TUNE it'll add up to zero RETURN ;so exit SKPC ;if pattern was 00 there'll be no carry GOTO TUP ;so tune up ;it must be TUNE_DOWN : pulse bit 1 of port A high for 2.4 uS BSF PORTA,1 NOP NOP BCF PORTA,1 RETURN ;and exit ;pulse bit 0 of port A low for 2.4 uS to tune up TUP BCF PORTA,0 NOP NOP BSF PORTA,0 ;and exit in next instruction SW_EXIT RETURN SEVENSEG ;add offset in W to index into the table ADDWF PCL,F SEGTBL DT SEG0,SEG1,SEG2,SEG3,SEG4,SEG5,SEG6,SEG7,SEG8,SEG9,NOSEG,MINUSEG CATHODE ;add offset in W to index into the table ADDWF PCL,F CATHTBL DT DIGIT6,DIGIT5,DIGIT4,DIGIT3,DIGIT2,DIGIT1,DIGIT0 ;---------------------------------------------------------------------------------------------- BEGIN ; MODULAR DIAL CODE ;---------------------------------------------------------------------------------------------- ;initialize I/O port directions ;select bank one BSF STATUS,B_RP0 BCF STATUS,B_RP1 ;PORTB and PORTC all outputs CLRF TRISB CLRF TRISC ;PORTA<4>=count input, all others pins are outputs MOVLW H'10' MOVWF TRISA ; ;initialize TMR0 and TMR2 ;TIMER0 as ext counter by T0CS=1, OPTION<5=1>, input TOCKI (PORTA<4>) ;Prescalar to TMR0 by PSA=0, and prescaled by 256 by PS2=PS1=PS0=1 MOVLW T0CS+PS2+PS1+PS0 MOVWF OPTION_REG MOVLW D'249' ;PR2=249 to produce modulo-250 division by TMR2 MOVWF PR2 MOVLW TMR2IE ;TMR2 has interrupt, everything else is status MOVWF PIE1 ;select bank zero BCF STATUS,B_RP0 ;initialize software lock flag AND unlock integrator with bits from PORTA CLRF LOCK ;clear ALL the lock flags indicating LOCK is ;off, no pending LOCK, ZERO is off ;initialize display brightness MOVLW 1 ;initial LED duty cycle (brightness) = 100% MOVWF BRIGHTNESS MOVLW LED_OFF ;turn LED display off MOVWF PORTB ;by setting all PNP bases high ;initialize switch debounce counter, and zero switch input variables MOVLW H'41' ;initialize debounce count to decimal 65 MOVWF BOUNCE CLRF PREVIOUS_SWITCH ;no switches were pushed CLRF DEBOUNCD_SWITCH ;no switches were pushed and debounced ;CURRENT_SWITCH cleared every time anyway ;turn on carrier oscillator and select it as input to the counter BSF PORTB,B_F2_ON ;turn F2 on to stabilize MOVLW F2+NOT_GATE ;select F2, gate closed for 0.1 second MOVWF PORTA ;keep gate closed for 0.1 second while F2 stabilizes CLRF COUNT1 CLRF COUNT2 DELAY1 NOP DECFSZ COUNT1,F GOTO DELAY1 DECFSZ COUNT2,F GOTO DELAY1 ;zero the counter CLRF COUNT_H ;MS byte (software part of counter) CLRF TMR0 ;middle and LS byte (clears prescalar, too) BCF INTCON,B_T0IF ;clear TMR0 overflow flag ;set up the gate timer, TMR2. 6.4 mS x 125 = 0.8 second gate MOVLW D'125' ;count 125 timer overflows MOVWF COUNT1 CLRF VECTOR ;VECTOR = 0 points to open-gate interrupt svc ;-------------------------------------------------------------------------------------- ;clear TMR2, TMR2 overflow flag, then enable TMR2 and interrupts ;-------------------------------------------------------------------------------------- CLRF TMR2 BCF PIR1,B_TMR2IF ;clear gate timer overflow flag ;timer 2 ON with postscale=16, prescale=4 (timer period = 6.4 mS) MOVLW TOUTPS3+TOUTPS2+TOUTPS1+TOUTPS0+T2CKPS0+TMR2ON MOVWF T2CON MOVLW GIE+PEIE ;global enable interrupts MOVWF INTCON ;(INTCON maps into all register banks) ;-------------------------------------------------------------------------------------- WAIT10 ;loop waiting for first TMR2 overflow and processing of GATE_OPEN interrupt ;-------------------------------------------------------------------------------------- BTFSS PIR1,B_TMR2IF GOTO WAIT10 ;-------------------------------------------------------------------------------------- WAIT11 ;first TMR2 interrupt opened gate via "OPEN_GATE" interrupt service ;now circle in this loop, counting 124 more TMR2 overflows ;and counting F2 overflows of TMR0 ;-------------------------------------------------------------------------------------- ;increment MSB of COUNT when T0 overflows BTFSS INTCON,B_T0IF GOTO WAIT12 INCF COUNT_H,F BCF INTCON,B_T0IF WAIT12 BTFSS PIR1,B_TMR2IF ;check for TMR2 overflow GOTO WAIT11 ;no overflow, loop back and check TMR0 BCF PIR1,B_TMR2IF ;clear TMR2 overflow flag DECFSZ COUNT1,F ;decrement overflow count in COUNT1 GOTO WAIT11 ;loop back to WAIT11 if not zero ;-------------------------------------------------------------------------------------- ;COUNT1 is zero so 124 TMR2 overflows have happened. enable interrupts so the ;next one will close it. Wait in this loop for 125th TMR2 overflow. ;-------------------------------------------------------------------------------------- MOVLW 2 ;VECTOR=2 points to CLOSE_GATE interrupt svc MOVWF VECTOR BSF INTCON,B_GIE ;turn on interrupts WAIT13 BTFSS INTCON,B_T0IF ;increment MSB, COUNT_H, if TMR0 overflows GOTO WAIT14 INCF COUNT_H,F BCF INTCON,B_T0IF WAIT14 BTFSS PIR1,B_TMR2IF ;loop until TMR2 has timed out for last time GOTO WAIT13 ;indicating GATE_CLOSE has been tripped ;-------------------------------------------------------------------------------------- ;0.8 seconds after opened, second interrupt closes gate with "CLOSE_GATE" ;interrupt service. count is done ;-------------------------------------------------------------------------------------- BCF PORTB,B_F2_ON ;turn carrier oscillator off CALL GET_LSB ;force COUNT_L out of the prescalar CALL COUNT_TO_FA ;COUNT-->FA CALL ROUND_FA ;round off count and divide by two ;divide by two again CLRC RRF FA_H,F RRF FA_M,F RRF FA_L,F ;save count rounded to nearest 5 Hz in F2_H/M/L) MOVF FA_H,W ; FA--->W MOVWF F2_H ; W--->F2 MOVF FA_M,W ;etc MOVWF F2_M MOVF FA_L,W MOVWF F2_L ;divide by two again for display purposes, total division = 8 CLRC RRF FA_H,F RRF FA_M,F RRF FA_L,F ;convert FA to 7 BCD digits CALL BIN_TO_BCD ;initialize the display digit to 6 (MSD) and set leading zero flag MOVLW H'6' MOVWF DIGIT BSF LZ_CK,0 ;Turn on timer 1. Period set to 2048 in display exit routine ;Count 0.4uS clock 2048 times = 819.2 uS display period MOVLW TMR1ON MOVWF T1CON ;simulate a TMR1 flag to start DISPLAY next time TMR1IF is tested BSF PIR1,B_TMR1IF ;set up 1 second display of F2 CLRF COUNT1 CLRF COUNT2 MOVLW H'0A' MOVWF COUNT3 ;when COUNT1 = COUNT2 = COUNT3 = zero, drop into main LOOP DELAY2 CALL DISPLAY ;check switches, refresh display when necessary DECFSZ COUNT1,F GOTO DELAY2 DECFSZ COUNT2,F GOTO DELAY2 DECFSZ COUNT3,F GOTO DELAY2 ;---------------------------------------------------------------------------------------------- ;RUN-time loop, counts F1 about 5 times/second and displays F1+/-F2 ;---------------------------------------------------------------------------------------------- RUN ;select F1 (gate remains closed, TUNING LSbits aren't disturbed) BCF PORTA,B_VSS BSF PORTA,B_F1 CLRF VECTOR ;set interrupt vector to point to GATE_OPEN ;-------------------------------------------------------------------------- ;The 10 MHz clock is divided internally to 2.5 MHz (0.4 uS period). This is ;prescaled by 4 to get 0.8uS period, PR2 was already set so TMR2 divides by ;250, and its output is post-scaled by 2 so ;gate time = 0.4uS x 4 x 250 x 2 = 0.8 msec x 250 in COUNT1 = 0.2 sec ;-------------------------------------------------------------------------- MOVLW D'250' ;count 250 TMR2 TMR2 cycles MOVWF COUNT1 CLRF COUNT_H ;zero MS byte of 24-bit counter CLRF TMR0 ;zero TMR0 which also clears prescalar BCF INTCON,B_T0IF ;clear TMR0 overflow flag BCF PIR1,B_TMR2IF ;clear gate timer interrupt flag ;clear TMR2 and overflow flag, set period, enable interrupts and TMR2 CLRF TMR2 BCF PIR1,B_TMR2IF ;clear gate timer overflow flag MOVLW TOUTPS0+T2CKPS0 ;TMR2 prescale=4, postscale=2 MOVWF T2CON ;period = 0.8 mS MOVLW GIE+PEIE ;global enable interrupts MOVWF INTCON ;(INTCON maps into all register banks) BSF T2CON,B_TMR2ON ;turn TMR2 ON ;-------------------------------------------------------------------------- WAIT21 ;loop, until TMR2 interrupt ;-------------------------------------------------------------------------- BTFSS PIR1,B_TMR2IF GOTO WAIT21 ;-------------------------------------------------------------------------- ;first TMR2 interrupt opened the gate : now counting ;loop, counting F1 overflows and maintaining LED display ;until COUNT1 of TMR2 overflows has decremented to zero ;-------------------------------------------------------------------------- WAIT31 BTFSS INTCON,B_T0IF ;increment MSB of COUNT when TMR0 overflows GOTO WAIT32 INCF COUNT_H,F BCF INTCON,B_T0IF WAIT32 CALL DISPLAY ;check switches & refresh display if necessary BTFSS PIR1,B_TMR2IF ;decrement COUNT1 when TMR2 overflows GOTO WAIT31 ;loop if TMR2 not timed out BCF PIR1,B_TMR2IF ;clear timer flag DECFSZ COUNT1,F ;decrement gate count GOTO WAIT31 ;-------------------------------------------------------------------------- ;COUNT1 is zero. Set global interrupts ;-------------------------------------------------------------------------- MOVLW 2 ;VECTOR=2 for CLOSE_GATE interrupt service MOVWF VECTOR BSF INTCON,B_GIE ;turn on global interrupt bit again ;next TMR2 timeout will close the gate WAIT35 BTFSS INTCON,B_T0IF ;increment MSB of COUNT when TMR0 overflows GOTO WAIT36 INCF COUNT_H,F BCF INTCON,B_T0IF WAIT36 CALL DISPLAY BTFSS PIR1,B_TMR2IF ;loop until TMR2 has timed out for last time GOTO WAIT35 ;(this'll be after the gate was closed ; by the last TMR2 overflow/interrupt) ;gate is closed CALL GET_LSB ;move prescalar contents into COUNT_L CALL LOCKPROC ;set up flags for UP/DOWN pulses if necessary CALL ARITHMETIC ;add or subtr F2 to/from F1 depending on SWITCH CALL DISPLAY CALL ROUND_FA ;round off sum/difference to nearest 10 Hz CALL ZEROPROC ;compute ZERO freq, set MINUS flag if necessary CALL BIN_TO_BCD ;convert 24 bit binary in FA to 7 digits BCD GOTO RUN ;repeat whole process ;---------------------------------------------------------------------------------------------- GET_LSB ;The gate is closed. COUNT_H has the 8 MSB, TMR0 has next MSB ;when done the least signifigant byte of count is in COUNT_L ;---------------------------------------------------------------------------------------------- ;save middle byte MOVF TMR0,W ;get COUNT_M from TMR0 MOVWF COUNT_M ;and put it into middle byte of COUNT ;catch a potential last TMR0 overflow BTFSC INTCON,B_T0IF ;increment MSB of COUNT when TMR0 overflows INCF COUNT_H,F ;prescalar has LS byte, here count one bit at a time into COUNT_L ;preset LS byte to 256 (is same as zero, modulo 256) CLRF COUNT_L ;set input to Vdd (+5v) w/o disturbing LOCK bits 0-1, gate still closed BSF PORTA,2 BSF PORTA,3 ;PORTA = x1xx11xx = Vdd input BCF PORTA,B_NOT_GATE ;open gate: PORTA = x0xx11xx ;74AC151 output + +5V INCREMENT ;decrement the LS byte count DECF COUNT_L,F ;pulse the prescalar by one count under software control ;generate a VDD-->VSS-->VDD sequence from the 74AC151 by changing ;only bit 2 of PORTA, and without disturbing LOCK bits 0-1 using ;the bit set/clear instructions BCF PORTA,2 ;PORTA = x0xx10xx = switch 74AC151 to Vss (0V) BSF PORTA,2 ;PORTA = x0xx11xx = switch 74AC151 back to Vdd (5V) NOP ;precaution to allow carry INC_TEST ;If TMR0 and COUNT_M are the same, the prescalar did not overflow MOVF TMR0,W SUBWF COUNT_M,W SKPNZ INC1 GOTO INCREMENT ;did NOT overflow so do it again ;prescalar DID overflow. COUNT_L now is the LS byte of the 24 bit count BSF PORTA,B_NOT_GATE ;PORTA = x1xx11xx = close gate = Vdd RETURN ;---------------------------------------------------------------------------------------------- ROUND_FA ;round off FA ;---------------------------------------------------------------------------------------------- MOVLW H'1' ;1->FB MOVWF FB_L CLRF FB_M CLRF FB_H CALL A_PLUS_B ; FA + FB = COUNT + 1 --> FA ;divide rounded count in FA by two ;not strictly part of rounding, but since it was always followed ;by a divide might as well stick it here. CLRC RRF FA_H,F RRF FA_M,F RRF FA_L,F RETURN ;---------------------------------------------------------------------------------------------- LOCKPROC ;frequency lock process ;---------------------------------------------------------------------------------------------- BTFSS LOCK,B_FIRST_LOCK ;skip if this is first pass since LOCK switch pressed GOTO LOCKPROC2 ;FIRST_LOCK is NOT set, do LOCKPROC2 ;first pass since LOCK button was pushed : clear the first lock flag BCF LOCK,B_FIRST_LOCK ;toggle LOCK : should it be enabled, or disabled? BTFSS LOCK,B_UP ;LOCK is enabled if LSB of LOCK byte = 1 GOTO LOCKPROC1 ;LSB of LOCK byte was 1 indicating lock was ON, so turn lock OFF BCF LOCK,B_UP ;clear the lock flag to indicate LOCK is off BCF PORTA,B_UP ;clear the UP bit on PORTA to clamp integrator RETURN ; ;lock was OFF, so copy frequency-->lock frequency and turn it on LOCKPROC1 MOVF COUNT_H,W ;put most sig byte of count MOVWF FL_H ;into FL_H MOVF COUNT_M,W ;put middle byte of count MOVWF FL_M ;into FL_M MOVF COUNT_L,W ;puts least sig byte of count MOVWF FL_L ;into FL_L ;then turn lock ON BSF PORTA,B_UP ;set the UP bit on PORTA to unclamp integrator BSF LOCK,B_UP ;LOCK=1 indicating "lock is on" MOVLW NO_TUNE ;but disable integrator... MOVWF TUNE_PATTERN ;...until next count indicates someting is required RETURN ;If lock is off then exit the LOCKPROCess LOCKPROC2 BTFSS LOCK,B_UP ;LOCK enabled if LSB of LOCK byte = 1 RETURN ;return if LOCK not enabled ;LOCK is ON : is COUNT the same, higher, or lower than FL? Do byte-by-byte ;subtract starting at MSB not to get actual difference, but just to get Z and ;C to point to no tune, tune up or tune down. MOVF COUNT_H,W ;MSB of count into W SUBWF FL_H,W ;subtract F1_H from stored FL_H SKPZ ;if zero gotta check the next byte of freq GOTO LOCK1 ;when nonzero we can tune up or down MOVF COUNT_M,W ;middle byte of count into W SUBWF FL_M,W ;subtract F1_M from stored FL_M SKPZ ;if zero gotta check the next byte of freq GOTO LOCK1 ;when nonzero we can tune up or down MOVF COUNT_L,W ;LSB of count into W SUBWF FL_L,W ;subtract F1_L from stored FL_L ;based on subtraction, set TUNE_PATTERN so tuning pulses will be generated MOVLW NO_TUNE ;01 = lock on, but don't tune SKPNZ GOTO LOCK2 ;freq hasn't changed, no tuning pulses ;freq is not the same, it's either high or low LOCK1 MOVLW TUNE_UP ;00 = lock on, tune up SKPC ;if carry COUNT is low so tune up MOVLW TUNE_DOWN ;else tune down : 10 = lock on, tune down LOCK2 MOVWF TUNE_PATTERN RETURN ;---------------------------------------------------------------------------------------------- ZEROPROC ;---------------------------------------------------------------------------------------------- ;do something if B_FIRST_ZERO or B_ZERO are set BTFSC LOCK,B_FIRST_ZERO GOTO FIRSTZERO ;branch to FIRSTZERO if FIRST_ZERO bit is set BTFSC LOCK,B_ZERO ;branch to DOZERO if B_ZERO is set GOTO DOZERO RETURN ;no ZERO function was indicated so RETURN ;-------------------------------------------------------------------------- FIRSTZERO ;toggle state of ZERO flag ;-------------------------------------------------------------------------- BCF LOCK,B_FIRST_ZERO ;clear FIRST_ZERO flag BTFSS LOCK,B_ZERO GOTO ZERO_ON ;if ZERO is clear, jump to turning it ON BCF LOCK,B_ZERO ;ZERO function was on, turn it off BCF LOCK,B_MINUS ;and turn off possible jam of "-" into display RETURN ;-------------------------------------------------------------------------- ZERO_ON BSF LOCK,B_ZERO ;zero : OFF--->ON ;-------------------------------------------------------------------------- ;store 24 bit current frequency into FZ to become zero frequency MOVF FA_L,W ;FA-->FZ MOVWF FZ_L MOVF FA_M,W MOVWF FZ_M MOVF FA_H,W MOVWF FZ_H ;immediately fall through to perform the ZERO display process ;-------------------------------------------------------------------------- DOZERO ;-------------------------------------------------------------------------- BCF LOCK,B_MINUS ;anticipate positive difference: minus sign off MOVF FA_L,W ;save copy of FA (dial frequency) in FC MOVWF FC_L MOVF FA_M,W MOVWF FC_M MOVF FA_H,W MOVWF FC_H MOVF FZ_L,W ;put FZ (lock frequency) in FB MOVWF FB_L MOVF FZ_M,W MOVWF FB_M MOVF FZ_H,W MOVWF FB_H CALL A_MINUS_B ;FA(dial frequency) - FZ(lock frequency)-->FA SKPNC ;if carry then dial-lock > so... RETURN ;...its done ;no carry, so dial FA RETURN ;---------------------------------------------------------------------------------------------- ARITHMETIC ; input : 24 BIT counts in COUNT_H/M/L and F2_H/M/L ; add/subtract switch at PORTA<0> ; output : 24 bit sum or difference in FA_H/M/L ;---------------------------------------------------------------------------------------------- CALL COUNT_TO_FA MOVF F2_L,W ;F2_TO_FB MOVWF FB_L MOVF F2_M,W MOVWF FB_M MOVF F2_H,W MOVWF FB_H BTFSS DEBOUNCD_SWITCH,B_SUBTRACT ;test ADD/SUBTRACT switch GOTO A_PLUS_B ;add if subtract bit IS NOT set CALL A_MINUS_B ;subtract if subtract bit IS set SKPNC ;if carry then FA > FB so we're done GOTO ADONE ; ;there was no carry therefore FA < FB, so reverse them, subtract again MOVF COUNT_L,W ;COUNT_TO_FB : three bytes of F1 to FB MOVWF FB_L MOVF COUNT_M,W MOVWF FB_M MOVF COUNT_H,W MOVWF FB_H ; MOVF F2_L,W ;F2_TO_FA : three bytes of F2 to FA MOVWF FA_L MOVF F2_M,W MOVWF FA_M MOVF F2_H,W MOVWF FA_H A_MINUS_B MOVF FB_L,W ; A - B--> A (24 bit subtract) SUBWF FA_L,F SKPC GOTO SUB2 SUB1 MOVFW FB_M SUBWF FA_M,F SKPC GOTO SUB3 GOTO SUB4 SUB2 MOVLW H'01' SUBWF FA_M,F SKPNC GOTO SUB1 MOVF FB_M,W SUBWF FA_M,F SUB3 DECF FA_H,F SUB4 MOVF FB_H,W SUBWF FA_H,F RETURN A_PLUS_B MOVF FB_L,W ; A + B--> A (24 bit add) ADDWF FA_L,F SKPNC GOTO ADD2 ADD1 MOVFW FB_M ADDWF FA_M,F SKPNC GOTO ADD3 GOTO ADD4 ADD2 MOVLW H'01' ADDWF FA_M,F SKPC GOTO ADD1 MOVF FB_M,W ADDWF FA_M,F ADD3 INCF FA_H,F ADD4 MOVF FB_H,W ADDWF FA_H,F ADONE RETURN ;---------------------------------------------------------------------------------------------- ; subroutine to MOVE the three bytes of COUNT to FA ;---------------------------------------------------------------------------------------------- COUNT_TO_FA MOVF COUNT_L,W MOVWF FA_L MOVF COUNT_M,W MOVWF FA_M MOVF COUNT_H,W MOVWF FA_H RETURN ;---------------------------------------------------------------------------------------------- BIN_TO_BCD ; BINARY TO BCD ; input : 24 bit, rounded off 0.2 second count in FA ; output : 7 BCD digits into BCD1-7 (BCD1 = least signifigant digit) ;---------------------------------------------------------------------------------------------- MOVLW BCD1 ;store BCD1 destination into Divide_FSR MOVWF D_FSR_SV MOVLW 7 ;number of digits to be converted MOVWF DIGITLOOPS ;---------------------------------------------------------------------------------------------- DIV_BY_TEN ;divide FA_H/M/L by 10 ;after first divide remainder is least signifigant BCD digit, BCD1. ;Each subsequent divide makes the next BCD digit from BCD1 to BCD7 ;---------------------------------------------------------------------------------------------- CALL DISPLAY ;check switches, refresh display if necessary CLRF DIV_H ;set divisor to 10 MOVLW H'0A' MOVWF DIV_L ; CLRF REMDR_H ;clear remainder CLRF REMDR_L ; MOVF DIV_L,W SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F RLF FA_H,F ; MOVLW 7 ;set loop count to 7 MOVWF LOOPCOUNT ; LOOPA RLF FA_H,W RLF REMDR_L,F RLF REMDR_H,F MOVF DIV_L,W BTFSS FA_H,LSB GOTO ADDA ; SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F GOTO OKL1 ; ADDA ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F ; OKL1 RLF FA_H, F DECFSZ LOOPCOUNT,F GOTO LOOPA ; RLF FA_M,W RLF REMDR_L,F RLF REMDR_H,F MOVF DIV_L,W BTFSS FA_H,LSB GOTO ADDL8 ; SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F GOTO OKL8 ; ADDL8 ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F OKL8 RLF FA_M,F ; MOVLW 7 ;set loop count to 7 MOVWF LOOPCOUNT ; LOOPB RLF FA_M,W RLF REMDR_L,F RLF REMDR_H,F MOVF DIV_L,W BTFSS FA_M,LSB GOTO ADDB ; SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F GOTO OKLB ; ADDB ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F ; OKLB RLF FA_M, F DECFSZ LOOPCOUNT,F GOTO LOOPB ; RLF FA_L,W RLF REMDR_L,F RLF REMDR_H,F MOVF DIV_L,W BTFSS FA_M,LSB GOTO ADDL16 ; SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F GOTO OKL16 ; ADDL16 ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F ; OKL16 RLF FA_L, F ; MOVLW 7 ;set loop count to 7 MOVWF LOOPCOUNT ; LOOPC RLF FA_L,W RLF REMDR_L,F RLF REMDR_H,F MOVF DIV_L,W BTFSS FA_L,LSB GOTO ADDLOOPC ; SUBWF REMDR_L,F MOVF DIV_H,W SKPC INCFSZ DIV_H,W SUBWF REMDR_H,F GOTO OKLOOPC ; ADDLOOPC ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F OKLOOPC RLF FA_L,F DECFSZ LOOPCOUNT,F GOTO LOOPC ; BTFSC FA_L,LSB GOTO DIVDONE MOVF DIV_L,W ADDWF REMDR_L,F MOVF DIV_H,W SKPNC INCFSZ DIV_H,W ADDWF REMDR_H,F DIVDONE ;---------------------------------------------------------------------------------------------- ;after dividing the remainder is the desired BCD digit. Store it. ;repeat until DIGITLOOPS is zero MOVF D_FSR_SV,W ;get destination address MOVWF FSR ;and put in FSR MOVF REMDR_L,W ;move REMDR_L into W MOVWF INDF ;store indirect ;if loops is 3 (100 KHz digit computed) DDD1 MOVF DIGITLOOPS,W SUBLW 3 SKPZ GOTO DIVDON1 ;and if MINUS bit in LOCK is set jam a "-" character into BCD BTFSS LOCK,B_MINUS ;if MINUS flag is NOT set... GOTO DIVDON1 MOVLW H'0B' ;but if it is, jam pointer for MINUSEG char... MOVWF INDF ;...overstoring tested digit DIVDON1 INCF D_FSR_SV,F ;increment destination pointer DECFSZ DIGITLOOPS,F ;decrement DIGITLOOPS... GOTO DIV_BY_TEN ;...repeat until zero RETURN END ;of progr