frequency counter. need help.

picaxester

Senior Member
Hi,
I need some help, I'm trying to make a frequency counter.
My test circuit is a 1Mhz crystal being divided by 4096, by a CD4040BE 12-stage ripple-carry binary counter/divider, whitch then goes to input 0 on my 18X PICAXE chip.
Here is my test code:
main:
count 0,1000,w1
w3 = w1 ** 4096
w2 = w1 * 4096
debug b0
pause 1000
goto main

And when I debug I get this:
Word:
W1=246
W2=24576
W3=15
Byte:
B2=246
B3=0
B4=0
B5=96
B6=15
B7=0

I know it's probable the circuit, I think I don't know what I'm doing when it comes to the math.
Thank you.

Eric
 

thelabwiz

Senior Member
Considering the +/- 1% accuracy of the PICAXE's internal clock, 246 is pretty close to the 244.140625 you get wgen dividing 1000000 by 4096.

The largest number the PICAXE can handle is 16 bits, so you won't be able to process numbers in the range of 1000000 directly - that needs 32 bit math. Do a forum search for 32 bit or look into the add-on floating point processor chip.

John
 

picaxester

Senior Member
I thought I could put half the number in W2 and the other in W3 with * and ** because in the pdf it says:
* ; multiply (returns low word of result)
** ; multiply (returns high word of result)

Is there another way to make a frequency counter with the PICAXE.
 

BeanieBots

Moderator
The only way to avoid the 16 bit limit is to do the counting yourself against some other time indicator.
Test the input, wait for an edge, increment a word variable, check the word variable for overflow and increment the next one as/if required.
Then check to see if the "timeout" period has elapsed. If it has, you have a count, if not, keep counting.
The problem with that method is that the tests and branches take a variable amount of time to process which would make it difficult to accurately determine the time frame without using an external time reference.
 

picaxester

Senior Member
What does
** ; multiply (returns high word of result)
do, can someone show me a simple example it.
Thanks for helping me.
 

Jeremy Leach

Senior Member
Personally I'd approach this problem from a different direction:

First you need to decide what your maximum frequency is going to be. Assume it's 6.5535 MHz. This will fit nicely into a Word variable that can be passed to a display routine.

Next, decide on the acceptable sample time. Let's stick to 1 second as you've got it.

Next, look at the Count command in detail. Assuming you're going to stick with a 4MHz clock for the Picaxe the manual tells us that 25KHz is the maximum frequency that it can record at this clock speed.

A basic principle is that you want the Count to be as high as possible in order to get highest accuracy from your frequency counter. So the divider chip needs to divide by (6,553,500Hz/25KHz) = 262. The nearest practical value to this, that will allow us to measure up to the maximum frequency, is 512. So I'd change the pin you get your division from on the divider chip to give divide by 512.

Next, the calculation. If Cw is the count word value, and Dw is the display word value, then:

Dw = (Cw * 512) / 100
= (Cw * 128)/25

Now at the max input frequency of 6,553,500Hz, Cw = 12799. So Cw * 128 is going to overflow if we multiply straight away. The best solution I've found is to play with remainders:

Dw =(Cw * 128)/25
= (Cw/25) * 128
=(W + R/25) * 128 where W is the whole part of the division and R is the remainder.
= (W * 128) + [(R * 128)/25]

[(R * 128)/25] won't overflow, since R<= 24. And (W * 128) is just ok too because at the max frequency Cw = 12799 so Cw/25 = 511 and (511 * 128) = 65408 !

So in picaxe code this could be expressed as:

<code><pre><font size=2 face='Courier'>
Symbol CountW = W1
Symbol WholeW = W2
Symbol BRemainder = b4
Symbol DisplayW = W4

Count 0,1000, CountW
WholeW = CountW / 25
BRemainder = CountW // 25 'Modulus
DisplayW = WholeW * 128
DisplayW = BRemainder * 128 / 25 + DisplayW
</font></pre></code>

For an example, let's say the input frequency is 6,000,000.
Cw would be (6,000,000/512) = 11718

The floating point result of Dw =(Cw * 128)/25 would give 59,996
My code will give:
WholeW = 468
BRemainder = 18
DisplayW = 468 * 128 = 59904
DisplayW = (18 * 128 / 25) + 59904 = 59996

So it seems to work well ! Phew...

Using a Picaxe frequency of 8MHz would (as the manual says) give a maximum count frequency of 50KHz, which means that your chip could divide by 256 instead of 512. The method of calculation would be similar. This would increase the accuracy of the result in theory.

