## Finding Frequency from Period, speed up process

18 posts / 0 new
Author
Message

I need to calculate frequency in centy Hz (eg. 0.01 Hz precision) from a variable period.
I'm measuring line frequency from timer counter since last zero cross.
Target is Atmega328.

Currently i do the calculation like this: HZ=K/p;

//cHz range 4444-6666 (44.44-66.66Hz);
//PERIOD range 360000-240000 (320000 is the most occurence expected on 50hz line)
#define F_CPU 16000000
unsigned long cHZ,PERIOD ;
cHZ = (unsigned long)(F_CPU*100) / PERIOD ;

How to improve speed on this specific division?
F_CPU is constant and should not change. Code portability is not an issue.

Quote:

How to improve speed on this specific division?

OK, I'll bite: There is only a period every 20ms at 50Hz; there is only a zero-cross every 10ms at 50Hz.

If we come up with a method that saves you e.g. 10us, that is 1% of the time between calculations. Is that really critical?

I'd further question some of the assumptions/requirements. You talk of zero crossings, but the variable is PERIOD. I'd wander whether the zero-crossing point is exactly symmetrical, rising and falling.

Now, what are you going to do with this exact measurement? No-one can distinguish different values displayed every few milliseconds.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Hi theusch,
The requirement is for a lib to monitor various line parameters.
https://github.com/chaveiro/Emon...

The requested calculation is taking about 587cycles (36.69us) on a ~2666cycles window in ADC ISR already overcrowded with other calculations. (50hz/120samples per second at 16Mhz cpu = ~2666 cycles per sample)
If we can improve this particular division we can increase sample rate of sensor data and get more precision for the lib.
Freq is supposed to be calculated every cycle, then integrated than average is calculated outside critical code at required user event.

Interesting. If there are only 20 periods every second, then why do you need to do the calculation of the same information over and over? Have "new data" flag...

