The Weeder Frequency Counter Expanded by Brad Hernlem 3 byte count, 8 digits, 1Hz resolution below 16MHz ;****************************************************************************** ; FREQUENCY COUNTER ; Model : WTCNT ; Author : Terry J. Weeder ; Date : November 18, 1993 ; Version: 1.0 ; ; WWW.WEEDTECH.COM ; ; Ported to 16f84 by ; Peter Cousens ; October 1998 ; ; Further revised, ; expanded and modified by ; Brad Hernlem ; October 2000 ;****************************************************************************** ; This version is changed to use the full capability to count to 3 byte ; quantities (16,777,215) and disables the overflow at 10,000,000 originally ; in Cousens' code. Also, 8 digits are used instead of seven and the gate ; times are set to 1 second to allow 1 Hz resolution on all ranges. ; When the frequency exceeds 16,777,215 Hz the gate period is ; reduced to 0.1 second to keep the count within 3 bytes. When frequency is ; again less than 16,777,216 Hz the gate period reverts to 1 second. ; Please also note that the register addresses are now designated in a ; CBLOCK. This means that any changes to the CBLOCK MUST NOT affect the ; fixed addresses of the digs to digs+7 registers which SHOULD begin at ; 0C and increase from there. If these are changed, the FSR indirect ; addressing will be munged. ; ; Other changes: ; ; 1) the LCD control pins are reassigned to be compatible with Halicky's code ; 2) the display reads "0" when no pulses are inputted. Cousens' code included ; an rtcc toggle of unknown use which gave an output of "1" without input. ; 3) display shows message "Counting" during initial 1 second count delay. ; ; [Brad Hernlem] ; ; FCounter.ASM ;*************************************************************************** list P=PIC16F84A INCLUDE __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON rtcc equ 1h c equ 0h z equ 2h o equ 7h rs equ 0h ;This bits were changed for compatibility rw equ 1h ;with other counter e equ 2h ;routines (e.g. Halicky) ;Cousens had the following hard designated. CBLOCK makes them float ;but BEWARE that digs is addressed by FSR and MUST NOT be changed from ;the first position of this block CBLOCK h'0C' digs:8 ;the display digits registers digs to digs+7 count1 count2 addcnt gate cnt1 cnt2 cnt3 calc1 calc2 calc3 sum1 sum2 sum3 rtcc2 temp1 temp2 temp3 cnt4 ENDC ; org 0 goto start ; int_del movlw 0x05 ;delay 5.000 ms (4 MHz clock) movwf count1 d1 movlw 0xA5 movwf count2 d2 decfsz count2,f goto d2 decfsz count1,f goto d1 retlw 0x00 ; lcd_out movwf PORTB ;load data into PORTB movlw b'00000000' ;define PORTB as output tris PORTB bsf PORTA,rs ;rs = data bcf PORTA,rw ;r/w = write bsf PORTA,e ;toggle enable bcf PORTA,e movlw b'11111111' ;define PORTB as input tris PORTB bcf PORTA,rs ;rs = instruction bsf PORTA,rw ;r/w = read bsf PORTA,e ;enable high movf PORTB,w ;get address counter movwf addcnt bsf addcnt,7 bcf PORTA,e ;enable low out1 bsf PORTA,e ;enable high btfss PORTB,7 ;test busy flag goto out2 bcf PORTA,e ;enable low goto out1 out2 bcf PORTA,e ;enable low goto shift ; inst movwf PORTB ;load instruction into PORTB movlw b'00000000' ;define PORTB as output tris PORTB bcf PORTA,rs ;rs = instruction bcf PORTA,rw ;r/w = write bsf PORTA,e ;toggle enable bcf PORTA,e movlw b'11111111' ;define PORTB as input tris PORTB bsf PORTA,rw ;r/w = read inst1 bsf PORTA,e ;enable high btfss PORTB,7 ;test busy flag goto inst2 bcf PORTA,e ;enable low goto inst1 inst2 bcf PORTA,e ;enable low retlw 0x00 ; shift btfss addcnt,0 ;shift to opposite side of display? retlw 0x00 btfss addcnt,1 retlw 0x00 btfss addcnt,2 retlw 0x00 btfss addcnt,3 retlw 0x00 movlw 0x39 addwf addcnt,f bsf addcnt,7 movf addcnt,w goto inst ; sub bcf STATUS,o ;clear overflow bit movf calc1,w ;subtract calc1 from cnt1 subwf cnt1,f btfsc STATUS,c goto sb1 movlw 0x01 ;borrow from cnt2 if overflow subwf cnt2,f btfsc STATUS,c goto sb1 subwf cnt3,f ;borrow from cnt3 if cnt2 overflow btfss STATUS,c bsf STATUS,o ;set overflow bit if result is negative sb1 movf calc2,w ;subtract calc2 from cnt2 subwf cnt2,f btfsc STATUS,c goto sb2 movlw 0x01 ;borrow from cnt3 if cnt2 overflow subwf cnt3,f btfss STATUS,c bsf STATUS,o ;set overflow bit if result is negative sb2 movf calc3,w ;subtract calc3 from cnt3 subwf cnt3,f btfss STATUS,c bsf STATUS,o ;set overflow bit if result is negative retlw 0x00 ; add movf calc1,w ;add calc1 to cnt1 addwf cnt1,f btfss STATUS,c goto ad1 incfsz cnt2,f ;add to cnt2 if cnt1 overflow goto ad1 incf cnt3,f ;add to cnt3 if cnt2 overflow ad1 movf calc2,w ;add calc2 to cnt2 addwf cnt2,f btfsc STATUS,c incf cnt3,f ;add to cnt3 if cnt2 overflow movf calc3,w ;add calc3 to cnt3 addwf cnt3,f retlw 0x00 ; ; The following has been modified to extract 8 digits ; and get 1 Hz accuracy up to the full 3 byte limit of 16,777,215 ; cnvt movlw 0x08 ;8 digits in display movwf count1 movlw 0x0C ;set fsr for MSB in display movwf FSR movlw 0x2F ;one less that ASCII "0" cnvt0 movwf INDF incf FSR,f decfsz count1,f goto cnvt0 movlw 0x98 ;load "10,000,000" in calc1-3 movwf calc3 movlw 0x96 movwf calc2 movlw 0x80 movwf calc1 cnvt1 call sub ;subtract 10 million from count incf digs,f ;increment 10,000,000s register btfss STATUS,o ;check if overflow goto cnvt1 call add ;add back last number movlw 0x0F ;load "1,000,000" in calc1-3 movwf calc3 movlw 0x42 movwf calc2 movlw 0x40 movwf calc1 cnvt2 call sub ;subtract number from count incf digs+1,f ;increment 1,000,000's register btfss STATUS,o ;check if overflow goto cnvt2 call add ;add back last number movlw 0x01 ;load "100,000" in calc1-3 movwf calc3 movlw 0x86 movwf calc2 movlw 0xA0 movwf calc1 cnvt3 call sub ;subtract number from count incf digs+2,f ;increment 100,000's register btfss STATUS,o ;check if overflow goto cnvt3 call add ;add back last number clrf calc3 ;load "10,000" in calc1-3 movlw 0x27 movwf calc2 movlw 0x10 movwf calc1 cnvt4 call sub ;subtract number from count incf digs+3,f ;increment 10,000's register btfss STATUS,o ;check if overflow goto cnvt4 call add ;add back last number movlw 0x03 ;load "1,000" in calc1-3 movwf calc2 movlw 0xE8 movwf calc1 cnvt5 call sub ;subtract number from count incf digs+4,f ;increment 1,000's register btfss STATUS,o ;check if overflow goto cnvt5 call add ;add back last number clrf calc2 ;load "100" in calc1-3 movlw 0x64 movwf calc1 cnvt6 call sub ;subtract number from count incf digs+5,f ;increment 100's register btfss STATUS,o ;check if overflow goto cnvt6 call add ;add back number movlw 0x0A ;load "10" in calc1-3 movwf calc1 cnvt7 call sub ;subtract number from count incf digs+6,f ;increment 10's register btfss STATUS,o ;check if overflow goto cnvt7 call add ;add back last number movf cnt1,w ;put remainder in 1's register addwf digs+7,f incf digs+7,f retlw 0x00 ; ; Count period is 25*count2*count1 clocks ; for 4MHz crystal with count2=200, count1=20 gives 0.1 s gate period ; count1=200 gives 1 s gate period ; count movlw b'00110111' ;rtcc = ext, 1/256 option movlw b'00010000' ;define PORTA as output tris PORTA bcf PORTA,3 bcf PORTA,rs clrf cnt3 clrf cnt4 clrf temp3 clrf rtcc clrf rtcc2 ; bsf PORTA,rs ;toggle rtcc pin ; bcf PORTA,rs ;[these two lines were in Cousens' code. purpose unkown] movf gate,w ;get gate time movwf count1 st1 bsf PORTA,3 ;start count fr4 movlw 0xC8 ;1 count2 set to 200 movwf count2 ;2 goto fr6 ;4 fr5 nop ;24 nop ;25 nop ;01 nop ;02 nop ;03 nop ;04 fr6 movf rtcc,w ;5 test for rtcc rollover (12) subwf rtcc2,f ;6 btfss STATUS,z ;7 goto fr7 ;9 nop ;9 goto fr8 ;11 fr7 btfsc STATUS,c ;10 incf cnt3,f ;11 fr8 movwf rtcc2 ;12 movf cnt3,w ;13 test for cnt3 rollover subwf temp3,f ;14 btfss STATUS,z ;15 goto br1 ;17 nop ;17 goto br2 ;19 br1 btfsc STATUS,c ;18 incf cnt4,f ;19 increment cnt4 if cnt3 rollover br2 movwf temp3 ;20 decfsz count2,f ;21 goto fr5 ;23 decfsz count1,f ;23+25(count2-1) goto fr4 ;25 bcf PORTA,3 ;25*count2*count1 stop count st2 movf rtcc,w ;get rtcc count movwf cnt2 subwf rtcc2,f ;test for rtcc rollover btfss STATUS,c goto fr9 btfss STATUS,z incf cnt3,f fr9 clrf cnt1 ;set to get prescaler count fr10 decf cnt1,f bsf PORTA,rs ;toggle rtcc pin bcf PORTA,rs movf rtcc,w ;test if rtcc has changed xorwf cnt2,w btfsc STATUS,z goto fr10 retlw 0x00 ; ;****************************************************************************** ; START ;****************************************************************************** ; start clrf PORTA ;instruction, write, enable low movlw b'00010000' tris PORTA clrf PORTB movlw b'00000000' tris PORTB call int_del call int_del call int_del movlw 0x38 ;initialize display movwf PORTB bsf PORTA,e ;toggle enable call int_del bcf PORTA,e bsf PORTA,e ;toggle enable call int_del bcf PORTA,e bsf PORTA,e ;toggle enable call int_del bcf PORTA,e movlw 0x38 ;function call inst movlw b'00001100' ;display on, cursor off call inst movlw b'00000001' ;clear display call inst movlw b'00000110' ;entry mode call inst ; Intro message during initial count movlw 0x20 ;space call lcd_out movlw 0x20 ;space call lcd_out movlw 0x20 ;space call lcd_out movlw 0x20 ;space call lcd_out movlw 0x43 ;C call lcd_out movlw 0x6F ;0 call lcd_out movlw 0x75 ;u call lcd_out movlw 0x6E ;n call lcd_out movlw 0x74 ;t call lcd_out movlw 0x69 ;i call lcd_out movlw 0x6E ;n call lcd_out movlw 0x67 ;g call lcd_out ; mhz movlw 0xC8 ;1 sec gate movwf gate call count clrw ;check whether cnt4 is zero xorwf cnt4,w btfss STATUS,z goto shortg ;not zero - use shorter gate period call cnvt ;convert binary to BCD movlw 0x30 ;test if "0" xorwf digs,w btfss STATUS,z goto mhz1 movlw 0x30 ;test if "0" xorwf digs+1,w btfsc STATUS,z goto khz1 mhz1 movlw 0x81 ;set display address call inst movlw 0x02 ;output first 2 characters movwf count1 movlw 0x0C ;MSD of freq, i.e. digs movwf FSR mhz2 movlw 0x30 ;test if "0" xorwf INDF,w btfss STATUS,z goto mhz3 movlw 0x20 ;change preceeding "0's" to "space" call lcd_out incf FSR,f decfsz count1,f goto mhz2 goto mhz4 mhz3 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto mhz3 mhz4 movlw 0x2E ;"." call lcd_out movlw 0x06 ;output last 6 characters movwf count1 mhz5 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto mhz5 movlw 0x20 ;"space" call lcd_out movlw 0x4D ;"M" call lcd_out movlw 0x48 ;"H" call lcd_out movlw 0x7A ;"z" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out goto mhz ; khz movlw 0xC8 ;1 sec gate movwf gate call count call cnvt ;convert binary to BCD movlw 0x30 ;test if 0 xorwf digs,w btfss STATUS,z goto mhz1 movlw 0x32 ;test if < 2 subwf digs+1,w btfsc STATUS,c goto mhz1 movlw 0x30 ;test if "0" xorwf digs+1,w btfss STATUS,z goto khz1 movlw 0x30 ;test if "0" xorwf digs+2,w btfss STATUS,z goto khz1 movlw 0x30 ;test if "0" xorwf digs+3,w btfsc STATUS,z goto hz0 khz1 movlw 0x81 ;set display address call inst movlw 0x05 ;output first 5 characters movwf count1 movlw 0x0C ;MSD of freq, digs register movwf FSR khz2 movlw 0x30 ;test if "0" xorwf INDF,w btfss STATUS,z goto khz3 movlw 0x20 ;change preceeding "0's" to "space" call lcd_out incf FSR,f decfsz count1,f goto khz2 goto khz4 khz3 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto khz3 khz4 movlw 0x2E ;"." call lcd_out movf INDF,w ;output last 3 characters call lcd_out incf FSR,f movf INDF,w call lcd_out incf FSR,f movf INDF,w call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x4B ;"K" call lcd_out movlw 0x48 ;"H" call lcd_out movlw 0x7A ;"z" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out goto khz ; hz movlw 0xC8 ;1 sec gate movwf gate call count call cnvt ;convert binary to BCD movlw 0x30 ;test if "0" xorwf digs,w btfss STATUS,z goto mhz1 movlw 0x32 ;test if < 2 subwf digs+1,w btfsc STATUS,c goto mhz1 movlw 0x30 ;test if "0" xorwf digs+1,w btfss STATUS,z goto khz1 movlw 0x30 ;test if "0" xorwf digs+2,w btfss STATUS,z goto khz1 movlw 0x30 ;test if "0" xorwf digs+3,w btfss STATUS,z goto khz1 hz0 movlw 0x81 ;set display address call inst movlw 0x07 ;output first 7 characters movwf count1 movlw 0x0C ;MSD of freq, digs register movwf FSR hz1 movlw 0x30 ;test if "0" xorwf INDF,w btfss STATUS,z goto hz2 movlw 0x20 ;change preceeding "0's" to "space" call lcd_out incf FSR,f decfsz count1,f goto hz1 goto hz3 hz2 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto hz2 hz3 movf INDF,w call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x48 ;"H" call lcd_out movlw 0x7A ;"z" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out goto hz ; shortg movlw 0x14 ;0.1 sec gate movwf gate call count call tenx ;multiply count by ten clrw ;check whether cnt4 is zero xorwf cnt4,w btfsc STATUS,z goto mhz ;was zero - use longer gate period call cnvt ;convert binary to BCD movlw 0x30 ;test if "0" xorwf digs,w btfss STATUS,z goto shg1 movlw 0x30 ;test if "0" xorwf digs+1,w btfsc STATUS,z goto mhz shg1 movlw 0x81 ;set display address call inst movlw 0x03 ;output first 3 characters movwf count1 movlw 0x0C ;MSD of freq, i.e. digs movwf FSR shg2 movlw 0x30 ;test if "0" xorwf INDF,w btfss STATUS,z goto shg3 movlw 0x20 ;change preceeding "0's" to "space" call lcd_out incf FSR,f decfsz count1,f goto shg2 goto shg4 shg3 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto shg3 shg4 movlw 0x2E ;"." call lcd_out movlw 0x05 ;output last 5 characters movwf count1 shg5 movf INDF,w call lcd_out incf FSR,f decfsz count1,f goto shg5 movlw 0x20 ;"space" call lcd_out movlw 0x4D ;"M" call lcd_out movlw 0x48 ;"H" call lcd_out movlw 0x7A ;"z" call lcd_out movlw 0x20 ;"space" call lcd_out movlw 0x20 ;"space" call lcd_out goto shortg ; ; This subroutine multiplies the count in cnt1-3 by 10 (called from the 0.1 sec ; gate routine) and puts any overflow into cnt4. This is to check whether the frequency ; has gone back under 16,777,216 and whether the gate period should be increased to 1 sec. ; tenx clrf cnt4 ;clear the overflow register and movf cnt1,w ;fill calc1-3 with current cnt1-3 values movwf calc1 ;it is assumed that cnt4 cannot be non-zero movf cnt2,w ;when the gate period is only 0.1 sec movwf calc2 ; movf cnt3,w ; movwf calc3 ; bcf STATUS,c rlf calc1,f ;multiply by 2 and carry through rlf calc2,f rlf calc3,f btfsc STATUS,c incf cnt4,f movf calc1,w ;temporarily store the times 2 value movwf temp1 movf calc2,w movwf temp2 movf calc3,w movwf temp3 bcf STATUS,c rlf calc1,f ;multiply by 2 twice and check for carry each time rlf calc2,f rlf calc3,f btfsc STATUS,c incf cnt4,f bcf STATUS,c rlf calc1,f rlf calc2,f rlf calc3,f btfsc STATUS,c incf cnt4,f movf temp1,w ;now add the x2 and x8 values for x10 total addwf calc1,f btfsc STATUS,c incf calc2, f btfsc STATUS,z incf calc3,f btfsc STATUS,z incf cnt4,f movf temp2,w addwf calc2,f btfsc STATUS,c incf calc3,f btfsc STATUS,z incf cnt4,f movf temp3,w addwf calc3,f btfsc STATUS,c incf cnt4,f return ; end