Kronos Robotics and Electronics
Site Map
 
Home Zeus Projects App Notes Downloads Dios Athena Forums
 

DAN131

Build a Frequency Counter

  By Michael Simpson

Updated on 01/28/2005

I have built a few frequency counters in the past but decided to do one based on the Dios.   In this application note we will be concertinaing on the software.  I will present 3 different programs each one will show you a different way of building a frequency counter.

Counter 1

Here we will use Dios only code to set up timer0 to give us a 1 second interval count.  We will count the number of cycles during that interval.

The input will be placed on IOport 15.  For the output we will just display the count in the debug window.

One advantage of this type counter is the input can be placed on any of the IOports because its primarily software based.

Download it here

'=======================================================
'Demonstrate a simple Software Frequency Counter
' Uses timer0 as a 1 second timer
'=======================================================
func main()
dim fcount

again:
fcount = countpulses()
print fcount,"Hz"
goto again

endfunc


'======================================================
'Count the cycles
'======================================================
func countpulses()

dim fcount
clear fcount

'----------------------------------------------------
'First thing setup timer 0
'----------------------------------------------------
INTCON.bit(2)=0 'clear timer0 overflow flag
T0CON.bit(7)=0 'Make sure its off
TMR0H=103 '1 second (you must always write high byte first
TMR0L=150
T0CON.bit(6)=0 '16 bit mode
T0CON.bit(5)=0 'Internal clock
T0CON.bit(3)=0 'Assign prescale
T0CON.bit(2)=1 'Prescale Each Tic = 25.6us
T0CON.bit(1)=1 'Prescale
T0CON.bit(0)=1 'Prescale

T0CON.bit(7)=1 'Start Timer

loop1:
if INTCON.bit(2) = 1 then exit fcount
if IOPORT(15) = 0 then goto loop1

loop2:
if INTCON.bit(2) = 1 then exit fcount
if IOPORT(15) = 1 then goto loop2

inc fcount
goto loop1

endfunc

 

There is 1 fundamental problem with this type counter.   Even with the speed of the Dios Its only good for up to 6000hz.  If you know you are only be measuring low frequency values then this may be all you need.

Counter 2

We will do pretty much the same thing as counter 1 except the time critical sections will be done with inline assembly.  I decided to use a floating point variable to hold the final result because Im using 24 bytes as the counter here.

The input will be placed on IOport 15.  For the output we will just display the count in the debug window.

Just like the previous example the input can be placed on any of the IOports because its primarily software based.

 

Download it here

'=======================================================
'Demonstrate a simple Software Frequency Counter
' Uses timer0 as a 1 second timer
'=======================================================
func main()

gconst freqin 15

dim bnum as float
dim inum as integer

again:
countpulses()

inum = BYTE1 * 256 + BYTE0
bnum = BYTE2 *65535 + inum
print bnum
goto again


endfunc

'===============================================
'Go get a reading
'===============================================
func countpulses()

'Clear counters
BYTE0=0
BYTE1=0
BYTE2=0

'----------------------------------------------------
'First thing setup timer 0
'----------------------------------------------------
INTCON.bit(2)=0 'clear timer0 overflow flag
T0CON.bit(7)=0 'Make sure its off
TMR0H=103 '1 second (you must always write high byte first)
TMR0L=150
T0CON.bit(6)=0 '16 bit mode
T0CON.bit(5)=0 'Internal clock
T0CON.bit(3)=0 'Assign prescale
T0CON.bit(2)=1 'Prescale Each Tic = 25.6us
T0CON.bit(1)=1 'Prescale
T0CON.bit(0)=1 'Prescale

'-----------------------------------------
'Inline assembly section
'-----------------------------------------
startasm

bsf T0CON,7 'Start timer0

'Look for 1
loop1:
   btfsc INTCON,TMR0IF
   goto alldone
   if inp.freqin = 0 then
      goto loop1
   endif

'look for 0
loop2
   btfsc INTCON,TMR0IF
   goto alldone
   if inp.freqin = 1 then
     goto loop2
   endif

   incf BYTE0,f
   btfss STATUS,Z
   goto loop1
   incf BYTE1,f
   btfss STATUS,Z
   goto loop1
   incf BYTE2,f
   goto loop1

alldone

   clrf T0CON 'Stop timer
   bcf INTCON,TMR0IF 'Clear flag
endasm
'------------------------------------------

endfunc
 

Ok what's wrong with this program.   Well for one thing it is only good for readings up to 100,000 Hz.  The other problem is that its got just a little too much assembly code.

Counter 3

In this example we are going to put a little more emphases on some of the internal capabilities of the Dios and simplify the assembly to just 5 instructions.

We are still going to use timer0 as out interval timer (gate).  But instead of counting the incoming pulses our self we will let the Dios hardware do it.

Most of the internal timers can be used as counters.  In this case we are going to use timer1 as a counter to count the pin transitions.