If you want to go higher than 6.5MHz then we'd need a rethink. But I expect you'll also get the guys who know about high frequency work telling you about issues with PCB design etc.

UPDATE: Need to make Symbol BRemainder = b0

Edited by - Jeremy Leach on 12/12/2006 07:48:44
 

picaxester

Senior Member
Wow, thanks for the help.
I read your post several times, but I'm sorry, I don't quite understand it all.
Mabye you can help me understand this:
&quot; ** ; multiply (returns high word of result)&quot;
and why my code doesn't work.
Thank you.
 

Jeremy Leach

Senior Member
Well, it looks like your code has worked to me - it's what you do with it is the problem ;-)

So, your code has multiplied 246 * 4096. It's split the result of this multiplication into two words: The most significant Word is W3 and the least significant W2.

Another way of looking at this is that the result = (W3 * 65536) + W2

So the result = (15 * 65536) + 24576 = 1007616, which isn't bad I guess.

The whole problem is how to manipulate your W2 and W3 values into something you can display, or use.

My method gets you a nice display variable value you can shove into a display routine. I'm happy to explain anything you don't understand.

Edited by - jeremy leach on 11/12/2006 22:32:41
 

picaxester

Senior Member
I'm starting to understand what your saying and it seemed to work on my calculator but when I tryed the code you showed me it didn't work:

Symbol CountW = W1
Symbol WholeW = W2
Symbol BRemainder = b4
Symbol DisplayW = W4

main:
Count 0,1000, CountW
WholeW = CountW / 25
BRemainder = CountW // 25 'Modulus
DisplayW = WholeW * 128
DisplayW = BRemainder * 128 / 25 + DisplayW
debug b0
pause 1000
goto main
 

Jeremy Leach

Senior Member
Ah, there's a clash of variables, sorry. BRemainder and WholeW both use b4. Try making :
Symbol BRemainder = b0

Another idea is to use two inputs to the Picaxe, both connected to different pins on your divider chip. Could count from either, depending on what range the frequency is in. If it's a really high frequency then can count from the one farthest down the chain. If lower freq, the one higher up the chain of dividers. Would need a set of code, like the one I've done, for each. But the advantage is that you can maximise accuracy. If you just have a massive division, like the 4096 you've got, you lose accuracy at the lower frequencies.

Edited by - Jeremy Leach on 12/12/2006 08:50:09
 

picaxester

Senior Member
lol I should have seen that oops.
Now I'm geting 10081 in W4.
So when I go to display it, I just add 2 zeros?
Can I calibrate it by changing the sample time?

Good idea with the two pins, I was thinking about adding a switch or a chip that the PICAXE could control and select whitch pin to use.
Thanks.
 

Jeremy Leach

Senior Member
Well, to get the reading in Hz, yes add two zeros. It's all based on you having a max displayable frequency of 6.5535 MHz - which is an assumption on my part. I was thinking more along the lines of displaying the result in MHz (since you mentioned you were testing a MHz frequency) and just shoving a decimal point in.

Yes I suppose you could calibrate it by changing the sample time - good idea !


I was actually thinking of autoranging using the two pins from the divider chip: Calcuate the reading on the currently selected pin, and if the frequency comes out as being more suitable to be read by the other pin then swap to reading from that pin - automatically !

You could even use more than two input pins ;-)
 

picaxester

Senior Member
I got it work on a 1x16 lcd :)
Here is the code:

Symbol CountW = W2
Symbol WholeW = W1
Symbol BRemainder = b6
Symbol DisplayW = W0

gosub init

b0 = 12
gosub wrins

main:
Count 1,992, CountW
WholeW = CountW / 25
BRemainder = CountW // 25 'Modulus
DisplayW = WholeW * 128
DisplayW = BRemainder * 128 / 25 + DisplayW
'w0 = 12345
b2 = w0 // 10 'b2 = 5
w6 = w0 / 10 'w6 = 1234
b3 = w6 // 10 'b3 = 4
w0 = w6 / 10 'w0 = 123
b4 = w0 // 10 'b4 = 3
w6 = w0 / 10 'w6 = 12
b5 = w6 // 10 'b5 = 2
b6 = w6 / 10 'b6 = 1

b0 = 128
gosub wrins

b0 = b6 + 48
gosub wrchr
b0 = 46
gosub wrchr
b0 = b5 + 48
gosub wrchr
b0 = b4 + 48
gosub wrchr
b0 = b3 + 48
gosub wrchr
b0 = b2 + 48
gosub wrchr
b0 = 77
gosub wrchr
b0 = 72
gosub wrchr
b0 = 192
gosub wrins
b0 = 90
gosub wrchr

goto main

