TITLE   "Frequency Counter: Measures the frequency of a TTL waveform between 1 & 9999"
LIST    p=PIC16F84

include "d:\mplab\p16f84.inc"

ctrval equ D'250'                     
;loading the counter with 250 (TMR0*Prescaler=4ms =>total 1ms)
hlfctr equ D'125'                     
;loading the counter with 125 (TMR0*Prescaler=4ms =>total 0.5ms)

;*****************************************************************************************************
;Needed Macros
;*****************************************************************************************************


load    macro   register, data        
;loads a register by a certain value
        movlw   data
        movwf   register
        endm

movff   macro   register1, register2  
;moves the value in register1 to register2
        movf    register1, w
        movwf   register2
        endm

clrfE   macro   register              
;clear a 16-bit register using the most significant register
        clrf    register
        clrf    register+1
        endm

incfE   macro   register              
;increment a 16-bit register using the most significant register
        movlw   D'01'
        addwf   register+1, F
        btfsc   STATUS, C
        incf    register,F
        endm

movffE  macro   register1E, register2E
;moves the value in a 16-bit register1E to a 16-bit register-2
        movff   register1E, register2E
        movff   register1E+1, register2E+1
        endm

divide  macro   nibbles, msregister   
;divide a register into 2 other registers
        movf    nibbles, W            
;=> put each nibble in a register
        andlw   H'0F'
        movwf   msregister+1
        swapf   nibbles, W
        andlw   H'0F'
        movwf   msregister
        endm


cblock  H'0C'
   frequency:2, temp:2, counter, digits:4, Qstates, flags, bcd1, bcd2, bcd3, ctr
endc

org     H'00'
goto    setup

org     H'04'
goto    isr

table   addwf PCL, F
        dt H'FC', H'60', H'DA', H'F2', H'66'
        dt H'B6', H'BE', H'E0', H'FE', H'F6'
        dt H'00', H'CF', H'B7'        
;10 for blank,11 for letter'P',12 for letter 'S'

;*********************************************************************************************************
; System Initialization
;*********************************************************************************************************


setup   bsf   STATUS, RP0             
;select Bank 1
        load  TRISB, B'00000001'      
;RB7-RB6 output pins for 7-segment display,RB0\Int used for ext int
        load  TRISA, B'11110000'      
;RA4~RA0 output pins for Qstates
        load  OPTION_REG, B'11010100' 
;prescaler=32, positive edge interrupt
        bcf   STATUS, RP0             
;select Bank 0
        load  FSR, digits             
;FSR pointing to 1st digit
        load  Qstates, B'00001000'    
;1st digit turned on
        load  counter, ctrval         
;counter loaded by 250 for 1ms
        clrf  flags                   
;clear used registers
        clrfE frequency
        clrfE temp
        clrfE digits
        clrfE digits+2
        call  BinBCDE
        load  TMR0, -D'125'           
;Timer0 loaded by 125 => *prescaler= 4ms
        bsf   INTCON, T0IE           
;enable Timer0 interrupt
        bsf   INTCON, INTE            
;enable external interrupt
        bsf   INTCON, GIE             
;enable global interrupt
        goto  $

;*********************************************************************************************************
; Interrupt Service routine
;*********************************************************************************************************


isr     btfss INTCON, T0IF            
;check the kind of interrupt (TMR0 or external)
        goto  extint
        bcf   INTCON, T0IF            
;TMR0 interrupt=> clr interrupt flag
        load  TMR0, -D'125'           
;reload TMR0
        movff Qstates, PORTA         
;write to ports (displaying 1 digit)
        movf  INDF, W
        call  table
        movwf PORTB

        rrf   Qstates, F              
;moving to next digit to the right
        incf  FSR, F                  
;FSR pointing to next digit to the right
        movlw digits+4                
;checking if no more digits to the right
        subwf FSR, W                  
;=> 4 digits has been displayed
        btfss STATUS, Z               
;=> return to the 1st digit
        goto  testctr
        load  FSR,digits
        load  Qstates, B'00001000'

testctr movlw hlfctr                   ;check if 0.5 sec elapsed if so call error2
        subwf counter, W
        btfsc STATUS, Z
        call  error2
        decfsz counter, F             
;decrement counter and check if counter=0
        retfie
        bcf   flags,0                 
;counter=0 => update frequency =>clear old flags
        movffE temp, frequency        
;update new frequency
        clrfE temp                    
;clear temp to start a new count of edges
        call  testE                   
;test if frequency out of range
        btfsc flags, 0
        goto  error1                  
;frequency out of range => call error1
        call  BinBCDE                 
;valid frequency => call Bin2bcdE
        load  counter, ctrval         
;pointing to first new digit
        load  FSR, digits
        load  Qstates, B'00001000'
        retfie

error1  load  digits, D'00'            ;frequency out of range
        load  digits+1, D'00'         
;=> load display with word "OOPS" for 0.5 sec
        load  digits+2, D'11'
        load  digits+3, D'12'
        load  counter, ctrval
        load  FSR, digits
        load  Qstates, B'00001000'
        retfie
error2  btfss flags, 0                
;0.5 sec elapsed
        return                        
;if freq in range => no change =>return
        load  digits, D'10'           
;if freq out of range => shut down display for next 0.5 sec to flash
        load  digits+1, D'10'
        load  digits+2, D'10'
        load  digits+3, D'10'
        load  FSR, digits
        load  Qstates, B'00001000'
        return
extint  bcf   INTCON, INTF            
;external interrupt => clear interrupt flag
        movlw H'FF'                   
;check if temp:2 (16bit) reached maximum value (2^16)
        subwf temp, W                 
;if temp:2 ='FFFF' no increment
        btfss STATUS, Z
        goto  inc
        movlw H'FF'
        subwf temp+1, W
        btfss STATUS, Z
inc     incfE temp                    
;else increment temp:2
        retfie

testE   movlw B'00100111'             
;check if value in frequency > 9999 (out of range)
        subwf frequency, W            
;=> set bit0 in flags to 1
        btfss STATUS, C
        return
        btfss STATUS, Z
        goto  greater
        movlw B'00010000'
        subwf frequency+1, W
        btfss STATUS, C
        return
greater bsf   flags, 0
        return

;********************************************************************************************************
; Converts a 16-bit binary number into 4 BCD numbers stored at digits (most signifant),        digits+1,digits+2,digits+3
*********************************************************************************************************


BinBCDE movlw  .16
        movwf  ctr
        clrf   bcd1
        clrf   bcd2
        clrf   bcd3
        goto   go

adjdec  movlw  0x33
        addwf  bcd1, F
        addwf  bcd2, F
        addwf  bcd3, F
        movlw  0x03
        btfss  bcd1, 3
        subwf  bcd1, F
        btfss  bcd2, 3 
        subwf  bcd2, F
        btfss  bcd3, 3
        subwf  bcd3, F
        movlw  0x30
        btfss  bcd1, 7
        subwf  bcd1, F
        btfss  bcd2, 7
        subwf  bcd2, F
        btfss  bcd3, 7
        subwf  bcd3, F

go      rlf    frequency+1, F
        rlf    frequency, F
        rlf    bcd3, F
        rlf    bcd2, F
        rlf    bcd1, F
        decfsz ctr, F
        goto   adjdec
        divide bcd3, digits+2
        divide bcd2, digits
        return

        end