;*******************************************************************
;                          FMETER.ASM
;             4 digits auto-ranging frequency-meter 
;           made by Simone Benvenuti & Andrea Geniola
;       e-mail: simone.benvenuti@studenti.ing.unipi.it
;               andrea.geniola@studenti.ing.unipi.it
;*******************************************************************
;
; A frequency counter which can read frequencies from 0Hz to 50MHz is
; here implemented using a PIC microcontroller. The basic hardware for
; the measurement circuit is shown below: the measure result can be read
; on 4 seven-segments displays in the engeneering notation (A.BC * 10^D).
; The displays are driven using only 11 output pins and 4 transistors;
; during a refresh cycle each digit is turned on for 62.5 usec
; The imput frequency is "gated" for a precise duration of time. The gate
; is implemented in software as an accurate delay. In order to minimize
; the error the gate is 1msec wide if the frequency is above 256KHz,
; otherwise is 1msec wide. If the frequency is below 128Hz the digits
; blink to warn that the error can be greater than the device resolution.
; To minimize the energy consumption the PIC turns in sleep mode if no
; input signal is detected for 10sec (this period is software tunable),
; but it wake-up itself immediately when the input is stimulated. 
; The program is written for the PIC16C84 but cheaper PICs (as PIC16C61)
; can be used.
;
;                        ________________
;             cents gnd -| RA2      RA1 |- tenths gnd       
;             esp   gnd -| RA3      RA0 |- units gnd       
; input -/\/\/\/\--+-----| RA4/TOCKI    |-                    ___a___
;         470ohm   |    -|              |- 4 MHz cristal      |     |
;                  | gnd-|              |- VDD 3..5 Volt     f|     |b
;   input control  +-----| RB0      Rb7 |- "g" segment        |  g  |
;          "a" segment  -| RB1      RB6 |- "f" segment        |-----|
;          "b" segment  -| RB2      RB5 |- "e" segment       e|     |c
;          "c" segment  -| RB3      RB4 |- "d" segment        |  d  |
;                        ----------------                     -------
;*******************************************************************
;
        LIST    p=16C84, wdte=0 ; PIC16C84 is the target processor
;
;status registers:
pc	equ	02	;program counter
porta   equ     05      ;I/O register
portb   equ     06      ;I/O register
status  equ     03      ;status register
tmr     equ     01      ;8 bits counter
trisa   equ     0x5     ;port "a" direction register
trisb   equ     0x6     ;port "b" direction register
intcon  equ     0x0B    ;interrupt control register
;
;general registers:
unita   equ     0x0C      ;display units  
decimi  equ     0x1D      ;display tenths  
cents   equ     0x1E      ;display cents  
esp     equ     0x1F      ;display exponent  
H_byte  equ     0x10      ;high_byte of the read number (N)
L_byte  equ     0x11      ;low_byte of the read number (N)
conta_r equ     0x12      ;refresh counter
cifra   equ     0x13      ;parameter
U       equ     0x14      ;units 
D       equ     0x15      ;tens 
H       equ     0x16      ;hundreds 
M       equ     0x17      ;thousands
DM      equ     0x18      ;tens of thousands
CM      equ     0x19      ;hundreds of thousands
conta1  equ     0x1A      :first counter register 
conta2  equ     0x1B      ;second counter register 
N       equ     0x1C      ;general register
zeri    equ     0x20      ;number of cycles without signal 
;
;***************************  MAIN PROGRAM  
	org     0
start        
        movlw   0x27
        option                  ;load 00100111 in option register 
	movlw	0x10
        movwf   intcon          ;enable interrupts 
	movlw	.20
        movwf   zeri             
	clrf    porta
        clrf    portb           
        bsf     status,5        
	movlw   0x10
        movwf   trisa           ;porta<0_3>=output, porta<4>=input 
        clrf    trisb           ;potrb<0_7>=output
        bcf     status,5        
        movlw   0x08            ;all the segments are switched on
        movwf   unita           ;to test the displays
	movwf   decimi
	movwf   cents
	movwf   esp
        movlw   .20             ;test refresh (0.5sec wide)
        movwf   conta_r         
loop1   call    refresh         ;Refresh of the displays 
	decfsz  conta_r
	goto    loop1
