;*******************************************************************
;                          FMETER.ASM
;             4 digit 0-9999 kHz frequency-meter 
;           made by Dr. Dmitry Albov, Laboratory of Structural Chemistry,
;			Moscow State University
;       	e-mail: dmitryalbov@mail.ru
;           
;*******************************************************************
;
processor 16f84a				;for Microchip MPLAB
#include p16f84a.inc
        LIST    p=16F84A
		__config _HS_OSC & _WDT_OFF & _PWRTE_ON
;
;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-bit counter
trisa   equ     0x5     ;port "a" direction register
trisb   equ     0x6     ;port "b" direction register
intcon  equ     0x0B    ;interrupt control register
;
;general registers:
H_byte  equ     0x10      ;high byte of measured frequency
L_byte  equ     0x11      ;low byte of measured frequency
count_r equ     0x12      ;refresh counter
U       equ     0x13      ;units 
D       equ     0x14      ;tens 
H       equ     0x15      ;hundreds 
M       equ     0x16      ;thousands
count1  equ     0x17      ;counter register 
N       equ     0x18      ;general register
;
;***************************  MAIN PROGRAM  
	org     0
start        
        movlw   0x27
        option                  ;load 00100111 in option register 
	movlw	0x10
        movwf   intcon      
	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   U      		;to test the displays
	movwf   D
	movwf   H
	movwf   M
	movlw   .125             ;test refresh 0.5 sec
        movwf   count_r         
loop1   call    refresh          ;refresh of the displays 
	decfsz  count_r
	goto    loop1
work	clrf    tmr		;clear timer
	clrwdt             	;clear prescaler
        bsf     status,5
        movlw   b'10000000'	; portB:7 - input, enable count
        movwf   trisb      	;- 
        bcf     status,5     	; |
        movlw   .99          	; |
	movwf   count1          ; |
ms1	nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |
        nop                     ; |- This is 1 millisecond measuring time for 4.000 MHz crystal.
	nop                     ; |- DO NOT CHANGE THIS BLOCK until you need calibration!
	decfsz  count1          ; |
        goto	ms1        	; |
        nop                     ; | 
        nop                     ; |
	nop                     ; |
	nop                     ; |If you need calibration, add or remove one or more NOPs here.
	nop			; |
	nop                     ; |
        bsf     status,5    	; |        
        clrf    trisb           ;- PortB - output, disable count
        bcf     status,5            
        call    prescaler       ;put the counted number into H_byte:L_byte 
	call    refresh   	;We need to refresh the display between measuring period and long calculations to reduce blinking
	clrf	M		;clear display registers
	clrf	H
	clrf	D
	clrf	U            
	call	bin2bcd		;convert binary H_byte:L_byte to binary coded decimal M:H:D:U, 4 digits
	call    adjust		;decimal adjust after conversion
        movlw   .125             
        movwf   count_r         
loopr   call    refresh         ;call refresh 125 times - it is about 0.5 sec
        decfsz  count_r         
        goto    loopr           
	goto	work		;work FOREVER! (or until power off...)

;
; *****************************     Delay subroutine 
delay
        movlw   .100            ; -
	movwf   count1          ; |
d1	nop                     ; |
	nop                     ; |
	nop                     ; |
	nop                     ; |-about 1 millisecond delay, we do not need exact time here
	nop                     ; |
        nop                     ; |  
	nop                     ; |
	decfsz  count1          ; |
        goto	d1        	; |
        return                  ; -
; ********************************************* BCD -> 7-segment table translation
segments addwf      pc          ; W + PC -> PC
        retlw      b'00111111' ; ..FEDCBA = '0'
        retlw      b'00000110' ; .....CB. = '1'
        retlw      b'01011011' ; .G.ED.BA = '2'
        retlw      b'01001111' ; .G..DCBA = '3'
        retlw      b'01100110' ; .GF..CB. = '4'
        retlw      b'01101101' ; .GF.DC.A = '5'
        retlw      b'01111101' ; .GFEDC.A = '6'
        retlw      b'00000111' ; .....CBA = '7'
        retlw      b'01111111' ; .GFEDCBA = '8'
        retlw      b'01101111' ; .GF.DCBA = '9'
; ***************************** display refresh subroutine:  250 mSec
refresh movfw    M			;display thousands
	call    segments
	movwf	portb
        bsf     porta,0         
	call    delay
        bcf     porta,0        
	movfw    H			;display hundreds
	call    segments
	movwf	portb
        bsf     porta,1        
	call    delay
        bcf     porta,1         
	movfw    D			;display tens
	call    segments
	movwf	portb
        bsf     porta,2       
	call    delay
        bcf     porta,2       
	movfw    U			;display units
	call    segments
	movwf	portb
        bsf     porta,3       
	call    delay
        bcf     porta,3       
	return

; ************** subroutine to extract the value contained in prescaler. Frequency=H_byte:L_byte=tmr:prescaler