[An interesting project, indeed. And we've done production "AVR-only" monitors. But for precision apps, we apply "energy monitor" chips such as ADE7xxx that have all the good stuff already inside.]

If indeed your aim is to monitor 50Hz mains lines to 0.01Hz, then the possible values are probably very limited. 49Hz to 51Hz is only 200 values.

Even your stated range of 44-66 still gives output values much less than 16 bits.

So you can "speed up" by not doing the full 32-bit divide. One way would be to use a "difference from nominal" and then work with that. (When doing that, consider the useful resolution in the timer counts--120000 range translates into ~2400 output values.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Quote:
(When doing that, consider the useful resolution in the timer counts--120000 range translates into ~2400 output values.)
In fact, 6666-4444 is only 2222 values. Put it in a table and do a binary search. That is only ~11 compares.

Regards,
Steve A.

The Board helps those that help themselves.

Plot the function between the two limits, the slope should vary by only 25%. If that is good enough just do a linear extrapolation using the slope at the typical operating point. Else use that to interpolate between entries in a smaller table. Having the entries spaced by 256 makes the lookup and interpolation faster. I'd expect sub-percent accuracy that way, but if that is still not good enough a second order correction can be applied.

You could re-scale your period timer so that it gives you a period in microseconds (20mS, 20000uS @ 50HZ). This will give you a 16 bit divisor over your frequency range. Then you can perform a 1/n division to give you frequency at the desired resolution (100,000,000/20000 = 5000). This is a 32/16 division will half your calculation time. However, ANSI C will not do this directly since it wants to perform a 32/32 division. You will have to write your division routine in assembly, then tie into C. Or, your compiler may have an extension to allow this division.

chaveiro wrote:

Freq is supposed to be calculated every cycle, then integrated than average is calculated outside critical code at required user event.

Err, so why not simply measure time, and average time, and only flip to frequency when the user asks ?

As you have found, 32 bit maths is not fast, and there is no real magic fix.

Also check the compiler is doing F_CPU*100 at compile time.
If it is not, simplify the expression.

Quote:

The requested calculation is taking about 587cycles (36.69us) on a ~2666cycles window in ADC ISR already overcrowded with other calculations. (50hz/120samples per second at 16Mhz cpu = ~2666 cycles per sample)
If we can improve this particular division we can increase sample rate of sensor data and get more precision for the lib.

This seems quite strange.
Why (re)do maths 120 times every cycle.
Even when you move it to once per 20ms, expecting to resolve a single cycle to LSB .01Hz, means you presume mains zero cross has under 4us of noise, which sounds optimistic ?

That calculation was done only every line period. But the time window to do it was the same 2666cycle due to continuous monitoring.
I ended up offloading division from ISR to user code. In ISR only save the differences on PeriodDiff accu var.
Thank you all for the brain storm!

I still have 3 issues now that i'd like your comments :

1- Timer1 as 1-2us jitter. Could not solve with sleep trick as after sleep first adc read is garbage.

2- At Timer1 COMPA int it starts a ADC read. But i happen to receive that read in ADC int at two different timer1 counter values alternated between the two on each time adc is stared. No other int is happening. This happens in simulator at last.

3- Improve this formula to be faster by using integer/shifting math:
edited:
//filtered = 0.996 *(lastFiltered+ ( sample-lastSample ) );

);

Last Edited: Mon. Jun 10, 2013 - 04:54 AM

Quote:
1- Timer1 as 1-2us jitter.
Quote:
3- Improve this formula to be faster by using integer/shifting math:
Hard to do since your formula and your line of code don't match each other, but 0.996 is ~1020/1024.

Regards,
Steve A.

The Board helps those that help themselves.

Koshchi,
1- it's skeeing the sampling point by that ammount. software pll tries to lock on zero cross constantly to compensate that. But is there a fix?

3- I've edited the post above. The value can be 255/256 it's not important as long as is close. It implements a High pass filter to remove dc from measured voltage.

Quote:

1- it's skeeing the sampling point by that ammount.

So, how is that sampling done?

I doubt very much that "timer1 jitters". Your code's >>reaction<< may have jitter. (There is another very recent thread on exactly this.) Other ISRs might be finishing; mainline code may be getting an atomic value; current instruction finishing; ...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

I guess I am missing part of the concept here.

If you wish to measure the frequency why are you using an inherently slow ADC with a "large" step size to sample the input signal?

If you use the ADC, then what threshold constitutes a new cycle?

Given the input attenuator, and the 10-Bit ADC, what is the delta V for that ADC determined threshold? (And hence the delta T, and hence the delta period calculation?)

Which leads us, finally, to the question already asked, what difference does 1 uS make given the ADC is the input trigger source? (Assuming it is)

You also mentioned using a software HPF to remove the DC. It wasn't clear where this occured witin the signal processing chain.

It might be easier to isolate and attenuate the incoming Mains, and then generate the ZC in hardware, and feed a digital signal into the micro's Timer/Counter to read the signal's period if the current method is not meeting your spec's.

JC

DocJC wrote:
I guess I am missing part of the concept here.

If you wish to measure the frequency why are you using an inherently slow ADC with a "large" step size to sample the input signal?

I can only guess the OP is trying to lock-onto the mains, by sampling multiple points, and then trying a 'best fit' to a virtual sine signal.

DocJC wrote:

It might be easier to isolate and attenuate the incoming Mains, and then generate the ZC in hardware, and feed a digital signal into the micro's Timer/Counter to read the signal's period if the current method is not meeting your spec's.

It is non-trivial to make a precise ZCD, especially if preserving Zero phase matters.
I can see a good reality check, for best Frequency measurement is to chain two integrators, each giving a 90' phase shift and good high frequency rejection, and then zero cross from that.
Harmonics are only going to add jitter to the Freq values, and software massaging can only do so much.

The link above was to an Energy Monitor project. I did not look at the link the first read through, and building an Energy Monitor wasn't explicitly stated in the OP's initial post.

There are several prior Threads on this subject, with the general concensus being it is a nontrivial task, and there are chips that can do this, and do it well.

So one can use a custom designed, purpose built chip, or have rather extensive signal processing hardware to feed the micro and get the data, or compromise in some parameter's accuracy and do the project with little input signal pre-processing and live with the inaccuracies and compromises.

For an energy monitor one will have to measure the voltage and the current, hence the ADC usage.

I suggested above that using the ADC might not be the "best" method of determining the zero crossings. A hardware ZC to digital signal feeding the Timer/Counter and letting the T/C module do the counting would be "easier", especailly as the OP wants +/- 2 uSec measurement accuracy to meet the 0.01 Hz accuracy requirement while also doing other processing.

If one has to use the ADC input for the ZC detection then tracking both the neg and positive half cycles and interpolating the ZC point would be an option, although more computationally involved, espeoially if several data points were used on each side of the ZC to plot the curve.

Clearly in the analog domain one can get arbitrarily close to the ZC point. (Think input clamps, op-amps, and comparators) With a simple clamp and an opto-coupler (used digitally, not in an analog mode), one can measure the ZC with a relatively fixed delay. Knowing the delay, and using a software PLL, one can know when the last ZC occurred, and when the next one is expected to occur.

I guess like any project there is a trade off between precison, accuracy, complexity, cost, etc.

It would be nice if the OP provided a schematic of the input circuitry. Otherwise I think the answer to the OP's original question about speeding up the calc's was answered above.

JC

An update, i've settled for this code for the High pass filter, an improvement in speed of >4.7x:

```//  y[n] = 0.996*y[n-1] + 0.996*x[n] - 0.996*x[n-1]
// Algorith float point - >169cpu cycles
/*
*/

// Algorithm Alt A - 36cpu cycles
signed int TempI;
signed long TempL;