loop5   clrf    tmr             
        bsf     status,5
        movlw   0x01
        movwf   trisb           ;- 
        bcf     status,5        ; |
        movlw   .99             ; |
	movwf   conta1          ; |
un_ms   nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |
        nop                     ; |-  1msec delay 
	nop                     ; |
	decfsz  conta1          ; |
        goto    un_ms           ; |
        nop                     ; | 
        nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |
        bsf     status,5        ; |        
        clrf    trisb           ;- 
        bcf     status,5            
        call    prescaler       ;put the counted number into H_byte-L_byte 
        movf    H_byte          
	btfss   status,2        ;test: H_byte=0
        goto    cal1_ms         ;no
        btfsc   L_byte,7        ;yes, then test if L_byte<=128
        goto    cal1_ms         ;no
        clrf    tmr             ;yes
        bsf     status,5
	movlw   0x01
        movwf   trisb           ;- 
        bcf     status,5        ; |
        movlw   .20             ; |
        movwf   conta_r         ; |
loopr   call    refresh         ; |
        decfsz  conta_r         ; |
        goto    loopr           ; |
        movlw   .4              ; |- 0.5 sec delay ("refresh" is called 
        movwf   N               ; |                 20 times)
wait    decfsz  N               ; |
        goto    wait            ; |
        nop                     ; |
        nop                     ; |
        bsf     status,5        ; |
        clrf    trisb           ;-
        bcf     status,5        ;     
        call    prescaler       ;put the counted number into H_byte-L_byte
        movf    H_byte          ;
        btfss   status,2        ;test if H_byte=0
	goto    cal05           ;NO
        btfsc   L_byte,7        ;yes, then test if L_byte<=127
	goto    cal05           ;NO
	movf	L_byte		
	btfss	status,2	
	goto 	lamp
        clrf    esp             ;no input signal is detected
        clrf    unita           ;clear all the displays and show
        clrf    decimi          ;zero without blinking for "zeri" times
	clrf	cents
	decfsz	zeri
	goto	loop5
	bcf	intcon,1
	sleep
	goto	loop1
lamp    movlw   0x14            ;blinking mode  
        movwf   N               
giro    call    ritardo         
	decfsz	N
	goto	giro	
	goto 	cal05
;
cal1_ms clrf    U               ;put the right values into the 4 displays
        clrf    D               ;registers and add 3 to the exponent 
        clrf    H               ;to multiply by one thousand 
        clrf    M               
	clrf    DM
	clrf    CM
	movlw   0x03
	movwf   esp
	call    calcolo
	movlw   .20
	movwf   conta_r
	goto    loop1
;
cal05   clrf    U               ;multiply the number by 2 and put the
        clrf    D               ;right values into the 4 displays regs
        clrf    H                
	clrf    M
	clrf    DM
	clrf    CM
	btfsc   H_byte,7
	call    sessant
	bcf     status,0
	rlf     L_byte
	rlf     H_byte
        clrf    esp
        call    calcolo
	goto    loop5
;
;
; *****************************     Delay subroutine 
ritardo movlw   .8              ; -     
        movwf   conta1          ;  |
        nop                     ;  |
beta    movlw   .0              ;  |
        movwf   conta2          ;  |_ 62.5 microsec delay
alfa    decfsz  conta2          ;  |
	goto    alfa            ;  |
        nop   			;  |
        decfsz  conta1          ;  |
	goto    beta            ;  |
	movlw	.14		;  |
	movwf	conta2		;  |
gamma	decfsz	conta2		;  |
	goto	gamma		;  |
	nop			;  |
	nop        		;  |
        return                  ; -
; *****************************  choice of the right segments
segmenti	nop
        movf    cifra,0
        addwf   pc              ;return into W the right set of segments
        retlw   0x7E            ;to be ligthed for each digit
	retlw	0x0C
	retlw	0xB6
	retlw	0x9E
	retlw	0xCC
	retlw	0xDA             
	retlw	0xFA
	retlw	0x0E
	retlw	0xFE
	retlw	0xDE
;
; ***************************** refresh subroutine:  250 mSec
refresh movf    unita,0
        movwf   cifra          
	call    segmenti
	movwf	portb
        bsf     porta,0         
	call    ritardo
        bcf     porta,0        
	movf    decimi,0
        movwf   cifra           
	call    segmenti
	movwf	portb
        bsf     porta,1        
	call    ritardo
        bcf     porta,1         
	movf    cents,0
        movwf   cifra          
	call    segmenti
	movwf	portb
        bsf     porta,2       
	call    ritardo
        bcf     porta,2       
	movf    esp,0
        movwf   cifra          
	call    segmenti
	movwf	portb
        bsf     porta,3       
	call    ritardo
        bcf     porta,3       
	return