I didn't include the lcd gosubs.
 

Jeremy Leach

Senior Member
Excellent ;-)

Best to define symbol names for everything. Also can condense the routines using loops etc, but it's shaping up nicely.
 

picaxester

Senior Member
I don't like symbols, I don't know why, mabye If they would changed colour once you difine them :)
lol I just wanted it to work I'll probable clean it up later ;)
But hey, I got the auto range working.
It switches to khz if it goes under 40 khz.
Hers the code:

Symbol CountW = W2
Symbol WholeW = W1
Symbol BRemainder = b6
Symbol DisplayW = W0

gosub init

b0 = 12
gosub wrins

main:
Count 0,992, CountW
if CountW &lt; 78 then khz
WholeW = CountW / 25
BRemainder = CountW // 25 'Modulus
DisplayW = WholeW * 128
DisplayW = BRemainder * 128 / 25 + DisplayW
'w0 = 12345
b2 = w0 // 10 'b2 = 5
w6 = w0 / 10 'w6 = 1234
b3 = w6 // 10 'b3 = 4
w0 = w6 / 10 'w0 = 123
b4 = w0 // 10 'b4 = 3
w6 = w0 / 10 'w6 = 12
b5 = w6 // 10 'b5 = 2
b6 = w6 / 10 'b6 = 1

b0 = 128
gosub wrins

b0 = b6 + 48
gosub wrchr
b0 = 46
gosub wrchr
b0 = b5 + 48
gosub wrchr
b0 = b4 + 48
gosub wrchr
b0 = b3 + 48
gosub wrchr
b0 = b2 + 48
gosub wrchr
b0 = 77
gosub wrchr
b0 = 72
gosub wrchr
b0 = 192
gosub wrins
b0 = 90
gosub wrchr

goto main

khz:
Count 1,992,w0
w0 = w0 * 2
'w0 = 12345
b2 = w0 // 10 'b2 = 5
w6 = w0 / 10 'w6 = 1234
b3 = w6 // 10 'b3 = 4
w0 = w6 / 10 'w0 = 123
b4 = w0 // 10 'b4 = 3
w6 = w0 / 10 'w6 = 12
b5 = w6 // 10 'b5 = 2
b6 = w6 / 10 'b6 = 1

b0 = 128
gosub wrins

b0 = b6 + 48
gosub wrchr
b0 = b5 + 48
gosub wrchr
b0 = 46
gosub wrchr
b0 = b4 + 48
gosub wrchr
b0 = b3 + 48
gosub wrchr
b0 = b2 + 48
gosub wrchr
b0 = 75
gosub wrchr
b0 = 72
gosub wrchr
b0 = 192
gosub wrins
b0 = 90
gosub wrchr

goto main

VERY lazy codeing. But it works.
Input 1 is divid by 2.
 

Jeremy Leach

Senior Member
This has taken my interest really, and I've come up with some code to use 4 input pins on an 08M and display the frequency in Hz up to 65,535,000 Hz (ie 65 MHz). It will display the frequency with commas and blanking leading zeroes.

The use of 4 inputs from the 4040 allows the frequency measurement to be maximised.

It is UNTESTED, but just compiles for an 08M.
And plenty of symbols !

Pity the PICAXE Projects website isn't functioning yet ;-)

<code><pre><font size=2 face='Courier'>
#rem
##########################################################
# #
# PICAXE 08M FREQUENCY COUNTER #
# #
# Code Version :1 #
# Status :Untested #
# Date :December 2006 #
# PICAXE Type :08M #
# Software Version :5.06 #
# #
# Author :Jeremy Leach #
# #
##########################################################


LCD DISPLAY:
------------
.---..---..---..---..---..---..---..---..---..---..---..---.
| 9 || 9 || , || 9 || 9 || 9 || , || 9 || 9 || 9 || H || Z |
'---''---''---''---''---''---''---''---''---''---''---''---'
Digit: 0 1 2 3 4 5 6 7

CIRCUIT DIAGRAM:
----------------

.----------.
.--(/4096)--|Q12 +V|--
| (/64)--|Q6 Q11|--(/2048)
| (/32)--|Q5 Q10|--(/1024)
| (/128)--|Q7 Q8|--(/256)
| .- (/16)--|Q4 Q9|--(/512)---------.
| | (/8)--|Q3 Reset|-- |
| | (/4)--|Q2 /Clk|-- |
| | --|0v Q1|--(/2) ---. |
| | '----------' | |
| | 4040 | |
| | | |
| | | |
| | | |
| | .----------. | |
| | --|+V 0V|-- | |
| | --|Serin Out0|-- | |
| '---------|In4 In1|----------' |
'-----------|In3 In2|-----------------'
'----------'
PICAXE 08M


