PDA

View Full Version : Accurate Freq Measurment



Terry
- 2nd June 2008, 18:33
I am using a PIC to make a very accurate (sub hertz) frequency measurement of a 4 kHz signal in as little time as possible, about ~ 100msec. I am using TIMER1 to measure the period of 400 cycles of the 4kHz signal. I am starting Timer1 on a rising edge of the 4kHz signal, keeping track of the number of cycles and then stopping Timer1 on the 400th cycle of the 4kHz signal. I then have a count and number of rollovers of the timer from which I calculate the frequency in a computer, not the PIC. Below is the code of my technique. The question I have is there a better approach to taking advantage of the PIC’s hardware Timers and/or capture compare module, such to improve the accuracy and to do this in the background. The code in currently for the PIC16F877, but I can easily switch to an 18F part.
Thanks in advance for the help,
Terry

amgen
- 2nd June 2008, 18:59
one pulse-in, will give a count in 10-micro second units,and I think give the same accuracy of your routine. Plus only take 1/4000 seconds or 1/8000 sec due to 1/2 cycle.

skimask
- 2nd June 2008, 19:58
Assuming a 50% duty cycle on the input signal, COUNT might work well for you. There are a few tricks a person could use to fool the compiler into giving you a result that would be 'sub-hertz'.

Terry
- 3rd June 2008, 00:02
Thanks for the suggestions, but at the moment I think I am getting quite a bit more resolution since essentially my time increment is 1/5MHz or 200nsec and then this is applied over a longer period of time in regards to only measuring over only 1 cycle, there is an extended resolution due to the fact that it is over 200 cycles rather than just one. It would be nice to use the hardware timers as I would like to do this in the background more on an interrupt basis rather than taking using CPU time to make the measurement, but I do appreciate the ideas.
T

dhouston
- 3rd June 2008, 00:54
The minimum period for COUNT is 1mS but I've never tested it to see how accurate that is. You might try it and compare it to your current method for accuracy. It counts low to high transitions so duty cycle is really immaterial. I'm not sure why the manual even mentions duty cycle. If you use 1mS, then the result x 1000 will give the frequency. It's certainly simpler than what you do now.

skimask
- 3rd June 2008, 01:32
It would be nice to use the hardware timers as I would like to do this in the background more on an interrupt basis rather than taking using CPU time to make the measurement, but I do appreciate the ideas.

Then you fire up one of the timers...
Say it runs at a base frequency of 1Mhz (4Mhz OSC / 4 ), each tick is 1us.
Clear the timer...
Wait for a rising edge of the input and start the timer.
Wait for the next rising edge of the input and stop the timer.
Subtract the few cycles it takes to start and stop the timer.
Since you want a frequency of 4Khz, the timer should have a count of 250 in it (after the necessary subtractions).
If the count is 249, then your input freq is 4016 Hz, if 251, then 3984 Hz.
But you want sub-Hz resolution...
So kick up the PIC to 40Mhz, and the timer runs at 10Mhz, each tick is .1us.
Using the same logic as above, the timer should have a count of 2500 in it between the 2 rising edges (after the subtraction).
If the count is 2499, the freq is 4001.6 Hz, count = 2501 then freq = 3998.4 Hz...
But that's still not quite sub-Hz resolution...Sooo....
Instead of counting one rising, you go for the max resolution, wait for the next 25 rising edges. After that 25th rising edge, the timer should have a count of 62,500.
If it's 62,499, your input frequency average over the last 25 cycles was 4000.0640010240163842621481943711 Hz.
If the timer reads 62,501, the input frequency average over the last 25 cycles was 3999.9360010239836162621398057631 Hz.
Problem is...it takes 25 cycles to get that number, which might not be fast enough for you. The only way to increase this is to increase the speed of the timer, hence the CPU. With a PIC18F, you aren't going to get much better than .1us accuracy at 40Mhz.
And since you want to do this in the background, that's easy enough by applying your input signal to, say, the RB0 interrupt, and having the interrupt routine do the timer starting/stopping, clearing, count saving and so on. And your main loop handles the hard math...

amgen
- 3rd June 2008, 12:41
yes....resolution and overhead

use ccp (capture/compare) module in capture mode....
count at 10MHZ,(Fosc/4 from 40MHZ) set for every 16th rise or fall edge to get counts directly, at 4000.0 hz, count is 40000
-at 3999 hz count is 40010
-at 4001 hz count is 39990

freq then = (40,000 +/- difference)/10 hz down to .1HZ

i think?
don

amgen
- 3rd June 2008, 13:44
actual hz calculation........

HZ = 16x10^7/count (from ccp) within range of 2500hz to 10MHZ ?
for ccp @ every 16 edges.

don f