This simplifies the time critical instructions needed.  One disadvantage is that the input must be placed on IOport 15.

One feature I have added to this program is the ability to change the gate interval.  This will allow us to extend the range of the counter as the cost of resolution. 

Resolution

  • 1-65535Hz  1Hz Resolution

  • 65536-655350Hz 10Hz Resolution

  • 655360-6Mhz 100Hz Resolution

Download it here

'================================================
'Multi Gate Frequency Counter
'================================================
' Uses hardware timer 0 as gate timer
' hardware timer 1 as counter
' Frequency input on pin 15
func main()
global gate
gate = 1

again:
countpulses()
goto again

endfunc



'===============================================
'Go get a reading
'===============================================
func countpulses()
dim fcount

again:
pause 10
clearcounter
setgate(gate)

'Turn on gate timer and start counter
'-----------------------------------------
startasm
   bsf T0CON,7 'Turn on timer0 Gate
   bsf T1CON,0 'Start counter

loop:
   btfss INTCON,TMR0IF
   goto loop

   'If we make it here then we have count
   bcf T1CON,0 'Stop counter

endasm
'------------------------------------------

if PIR1.bit(0)=1 then
print "Overflow"
gate = gate + 1
goto again
endif

fcount = TMR1L
fcount = TMR1H * 256 + TMR1L

if fcount < 6000 and gate > 1 then
gate = gate -1
goto again
endif

displaycount(gate,fcount)

endfunc

'=================================================
'Display the counter results
'=================================================
func displaycount(tgate,fcount)
dim v10000,v1000,v100,v10,v1
bintodec fcount,v10000,v1000,v100,v10,v1

branch tgate,do1,do1,do2,do3

do1:
print tgate,": ",v10000,v1000,",",v100,v10,v1,"Hz"
exit 0

do2:
print tgate,": ",v10000,v1000,v100,".",v10,v1,"KHz"
exit 0

do3:
print tgate,": ",v10000,v1000,v100,v10,".",v1,"KHz"
exit 0

endfunc


'=================================================
'Sets the gate time
'0 or 1 is 1 second gate
'2 .1 second gate
'3 .01 gate
'=================================================
func setgate(tgate)


INTCON.bit(2)=0 'clear overflow flag TMR0IF
T0CON.bit(7)=0 'Turn it off
T0CON.bit(6)=0 '16 bit mode
T0CON.bit(5)=0 'Internal clock
T0CON.bit(3)=0 'Assign prescale

branch tgate,do1,do1,do2,do3

'1 second gate
do1:
TMR0H=102 '1 second
TMR0L=215
T0CON.bit(2)=1 'Prescale Each Tic = 25.6us
T0CON.bit(1)=1 'Prescale
T0CON.bit(0)=1 'Prescale
exit 0

'.1 seconds gate
do2:
TMR0H=240 '.1 second
TMR0L=175
T0CON.bit(2)=1 'Prescale Each Tic = 25.6us
T0CON.bit(1)=1 'Prescale
T0CON.bit(0)=1 'Prescale
exit 0

'.01 seconds gate
do3:
TMR0H =59 '.01 second
TMR0L = 244
T0CON.bit(2)=0 'Prescale Each Tic = .2us
T0CON.bit(1)=0 'Prescale
T0CON.bit(0)=0 'Prescale
exit 0

endfunc


'=================================================
'This function clears the 16bit hardware counter
' and preps it for next gate
'=================================================
func clearcounter()

T1CON.bit(0)=0 'Turn it off
PIR1.bit(0)=0 'clear counter overflow TMR1IF
TMR1H=0
TMR1L=0
T1CON.bit(7)= 1 '16 bit operation
T1CON.bit(5)=0 'Prescale
T1CON.bit(4)=0 'Prescale
T1CON.bit(3)=0 'No Oscillator
T1CON.bit(2)=1 'Ignored
T1CON.bit(1)=1 'External

endfunc
 

As you can see this example is a bit more complicated.  Most of the code is devoted to the automatic gate interval selection.   If you want to do that manually you could remove about 50% of the code.

I have only tested this counter up to 6Mhz.  Also I will be updating this application note in the future to improve the accuracy by tweaking the timer interval values.

Update: I used a HP 53131 to calibrate the gate timers.    Now its one accurate program!!!!

Since I do most of my testing with digital devices.  I did not add and kind of amplifier or signal conditioner to the input.

If you want to make a actual meter all you need do now is include the LCD library and create your own digital frequency counter.  Just add a couple LCD display commands in the place of the print commands in the displaycount function.

Later I will be taking several of these application notes to create a whole new project called the ultimate utility meter.

 

Parts list

DiosPro 40 Pin Chip

Dios Workboard Deluxe

 

Easy RS232 Driver  

DiosPro 28 Pin Chip

Dios 32 Pin Carrier (Carrier #1)

 

9 Pin Cable

Breadboard Regulator

 

Copyright © 2001 - 2007 Kronos Robotics