DESCRIPTION:
------------
This frequency counter is capable of reading and displaying a frequency in
the range 0 to 65MHz. However this is relying on the accuracy of the internal
osciallor.

The circuit uses a 4040 binary ripple counter to divide down the
input frequency to a level that the PICAXE08 can read. Four separate
inputs are used on the PICAXE08M connected to the 4040 /2,/16,/512 and
/4096 outputs. This allows the PICAXE08M to sample, using the Count
command, using the input that will give the best accuracy.

The slection of the input pin to use is done automatically. A quick initial
'sample is taken using the highest division output of the 4040. This gives
'a rough frequency which is used to select the input pin for the main sample.

Careful maths is used for each input pin to maximise accuracy.

The frequency value is displayed to an LCD in Hz. Blanking of leading
zeroes is performed.

CALCULATION DETAIL:
-------------------

The Count command can sample at a maximum input frequency of 50KHz with the
PICAXE running at 8MHz. So the maximum signal frequency that can be measured
at each input is:

MEANING OF
INPUT MAX READABLE SIGNAL FREQUENCY (MRSF) FrequencyW = 65535

/2 50KHz * 2 = 100,000 Hz 65,535 Hz
/16 50KHz * 16 = 800,000 Hz 655,350 Hz
/512 50KHz * 512 = 25,600,000 Hz 6,553,500 Hz
/4096 50KHz * 4096 = 204,800,000 Hz 65,535,000 Hz

The right-most column shows the meaning of a FrequencyW value of 65535
when obtained from the calculations for each input.

Note though that the maximum frequency the 4040 chip (HC type) can read
is 90MHz, and the code puts a convenient cap of 65.535 MHz on the top
frequency that the counter can read.

#endrem


'#########################################################
'# VARIABLES #
'#########################################################
'Word0 (b0 and b1)
Symbol DisplayStarted = bit0
Symbol ASCIIChr = b1

'Word1 (b2 and b3)

'Word2 (b4 and b5)
Symbol FrequencyW = w2

'Word3 (b6 and b7)
Symbol WRemainder = w3

'Word4 (b8 and b9)
Symbol DivisorW = w4

'Word5 (b10 and b11)
Symbol CountW = w5

'Word6 (b12 and b13)
Symbol WholeW = w6
Symbol Digit = b12
Symbol StartDigit = b13

'#########################################################
'# CONSTANTS #
'#########################################################

'Miscellaneous
Symbol Sample1000ms = 2000 'at 8MHz. Tune this value to calibrate
'the frequency counter against a known
'source.
Symbol Sample100ms = 200 'at 8MHz
Symbol Yes = 1
Symbol No = 0

'Pin assignments
Symbol Div2InputPin = 1
Symbol Div16InputPin = 4
Symbol Div512InputPin = 2
Symbol Div4096InputPin = 3
Symbol LCDOutPin = 0

'#########################################################
'# INITIALISE #
'#########################################################

SetFreq M8 'To maximise accuracy

Pause 1000 'half a second at 8MHz. For the AXE033 LCD to initialise.

'Write &quot;Hz&quot; at the end of line 1 on the LCD.
Serout LCDOutPin,N1200,(254,136,&quot;Hz&quot;)

'#########################################################
'# MAIN PROGRAM #
'#########################################################

Main:
Gosub GetFrequency
Gosub DisplayFrequency
Goto Main

'#########################################################
'# SUBROUTINES #
'#########################################################

GetFrequency:
'Take an initial sample and do a rough calculation of the frequency. Use
'this result to select the best input pin to use for a more accurate
'one-second sample.

'ON EXIT: FrequencyW holds the frequency value. However it has different
'meaning depending on the Input pin used.

'Take an initial quick sample in one tenth of a second using the
'/4096 input.
Count Div4096InputPin,Sample100ms,CountW

'Calculate a rough input frequency, using the formula
'Freq = (Count * 10 * 4096)/1000.'Here, a frequencyW value of 65535
'means 65.535 MHz.

'As this is a rough calculation then we can approximate this formula to
'be: Freq = Count * 41. This also raises the calculated frequency slightly
'which is helpful for cases which lie on the threshold between which input
'is 'best'.

FrequencyW = CountW * 41

'Use this rough frequency to decide which input is best to read
'the main sample. 'Best' meaning where the input signal frequency is
'just within the input's Maximum Readable Signal Frequency (MRSF).

Select Case FrequencyW
Case &gt;6553 '65,530,000 Hz
'ActiveInput = Div4096InputPin