Terry
- 3rd June 2008, 18:49
skimask your last description is exactly what I am trying to do. My code essentially does just that over a 200 cycle period but not very gracefully as written, the start and stop of Timer1 I don't thinks is well done as I am using software to do it rather than hardware. In my attempt to measure the frequency fast, the physics of the problem limits how fast the measurement can be done for a given accuracy, but when comparing to a frequency counter approach I would need a 10 second period for 0.1Hz resolution, where by measuring the period as you point out can provide great resolution fast relative to a frequency counter, I guess I should have been more clear in my original posting, because my target is ~100 msecs for the measurement time. After doing more research it looks like the approach of using the capture/compare module with Timer1 as you point out Don is the way to go. I have not used the capture mode in the PIC's in an interrupt mode, are there any good code examples.
Terry

skimask
- 3rd June 2008, 19:20
I guess I should have been more clear in my original posting, because my target is ~100 msecs for the measurement time. After doing more research it looks like the approach of using the capture/compare module with Timer1 as you point out Don is the way to go. I have not used the capture mode in the PIC's in an interrupt mode, are there any good code examples.

As they say, you can have 2 of 3 things...fast,good,cheap...
You can have it fast, you can have it good, it won't be cheap.
You can have it good, and you can have it cheap, but it won't be fast.
You can have it cheap, and you can have it fast, but it won't be good :)

I think you're method will work, and I wouldn't doubt that what you are using right now could work well enough. Why not 'remember' the last bunch of readings, say the last 10 readings, then average them out, discard the oldest, plug in the newest...a moving average if you will. You won't necessarily gain resolution, well, yes you will since you've increased the effective sampling time. You can keep your 100ms measurement time, and still get fairly decent resolution.

amgen
- 4th June 2008, 03:04
Terry, I don't know specifically of capture code examples, the microchip data books lay out general routines. You probably know;
-set ccp for capture on 16 rise edge
-tmr1 prescale to 1
-make int routine in asm and add int pointer name for basic
-set pie and pir's for interrupt enables
---on int -- check for tmr1 overflow(not usable count)
---stop ccp and tmr1
---move count high and low to basic count word
---reset flags capture and tmr1 overflow
---tmr1h and tmr1L = 0
---start ccp and tmr1

basic prog can start and stop interrupts as desired.
took me some good head scratching for int's but is very reliable when working.
Once working, pic will just keep spitting out counts automatically.
don

Bruce
- 4th June 2008, 04:22
See this thread for a few examples http://www.picbasic.co.uk/forum/showthread.php?p=23401

You might want to download the .PDF Steve linked to in that thread for more info on using capture. It's very handy once you get the hang of using it.

If you really want to have some fun, take a peek at the 18F2431 series. This one has a TON of options for measuring frequency, pulse width, PWM motor control, high-speed simultaneous 2-channel A/D sampling, and lots of other goodies.

Terry
- 4th June 2008, 23:14
Bruce,
Thanks for the link, I just found it a shortly before you posted your message, I also found a few more of your posts with code for using the capture mode with Timer1, once I realized I needed to search on Capture I think you are the expert on the hardware Timers! I hope I can now greatly improve my code.
Terry

Bruce
- 5th June 2008, 01:40
Hi Terry,

Thanks, but I wouldn't call myself an expert on much beyond getting in trouble. At least, that's what my wife says..;o}

However, I have spent an hour or two messing with timers & hardware. Grab yourself an 18F2431, and give this a shot. It's really simple, but very precise.

I'm not really sure what the benefits of (sub hertz) measurements are going to give you. Oversampling A/D measurements is normally a good idea, so you can average measurements over time, but I really don't think this approach is going to give you any advantage when measuring a frequency. I mean, it's either X hZ or it's not.

Oversampling is just going to give you an average over time / the number of samples. If your frequency is changing, the result is garbage anyhow.

This is real simple. It sets up an 18F2431 to measure the time period between rising edges, which is the frequency.



DEFINE OSC 4
DEFINE DEBUG_REG PORTC
DEFINE DEBUG_BIT 6
DEFINE DEBUG_BAUD 9600
DEFINE DEBUG_MODE 0 ' 1 = inverted, 0 = true

T1 VAR WORD

Capture VAR PIR3.1
False CON 0
True CON 1

' auto time base reset, frequency mode, capture on every rising edge
Frequency CON %01000101

ANSEL0=0 ' All digital
TRISA.2 = 1 ' this is your (CAP1) input pin measuring the frequency
INTCON = 0 ' Interrupts off
TMR5H = 0 ' Clear high byte of TMR5 counter
TMR5L = 0 ' Clear low byte
T5CON = %00000001 ' prescale=1:1, int clock, TMR5=on
CAP1CON = Frequency ' we're measuring a frequency

DEBUG "START",13,10

Main:
Capture = False ' Reset capture flag
GOSUB Freq ' get frequency
PAUSE 500
GOTO Main

Freq:
' Frequency measurement mode, capture Timer5 count every rising edge
' with auto reset of Timer5 on each event
WHILE Capture = False
WEND
T1.HighByte = CAP1BUFH
T1.LowByte = CAP1BUFL
DEBUG "Freq = 1/",DEC T1,"uS",13,10
RETURN

END
With another PIC programmed to output a 4kHz signal into the 18F2431 CAP1 pin, this returns;