prescaler movfw    tmr
        movwf   H_byte          ;save the counter into H_byte
        clrf    N               
p1	bcf     portb,7
        bsf     portb,7
        bcf     portb,7         ;give an edge to the input controller
        incf    N               
        movfw   H_byte     	;make a copy of H_byte into W
        xorwf   tmr,0        	;check if tmr has been changed 
        btfsc   status,2       
        goto    p1		;if NOT, repeat 
        movlw   0xFF
        movwf   L_byte          
	movfw   N             	; N = how many pulses were needed to complete prescaler to 256
        subwf   L_byte          ;-|__ L_byte=256-N
	incf    L_byte          ;-|    
	return

; ****************************** BIN -> BCD conversion		H_byte:L_byte -> M:H:D:U

bin2bcd	btfss   H_byte,5        ;check bit5 of H_byte
	goto    b1
	movlw   0x08
	addwf   M
	movlw   0x01
    	addwf   H
	movlw   0x09
	addwf   D
	movlw   0x02
        addwf   U               ;if bit5=1 then add 8192
b1      btfss   H_byte,4        ;check bit4 of H_byte
	goto    b2
	movlw   0x04
	addwf   M
	movlw   0x09
	addwf   D
	movlw   0x06
        addwf   U               ;if bit4=1 then add 4096
b2      btfss   H_byte,3        ;check bit3 of H_byte
	goto    b3
	movlw   0x02
	addwf   M
	movlw   0x04
	addwf   D
	movlw   0x08
        addwf   U               ;if bit3=1 then add 2048
b3      btfss   H_byte,2        ;check bit2 of H_byte
	goto    b4
	movlw   0x01
	addwf   M
	movlw   0x02
	addwf   D
	movlw   0x04
        addwf   U               ;if bit2=1 then add 1024
b4      btfss   H_byte,1        ;check bit1 of H_byte
	goto    b5
	movlw   0x05
        addwf   H
	movlw   0x01
	addwf   D
	movlw   0x02
        addwf   U               ;if bit1=1 then add 512
b5      btfss   H_byte,0        ;check bit0 of H_byte
	goto    b6
	movlw   0x02
	addwf   H
	movlw   0x05
	addwf   D
	movlw   0x06
        addwf   U               ;if bit0=1 then add 256
b6      btfss   L_byte,7        ;check bit7 of L_byte
	goto    b7
	movlw   0x01
        addwf   H
	movlw   0x02
	addwf   D
	movlw   0x08
        addwf   U               ;if bit7=1 then add 128
b7      btfss   L_byte,6        ;check bit6 of L_byte
	goto    b8
	movlw   0x06
	addwf   D
	movlw   0x04
        addwf   U               ;if bit6=1 then add 64
b8      btfss   L_byte,5        ;check bit5 of L_byte
	goto    b9
	movlw   0x03
	addwf   D
	movlw   0x02
        addwf   U               ;if bit5=1 then add 32
b9      btfss   L_byte,4        ;check bit4 of L_byte
	goto    b10
	movlw   0x01
	addwf   D
	movlw   0x06
        addwf   U               ;if bit4=1 then add 16
b10     btfss   L_byte,3        ;check bit3 of L_byte
	goto    b11
	movlw   0x08
        addwf   U               ;if bit3=1 then add 8
b11     btfss   L_byte,2        ;check bit2 of L_byte
	goto    b12
	movlw   0x04
        addwf   U               ;if bit2=1 then add 4
b12     btfss   L_byte,1        ;check bit1 of L_byte
	goto    b13
	movlw   0x02
        addwf   U               ;if bit1=1 then add 2
b13     btfss   L_byte,0        ;check bit0 of L_byte
	goto    b14
	movlw   0x01
        addwf   U               ;if bit0=1 then add 1
b14     return

; ****************************** BCD ajust after conversion

adjust	movfw	U
	movwf	N				;make a copy of U in N
	movlw	0x0A			
	subwf	N,1				;test if U<10
	btfss	status,0
	goto	a1				;if U<10 then adjust is not needed, go to next digit
	movlw	0x0A			;if U>10 then U=U-10 and D=D+1 
	subwf	U,1				
	incf	D,1				
	goto	adjust			;repeat 
a1	movfw	D
	movwf	N				;make a copy of D in N
	movlw	0x0A			
	subwf	N,1				;test if D<10
	btfss	status,0
	goto	a2				;if D<10 then adjust is not needed, go to next digit
	subwf	D,1				;if D>10 then D=D-10 and H=H+1 			
	incf	H,1				
	goto a1					;repeat
a2	movfw	H
	movwf	N				;make a copy of H in N
	movlw	0x0A			
	subwf	N,1				;test if H<10
	btfss	status,0
	goto	a3				;if H<10 then adjust is not needed, go to next digit
	subwf	H,1				;if H>10 then H=H-10 and M=M+1 				
	incf	M,1				
	goto a2					;repeat
a3	return

     END
;----------------