Count Div4096InputPin,Sample1000ms,CountW
'Calculate frequency value using formula:
'Freq = (Count * 4096)/1000.
WholeW = CountW / 125
WRemainder = CountW // 125
FrequencyW = WholeW * 512
FrequencyW = WRemainder * 512 / 125 + FrequencyW

StartDigit = 0

Case &gt;655 '6,500,000 Hz
'ActiveInput = Div512InputPin

Count Div512InputPin,Sample1000ms,CountW
'Calculate frequency value using formula:
'Freq = (Count * 512)/100.
WholeW = CountW / 25
WRemainder = CountW // 25
FrequencyW = WholeW * 128
FrequencyW = WRemainder * 128 / 25 + FrequencyW

StartDigit = 1

Case &gt;65 '65,000 Hz
'ActiveInput = Div16InputPin

Count Div16InputPin,Sample1000ms,CountW
'Calculate frequency value using formula:
'Freq = (Count * 16)/10.
WholeW = CountW / 5
WRemainder = CountW // 5
FrequencyW = WholeW * 8
FrequencyW = WRemainder * 8 / 5 + FrequencyW

StartDigit = 2

Else '&lt;= 65,000 Hz
'ActiveInput = Div2InputPin

Count Div2InputPin,Sample1000ms,CountW
'Calculate frequency value using formula:
'Freq = Count * 2.
FrequencyW = CountW * 2

StartDigit = 3
EndSelect
Return

DisplayFrequency:
'Display the frequency on the LCD, adding in commas and blanking leading
'Zeroes.

'Initialise display variables
WRemainder = FrequencyW
DisplayStarted = No
DivisorW = 10000

'Move to the start of line1
Serout LCDOutPin,N1200,(254,128)

'Display the frequency value
For Digit = 0 To 7
If Digit &gt;= StartDigit Then
ASCIIChr = WRemainder / DivisorW + &quot;0&quot;
WRemainder = WRemainder // DivisorW

'If the value is &gt;0 then start the display of digits
If ASCIIChr &gt;&quot;0&quot; Then
DisplayStarted = Yes
EndIf

DivisorW = DivisorW/10
Endif

'Blank the digit if the Display hasn't started
If DisplayStarted = No Then
ASCIIChr = &quot; &quot;
Endif

Gosub WriteChrToLCD 'Write the digit

'If the digit is 1 or 4 then need to write a comma, but
'only if the display has started.
If Digit &lt;&gt; 1 And Digit &lt;&gt; 4 Then GADF_1

ASCIIChr = DisplayStarted * 12 + &quot; &quot;

Gosub WriteChrToLCD

GADF_1:
Next
Return


WriteChrToLCD:
'Writes the value of ASCIIChr to the LCD.
Serout LCDOutPin,N1200,(ASCIIChr)
Return

</font></pre></code>
 

picaxester

Senior Member
If you want to go passed 3.5mhz than don't use the cd4040be because it maxes out at 3.5mhz @ 5V,use the 74HC4040 it maxes out at 90mhz.
 

picaxester

Senior Member
WOW, thats some program, very nice!
You could also use a 3 wire interface using a 74hc164 shift register with the 08M and still have 2 input for the 4040, and that would make it cheaper.
Here are some cool links for frequency counters:
http://www.qsl.net/dl4yhf/freq_counter/freq_counter.html
http://www.hanssummers.com/radio/sfreq/index.htm
http://www.qsl.net/pa2ohh/sfreq.htm
 

eclectic

Moderator
Jeremy.

A Magnificent piece of work!

I&#8217;ve just set up your counter, using a 4060
( No /2 pin and frequency &lt; 4MHz),
so I had to rem out lines 235 - 243 .

I used a 28X pwmout, with 4,8 and 16 resonators as a &#8220;vfo&#8221;.
Moderate tweaking on line 130.

Works brilliantly!
The LCD readout correlates &lt;&lt;1% with the frequency readout
on my new OWON &#8216;scope. (Early Santa job.)

Just two tiny points I&#8217;ve had to change.
1. Line 153 to &#8230;. 254,140
2. The 4060 (and 4040) reset pins need to be grounded.

http://www.kpsec.freeuk.com/components/cmos.htm#4040

Many thanks.

e.

p.s. Thanks to picaxester for starting this thread.
 

eclectic

Moderator
Jeremy.
Refresh c. 1/sec.

The display was wobbling, but I've traced it to the cheap pot. I'm using on the ADC input.
I'll make a hard-wired 4040 section,
when Rapid deliver the new chips.

e.
 
Top