Freq = 1/250uS
Freq = 1/250uS
Freq = 1/250uS

1/250uS = 4kHz.

The 18F2431 has a lot of nifty features that can save you a boat-load of code & time.

It's definitely worth looking into. This captures the time between rising edges + resets the timer after each capture event. Very handy stuff...;o}

Terry
- 5th June 2008, 22:06
Bruce,
Thanks for the info on the 18F2431, I just order some today, I like the idea of having less code. My application is where I have a device that puts out two frequencies, and I need to know the frequency difference very accurately between the two of them, such that I am getting on the order the effectiveness of a 20 bit A/D process. I have your code up and running perfectly from the thread you listed earlier (the first code example), it works much better than mine. But I when tried changing the mode for the 2nd capture event from a falling edge to the 16th edge mode, I had odd values, acting like I had captured the 9th edge rather than the 16th, very strange. The only change to your code was going from:

CCP1CON.0 = 0 ' Configure capture for falling edge now
to
CCP1CON = %00000111 ' Capture mode, capture on 16th rising edge

Have you tried this capture mode, or is there something wrong with this simple change.
Cheers,
Terry

Bruce
- 5th June 2008, 22:28
Hi Terry,

Check the datasheet to see the effect of changing the prescaler with capture enabled.

That may be what's causing the bad capture value. You should be OK if you set it up for
every 16th edge before enabling capture. Switching prescaler with capture enabled can
cause odd results.

Edit: If you do need to do this, try disabling capture first, then changing prescaler.

CCP1CON=0 ' disable CCP module
Capture=0 ' clear capture flag
CCP1CON=%00000111 ' re-enable for every 16th edge

Never tried it myself, but it looks like it should work. Disabling the CCP module clears the
prescaler count.

Terry
- 6th June 2008, 23:09
Bruce,
You nailed the problem with the prescaler, set it and don't change it half ways through. A simple code change and it all now works great! My next step is to try your 2nd code example using Darrel Taylors' instant interrupts, I haven't had a need to try them yet, but they look very useful and reasonably easy to use for this application.
Thanks to ALL for your help and suggestions on my problem, I continue to learn many things from this great forum of users.
Cheers,
Terry

amgen
- 8th June 2008, 15:13
Bruce & Terry,
I have been following posts on freq measure. That timer 5 with the motion feedback module is just super-neat. Hardly any code to get very accurate measure. Being able to measure small ,small errors is amazing.
Terry, if you don't mind, how much are your readings varying around the 4khz and what clock did you end up using?
Also, what are you doing with the output value?
Hats off to you guys.
don
amgen

Terry
- 17th June 2008, 21:16
Don,
I have been experimenting with both the prescaler (divide by 16) and skipping it. When I use an Agilent frequency generator for testing, the count only jitters by 1 count with either approach, the measurement is very stable under this test situation with both approaches. Now that I have the PIC code working, I am now experimenting with my actual application to see which is the better method.
When measuring the period of 200 cycles of a 4 kHz signal, the frequency resolution is great, 1 count corresponds to 0.016 Hz at 4 KHz and the measurement time is 50 msec. The PIC counters are great once you understand how to set them up.
I output the count data via RS-232 to a PC.
Terry

davidcope
- 16th May 2011, 12:59
Hi,

I've read the great advice and example code here during a search of PIC period measurement. I'm in the process of using a PIC 18F2431 and using C.

Not being an expert with PICBasic, could I ask what PIR3.1 refers too from the example given above?


Capture VAR PIR3.1

Thanks in advance, David.

Acetronics2
- 16th May 2011, 14:20
Hi,

"C" ... What is "C" ...

is there anything called "C" somewhere ???? :D

has "C" ever existed ??? :D:D

Just open your pic datasheet, interrupts flags section ... you'll find it.

BTW : VAR ... just means " other name for " ( something like # DEFINE ... Ooops ... )

Alain

Bruce
- 16th May 2011, 14:24
PIR3.1 is bit #1 in the PIR3 register. Capture VAR PIR3.1 just creates an alias for this interrupt flag bit.

davidcope
- 16th May 2011, 17:37
PIR3.1 is bit #1 in the PIR3 register. Capture VAR PIR3.1 just creates an alias for this interrupt flag bit.

Many thanks Bruce that makes it perfectly clear. :)

@Alain Thanks too. Apologies for mentioning the "C" letter BTW!

As for the RTFM thing. Please, it's a cop out. It's the easiest thing in the world to belittle someone for not finding the information (or quite understanding how to frame a question) you may think should be obvious, but in the context of the wider picture may not be to the person asking the question. And of course the "Problem" may only be one of several or several tens that the poster may be concurrently dealing with at the time of the question.

Sorry Alain, I really don't mean to appear to be negative after you've been kind enough to reply to my question, it's just that RTFM is so often used to limit learning, not encourage it.

So yes, I see now that Section 10.2 (Page 102) "PIR Registers" of the data sheet provides the information for the code I was referring to. That's one problem down...many more to go. :)

Thanks again for your inputs. David.