; fcount.asm ; ; Code to the frequency counter project by Robert Östling ; http://www.robos.org/ ; ; Ideas "borrowed" from a project by Simone Benvenuti and ; Andrea Geniola. ; ; Clock speed: 3,686,400 Hz ; Instructions per second: 921,600 ; ; One sample can contain up to 2^16 cycles, with a maximum ; frequency of about 16MHz (2^24Hz) one sample should take ; 1/256 second or 3600 instruction cycles. Sampling is done ; in a 240*15 instruction loop, during which portb<0> is ; configured as an high-impedance input pin, allowing ; porta<4> to detect the signal. The rest of the time, ; portb<0> is a low-impedance output pin, essentialy ; grounding porta<4> ; ; If the frequency turns out to be below 2^16 Hz, the sample ; time is increased to 1 second, giving a 1Hz resolution. If ; the frequency is between 2^16 and 2^20 Hz, 1/16 second ; samples are used, giving a 16Hz resolution. These different ; modes can be recognized by the blinking speed of the display. ; In 2^24Hz mode, there is no blinking. In 2^20Hz mode, the ; frequency is 1Hz and in 2^16Hz mode it is 0.5Hz. ; ; portb<1-7> are connected to the LED segments as follows: ; ; 1 ; ___ ; 6 | | 2 ; |___| ; 5 | 7 | 3 ; |___| ; ; 4 ; ; Pin 0 of portb is connected directly to pin 4 of porta, ; this pair of pins is connected to the input signal via ; a 470R resistor. ; ; porta<0-3> bits control which LED display that is active. ; Bit 3 controls the LSD and bit 0 the MSD. This is all due ; to a constructing error, originally bit 0 controlled the ; LSD. A _high_ level on the pin activates the segment in ; question. list p=pic16f84a #include __config _cp_off & _pwrte_on & _wdt_off & _xt_osc cblock 0x0c cnt,cnt2,clow,chigh,dispcnt endc org 0 reset: clrf intcon ; disable all interrupts clrf porta clrf portb ; set all outputs to 0 bsf status,rp0 ; bank 1 movlw 0x27 ; external timer with prescaler 256 movwf option_reg ; trigger on rising edge movlw 0x10 movwf trisa ; porta<0-3> output, porta<4> input clrf trisb ; portb<0-7> output bcf status,rp0 ; bank 0 main_loop: call sample ; count the number of cycles in 1/256 second call cycles ; move this number to chigh:clow movfw chigh andlw 0xff ; is the frequency below 2^16 Hz? btfsc status,z goto do_1sec ; yes, count the number of cycles in 1 second movfw chigh andlw 0xf0 ; is the frequency below 2^20 Hz? btfsc status,z goto do_16sec ; yes, count the number of cycles in 1/16 second do_256sec: call display ; update display goto main_loop do_16sec: call mid_sample ; 1/16s sample call cycles ; move the sample to chigh:clow call delay102399 call delay102399 call delay102399 call delay102399 ; turn off the display for about 450ms call display ; update display goto main_loop do_1sec: call long_sample ; 1s sample call cycles ; move the sample to chigh:clow call display ; update display call display ; hold for a little bit longer (about 1s) goto main_loop sample: clrf tmr0 ; reset timer bsf status,rp0 ; bank 1 movlw 0x01 movwf trisb ; start collecting cycles! bcf status,rp0 ; bank 0 movlw 0xef movwf cnt ; cnt = 239 sample_loop: nop nop nop nop nop nop nop nop nop nop nop nop decfsz cnt goto sample_loop ; 15 cycle loop nop nop nop nop nop nop nop nop nop nop bsf status,rp0 clrf trisb ; another 15 cycles, then stop sampling. bcf status,rp0 return mid_sample: clrf tmr0 ; reset timer bsf status,rp0 ; bank 1 movlw 0x01 movwf trisb bcf status,rp0 ; bank 0, also start sampling movlw 0x2b ; | movwf cnt2 ; | 3 cycles so far mid_sample_loop: call delay1276 ; | nop ; | decfsz cnt2 ; | goto mid_sample_loop ; | 43*1280 cycles call delay1276 ; 1276 cycles call delay1276 ; 1276 cycles nop ; | nop ; | 3 cycles nop ; | bsf status,rp0 ; | 2 more cycles clrf trisb ; stop sampling bcf status,rp0 return delay1276: ; call delay1276 <-- 1 cycle movlw 0xfe ; | movwf cnt ; | 2 cycles delay1276_loop: nop ; | nop ; | decfsz cnt ; | 5*254 cycles goto delay1276_loop ; | nop ; | nop ; | 3 cycles return ; | long_sample: clrf tmr0 ; reset timer bsf status,rp0 ; bank 1 movlw 0x01 movwf trisb bcf status,rp0 ; bank 0, also start sampling call delay102399 call delay102399 call delay102399 call delay102399 call delay102399 call delay102399 call delay102399 call delay102399 call delay102399 nop nop nop nop nop nop bsf status,rp0 clrf trisb ; stop sampling bcf status,rp0 return delay102399: ; call delaydelay102399 <-- 1 cycle movlw 0x63 ; | movwf cnt2 ; | 2 cycles delaydelay102399_loop: call delay1016 ; | nop ; | nop ; | nop ; | nop ; | 99*1024 cycles nop ; | decfsz cnt2 ; | goto delaydelay102399_loop ; | call delay1016 ; 1016 cycles nop ; | nop ; | 3 cycles nop ; | return ; 1 cycle delay1016: ; call delay1016 <-- 1 cycle movlw 0xfd ; | movwf cnt ; | 2 cycles delay1016_loop: nop ; | decfsz cnt ; | 253*4 cycles goto delay1016_loop ; | return ; 1 cycle cycles: movfw tmr0 movwf chigh ; high byte = counter clrf cnt ; will become prescaler count cycles_loop: bcf portb,0 bsf portb,0 bcf portb,0 ; pulse to porta<4> (and increase prescaler) incf cnt movfw chigh xorwf tmr0,w ; did we overflow the prescaler? btfsc status,z goto cycles_loop ; no, send another pulse movlw 0xff movwf clow movfw cnt subwf clow incf clow ; clow = prescaler return display: movlw 0x30 movwf dispcnt ; display 0x30 times (about 500ms) display_again: movfw clow andlw 0x0f ; low 4 bits of byte call digit ; get its hex digit movwf portb ; output to LED segments bsf porta,3 ; enable first digit call delay ; wait 40ms... bcf porta,3 ; then disable it. swapf clow,w andlw 0x0f ; high 4 bits of byte call digit ; get its hex digit movwf portb ; output to LED segments bsf porta,2 ; enable second digit call delay ; wait 40ms... bcf porta,2 ; then disable it. movfw chigh andlw 0x0f ; low 4 bits of byte call digit ; get its hex digit movwf portb ; output to LED segments bsf porta,1 ; enable third digit call delay ; wait 40ms... bcf porta,1 ; then disable it. swapf chigh,w andlw 0x0f ; high 4 bits of byte call digit ; get its hex digit movwf portb ; output to LED segments bsf porta,0 ; enable fourth digit call delay ; wait 40ms... bcf porta,0 ; then disable it. clrf portb ; clear portb decfsz dispcnt goto display_again return ; Runs for about 2.5ms. delay: movlw 0x03 movwf cnt delay_loop1: clrf cnt2 delay_loop2: decfsz cnt2 goto delay_loop2 decfsz cnt goto delay_loop1 return ; w = digit, return code in w digit: addwf pcl retlw 0x80 ; 0 retlw 0xf2 ; 1 retlw 0x48 ; 2 retlw 0x60 ; 3 retlw 0x32 ; 4 retlw 0x24 ; 5 retlw 0x04 ; 6 retlw 0xf0 ; 7 retlw 0x00 ; 8 retlw 0x20 ; 9 retlw 0x10 ; A retlw 0x06 ; b retlw 0x8c ; C retlw 0x42 ; d retlw 0x0c ; E retlw 0x1c ; F end