; ************** subroutine to extract the value contained in prescaler
prescaler       movf    tmr,0
        movwf   H_byte          ;make a copy of the counter into H_byte
        clrf    N               
ciclo   bcf     portb,0
        bsf     portb,0
        bcf     portb,0         ;give un edge to the input controller
        incf    N               
        movf    H_byte,0        ;make a copy of H_byte into W
        xorwf   tmr,0           ;control if tmr is changed (tmr=H_byte)
        btfsc   status,2        
        goto    ciclo
        movlw   0xFF
        movwf   L_byte          
	movf    N,0             
        subwf   L_byte          ;-|__ L_byte=256-N
	incf    L_byte          ;-|    
	return
; ****************************** frequency calculation subroutine
calcolo movlw	.20
	movwf	zeri
	rlf     H_byte
        btfss   status,0        ;check bit7 of H_byte
	goto    c_2
	movlw   0x03
	addwf   DM
	movlw   0x02
	addwf   M
	movlw   0x07
        addwf   H
	movlw   0x06
	addwf   D
	movlw   0x08
        addwf   U               ;if bit7=1 then add 32768
c_2     rlf     H_byte
        btfss   status,0        ;check bit6 of H_byte
	goto    c_3
	movlw   0x01
	addwf   DM
	movlw   0x06
	addwf   M
	movlw   0x03
        addwf   H
	movlw   0x08
	addwf   D
	movlw   0x04
        addwf   U               ;if bit6=1 then add 16384
c_3     rlf     H_byte
        btfss   status,0        ;check bit5 of H_byte
	goto    c_4
	movlw   0x08
	addwf   M
	movlw   0x01
        addwf   H
	movlw   0x09
	addwf   D
	movlw   0x02
        addwf   U               ;if bit5=1 then add 8192
c_4     rlf     H_byte
        btfss   status,0        ;check bit4 of H_byte
	goto    c_5
	movlw   0x04
	addwf   M
	movlw   0x09
	addwf   D
	movlw   0x06
        addwf   U               ;if bit4=1 then add 4096
c_5     rlf     H_byte
        btfss   status,0        ;check bit3 of H_byte
	goto    c_6
	movlw   0x02
	addwf   M
	movlw   0x04
	addwf   D
	movlw   0x08
        addwf   U               ;if bit3=1 then add 2048
c_6     rlf     H_byte
        btfss   status,0        ;check bit2 of H_byte
	goto    c_7
	movlw   0x01
	addwf   M
	movlw   0x02
	addwf   D
	movlw   0x04
        addwf   U               ;if bit2=1 then add 1024
c_7     rlf     H_byte
        btfss   status,0        ;check bit1 of H_byte
	goto    c_8
	movlw   0x05
        addwf   H
	movlw   0x01
	addwf   D
	movlw   0x02
        addwf   U               ;if bit1=1 then add 512
c_8     rlf     H_byte
        btfss   status,0        ;check bit0 of H_byte
	goto    c_9
	movlw   0x02
        addwf   H
	movlw   0x05
	addwf   D
	movlw   0x06
        addwf   U               ;if bit0=1 then add 256
c_9     rlf     L_byte
        btfss   status,0        ;check bit7 of L_byte
	goto    c_10
	movlw   0x01
        addwf   H
	movlw   0x02
	addwf   D
	movlw   0x08
        addwf   U               ;if bit7=1 then add 128
c_10    rlf     L_byte
        btfss   status,0        ;check bit6 of L_byte
	goto    c_11
	movlw   0x06
	addwf   D
	movlw   0x04
        addwf   U               ;if bit6=1 then add 64
c_11    rlf     L_byte
        btfss   status,0        ;check bit5 of L_byte
	goto    c_12
	movlw   0x03
	addwf   D
	movlw   0x02
        addwf   U               ;if bit5=1 then add 32
c_12    rlf     L_byte
        btfss   status,0        ;check bit4 of L_byte
	goto    c_13
	movlw   0x01
	addwf   D
	movlw   0x06
        addwf   U               ;if bit4=1 then add 16
