## frequency measurement, overflow interrupt odd

13 posts / 0 new
Author
Message

I am measuring a frequency of a square wave using the input capture (I'm using an ATMEGA128RFA1).

Basically for the first falling edge of the waveform I set the timer to 0, and then for the second I record the input capture value. Now in order to be able to use at low frequencies I need to count how many times I overflow. That way my actual period is:

overflowcount*65535 + "capture value"

right? For some reason though this yields half the frequency, so by dividing my overflow count by 2 I get the correct frequency in my program. Why is this? Does this mean my overflow interrupt is firing twice? This is the ISRs I am using:

ISR(TIMER1_CAPT_vect)
{
capture = ICR1;	// Read input capture value

if(trans == 0)	// First falling edge
{
TCNT1 = 0; // Set the timer equal to 0
}
else  // Second falling edge
{
samples[reading] = capture;	// Store the capture value
samples[reading + 1] = overflowcount / 2;	// Store the amount of times we have overflowed
overflowcount = 0;		// Reset overflow count
}

trans ^= 1;

}

// Is triggered on overflow
ISR(TIMER1_OVF_vect)
{
overflowcount++;
}

"samples" holds the capture value in the first position, and overflow count in the next, then in my program I calculate the period as I mentioned above, then the frequency from that. Why do I need to divide by 2?

Quote:
Basically for the first falling edge of the waveform I set the timer to 0,
You should not do this, it reduces the accuracy of the reading. Instead you should read the current value of the timer, then subtract that value from the next reading to get the period.

Regards,
Steve A.

The Board helps those that help themselves.

Ok I can do that, but still why are my period overflows doubled?

You didn't say anything about clock frequency, timer setup or variable declarations so how do you expect anybody to be able to answer that ?

Sid

Life... is a state of mind

Well clock frequency shouldnt matter for my question but it is running at 16 mHz no prescaler, normal mode running to 65,535 and then overflowing (and triggering shown overflow interrupt) and so im counting how many times it overflows. That times 65,535 plus capture value would yield the period (16000000/divide by period yields frequency). But my expected value of period is twice what it should be... unless im making a mistake in my logic

I think the magic number you want is 2^16 = 65536. also what happens if you get a capture the same time the timer overflows? Which isr fires first? You need extra code to detect for this occurence.

Why do you not clear the overflow count and TCNT together? I think this explains your x2 error. As Koschi explains, you shouldn't reset the timer when using input capture. You should keep a timestamp - the capture value and the current overflow count. On the second edge, calculate the difference.

You should be multiplying by 65536 not 65535. But I can't explain why your overflow count seems double of what is expected, I need to see more code. Though your clearing of overflow seems suspicious, as it is not in sync with your sample period.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

#include
#include

void ginterrupt_enable(void);
void start_measurements(void);
void stop_measurements(void);

unsigned int capture = 0, samples[250], overflowcount = 0, reading = 0;
unsigned char trans = 0, foo = 0;

int main(void)
{
unsigned long frequency = 0, average = 0, power = 0;

unsigned int x = 0;

DDRB = 0;	// Set all of B to inputs
PORTB = 0xFF;	// Pull them high

ginterrupt_enable();	// Enable global interrupts

start_measurements();	// Start frequency measurements

while(1)
{
{
stop_measurements();	// Stop the measurements

average = (65535*samples[5])+samples[4];

frequency = 1600000000/average;
power = (425*frequency)/1000;
start_measurements();

}
}
}

// Enable global interrupts
void ginterrupt_enable(void)
{
sei();	// Enable interrupts globally
}

// Start frequency measurements
void start_measurements(void)
{
TCCR1B |= (1 << CS10);	// Set input capture on rising edge, set prescaler to clk/1 and activate the timer
TIMSK1 |= (1 << ICIE1) | (1 << TOIE1);	// Enable input capture and timer overflow interrupts
}

// Stop frequency measurements
void stop_measurements(void)
{
TIMSK1 &= (0 << ICIE1) | (0 << TOIE1);	// Disable input capture and timer overflow interrupts
TCCR1B &= (0 << CS10);
}

ISR(TIMER1_CAPT_vect)
{
capture = ICR1;	// Read input capture value

if(trans == 0)	// First falling edge
{
TCNT1 = 0; // Set the timer equal to 0
}
else  // Second falling edge
{
samples[reading] = capture;	// Store the capture value
samples[reading + 1] = overflowcount / 2;	// Store the amount of times we have overflowed
overflowcount = 0;		// Reset overflow count
}

trans ^= 1;

}

// Is triggered on overflow
ISR(TIMER1_OVF_vect)
{
overflowcount++;
}

Right now I am only converting data from a pair of the "sample" variables, but eventually this would be replaced by a loop storing is several that could be averaged, principle though...

The wrong position of the overflow count clearing is the cause of your problem (as already suspected by others). You are measuring every second cycle and you clear the overflow count at the end of the measurement, therefore the overflow count includes two cycles.

Stefan Ernst

Your shared variables should be declared volatile.

Your shared 16-bit variables should be accessed in main() with interrupts disabled.

Your average calculation is done with 16-bit math and then assigned to a long.

Your frequency calculation is based on 1,600,000,000 - where did that come from ?

Sid

Life... is a state of mind

So to fix it, overflow should be set to 0 where TCNT1 is set 0 now?

Quote:
Your shared 16-bit variables should be accessed in main() with interrupts disabled.

They are aren't they? the "stop_measurements()" is supposed to do that

Quote:
Your average calculation is done with 16-bit math and then assigned to a long.

So should it be:

average = (65535*(unsigned long)samples[5])+(unsigned long)samples[4];

This was how I did it before but I removed it and it still works, I get the right frequency out.

Quote:
Your frequency calculation is based on 1,600,000,000 - where did that come from ?

Increased the 0s to allow integer representation of a decimal number.

ChaunceyGardiner wrote:
Your average calculation is done with 16-bit math and then assigned to a long.
Not really. The "65535" has type long.

Stefan Ernst

ltkenbo wrote:
Quote:
Your shared 16-bit variables should be accessed in main() with interrupts disabled.

They are aren't they? the "stop_measurements()" is supposed to do that

You don't disable interrupts in stop_measurements(). And even if you did, you're not calling it until after you test the "reading" variable.

ltkenbo wrote:
Quote:
Your average calculation is done with 16-bit math and then assigned to a long.

So should it be:

average = (65535*(unsigned long)samples[5])+(unsigned long)samples[4];

I would have done it like this:

average = 65535L * samples[5] + samples[4];

but sternst points out that that is done automatically and he is usually right. So it was okay the way you had it.

(Also note that, as several has pointed out, you probably want to multiply by 65536 not 65535.)

ltkenbo wrote:
Quote:
Your frequency calculation is based on 1,600,000,000 - where did that come from ?

Increased the 0s to allow integer representation of a decimal number.

I see. You may want to keep in mind that you're close to the limit for a long, then. Add another decimal and you're out of luck.

Sid

Life... is a state of mind