I'm trying to understand how to set up an overflow interrupt on ATmega328 TIMER1 to measure a frequency.
I'm feeding pin PD4, which is where TIMER1 is externally clocked, with a 503kHz square wave (4.8Vpp).
Given that TIMER1 is 16 bit, I would assume that it would overflow 7 times every second. My reasoning is the following:
$$ number\;of\;overflows = \frac{f}{TIMER1\;max\;count} = \frac{503,000Hz}{65,536} = 7.67$$
However, my program counts 988 overflows instead. That's 128 times more than I expected. Why??
I'm using the following code:
volatile unsigned long int running;
ISR(TIMER1_OVF_vect) {
running++;
}
void setup()
{
Serial.begin(9600);
// set up the 16 bit timer as an external frequency counter:
TCCR1B |= (1 << CS10)|(1 << CS11)|(1 << CS12); // External clock, rising edge
TIMSK1 |= (1 << TOIE1); // Enable overflow interrupt
sei(); // enable global interrupts
}
void loop() {
running = 0;
delay(1000); // wait 1000ms
Serial.println(running);
}
Below is the external clock signal on the scope:
External clock scope shot
The schematic I'm using is this one below. The inductor is 220uH instead of 68uH.
Schematic
2 Answers 2
As per page 140 of the datasheet the flag that must be cleared is called \$\mathtt{TOV1}\$ and is the LSB of the register \$\mathtt{TIFR1}\$. That bit should be cleared automatically but that feature might be disabled to use nested interrupts, which seems the case to me. I would have expected an higher value for running, but that's an int and might well be 8 bit wide, so it's overflowing like crazy (that's even signed!).
Try adding this line as the first thing in your overflow ISR:
TIFR1 &= 0x01;
Maybe something like:
TIFR1 &= 1 << TOV1;
will work too, but who knows? You should dive into headers. Try the first solution, that should work.
-
\$\begingroup\$ Tried both lines, but didn't work. It turns out that the problem was something else. Somehow TCCR1A comes set to 0x01 instead of 0x00 in my setup. See my own answer below for more details. Thanks for the effort and for digging into the datasheet, though! +1 \$\endgroup\$Ricardo– Ricardo2014年08月15日 22:47:27 +00:00Commented Aug 15, 2014 at 22:47
Thanks to Mewa's comment, which I copy below, I found what the problem was.
Check out Table 16-4 in the datasheet for your micro, and make sure you are in Normal mode. Otherwise, the overflow flag could be triggering for other reasons.
Somehow, in my programming environment, the TCCR1A
is set initially to 1
and not to 0
as I assumed.
I verified and fixed the issue with the following lines:
Serial.println(TCCR1A); // <- this line outputs 1
TCCR1A = 0; // <- this line fixed the problem - I now get 7 to 8 overflows per second
TCCR1B |= (1 << CS10)|(1 << CS11)|(1 << CS12);
TIMSK1 |= (1 << TOIE1);
If I understand it correctly, that means that bit WGM10 as set to 1
, setting my TIMER1 to Mode 1 (PWM, Phase Correct, 8-bit) instead of the intended Mode 0 (Normal). The result was that the timer was overflowing at 511 and not 65535 as I assumed.
Thanks to all of you who helped me figure this one out!
-
1\$\begingroup\$ And that's why I alsways set all the bits I need, also if on reset they should be as I like. \$\endgroup\$Vladimir Cravero– Vladimir Cravero2014年08月15日 22:59:26 +00:00Commented Aug 15, 2014 at 22:59
F_CPU
, otherwise the compiler will default to 1MHz for use with delay. It looks like you are mixing AVRgcc with Arduino API. Try using_delay_ms(1000)
, which is the inline implementation given in the AVR header file delay.h. The Arduino implementation probably just calls this routine anyway, however. \$\endgroup\$