c_13    rlf     L_byte
        btfss   status,0        ;check bit3 of L_byte
	goto    c_14
	movlw   0x08
        addwf   U               ;if bit3=1 then add 8
c_14    rlf     L_byte
        btfss   status,0        ;check bit2 of L_byte
	goto    c_15
	movlw   0x04
        addwf   U               ;if bit2=1 then add 4
c_15    rlf     L_byte
        btfss   status,0        ;check bit1 of L_byte
	goto    c_16
	movlw   0x02
        addwf   U               ;if bit1=1 then add 2
c_16    rlf     L_byte
        btfss   status,0        ;check bit0 of L_byte
	goto    c_17
	movlw   0x01
        addwf   U               ;if bit0=1 then add 1
c_17    call    riporti
        movf    CM,0            ;the 1st significant digit (MSD) is searched
        btfss   status,2        ;and a 4th significant digit approximation 
        goto    appcmnz         ;is made
        movf    DM,0            
        btfss   status,2
        goto    appdmnz
        movf    M,0            
        btfsc   status,2
        goto    fuori           
        movf    U,0            
        movwf   N
        movlw   0x05
	subwf   N
        btfss   status,0
        goto    fuori
        incf    D
        goto    fuori
appdmnz movf    D,0             ; DM is the MSD
        movwf   N
        movlw   0x05
	subwf   N
        btfss   status,0
        goto    fuori
        incf    H
        goto    fuori
appcmnz movf    H,0             ;CM is the MSD
        movwf   N
        movlw   0x05
	subwf   N
        btfss   status,0
        goto    fuori
        incf    M
fuori   call    riporti
        movf    CM,0            ;put the 4th significant digit into
        btfss   status,2        ;the 4 display registers
        goto    cmnz            ;and put into esp the rigth value
        movf    DM,0            
	btfss   status,2
	goto    dmnz
	movf    M,0
	btfss   status,2
        goto    mnz             
        movlw   0x02            ;U is the MSD
	addwf   esp             
        movf    H,0
	movwf   unita
	movf    D,0
	movwf   decimi
	movf    U,0
	movwf   cents
        goto    esci
mnz     movlw   0x03            ;M is the MSD
	addwf   esp             
	movf    M,0
	movwf   unita
        movf    H,0
	movwf   decimi
	movf    D,0
	movwf   cents
        goto    esci
dmnz    movlw   0x04            ;DM is the MSD
	addwf   esp             
	movf    DM,0
	movwf   unita
	movf    M,0
	movwf   decimi
        movf    H,0
	movwf   cents
        goto    esci
cmnz    movlw   0x05            ;CM is the MSD
	addwf   esp             
	movf    CM,0
	movwf   unita
	movf    DM,0
	movwf   decimi
	movf    M,0
	movwf   cents
esci    return
; ****************************** carry subroutine
riporti movf    U,0
	movwf   N
ripu    movf    N,0             ;check if units>10
	movwf   U
	incf    D
	movlw   0x0A
	subwf   N
        btfsc   status,0
	goto    ripu
	decf    D
	movf    D,0
	movwf   N
ripd    movf    N,0             ;check if tens>10
	movwf   D
        incf    H
	movlw   0x0A
	subwf   N
        btfsc   status,0
	goto    ripd
        decf    H
        movf    H,0
	movwf   N
ripc    movf    N,0             ;check if hundreds>10
        movwf   H
	incf    M
	movlw   0x0A
	subwf   N
        btfsc   status,0
	goto    ripc
	decf    M
	movf    M,0
	movwf   N
ripm    movf    N,0             ;check if thousands>10
	movwf   M
	incf    DM
	movlw   0x0A
	subwf   N
        btfsc   status,0
	goto    ripm
	decf    DM
	movf    DM,0
	movwf   N
ripdm   movf    N,0             ;check if tens of thousands>10
	movwf   DM
	incf    CM
	movlw   0x0A
	subwf   N
        btfsc   status,0
	goto    ripdm
	decf    CM
	return
; ****************************** subroutine to add 64K
sessant movlw   0x06
	addwf   DM
	movlw   0x05
	addwf   M
	movlw   0x05
        addwf   H
	movlw   0x03
	addwf   D
	movlw   0x06
        addwf   U               ;add 65536
	return
     END
;----------------
