0
\$\begingroup\$

I am blinking an LED on PORTB5 using timer 0 in mode 7. The time between blinks is computed as $$ \frac{(\text{OCROA}+1) \times \text{prescaler} \times \text{timerCount}}{\text{FCPU}} $$ \$FCPU=16MHz\$. With \$OCROA=124\,ドル \$prescaler=1024\,ドル and \$timerCount=125\,ドル the time turns out to be \1ドル \space second\$ and this works as expected. But with \$OCROA=16\,ドル \$prescaler=1\,ドル and \$timerCount=2956793\,ドル I would expect the time to be \$~3.14 \space seconds\,ドル but I am getting something around \12ドル \space seconds\$.

Code that works as expected:

#include <avr/interrupt.h>
#include <stdint.h>
volatile uint32_t timerCount = 0;
ISR(TIMER0_COMPA_vect)
{
 ++timerCount;
}
int main(void)
{
 DDRB |= 1 << DDB5;
 TIMSK0 |= 1 << OCIE0A;
 TCCR0A |= 1 << WGM00 | 1 << WGM01;
 TCCR0B |= 1 << WGM02;
 OCR0A = 124;
 sei();
 TCCR0B |= 1 << CS02 | 1 << CS00;
 while(1)
 {
 if(timerCount >= 125)
 {
 PORTB ^= 1 << PORTB5;
 timerCount = 0;
 }
 }
 return 0;
}

Code that does not work as expected:

#include <avr/interrupt.h>
#include <stdint.h>
volatile uint32_t timerCount = 0;
ISR(TIMER0_COMPA_vect)
{
 ++timerCount;
}
int main(void)
{
 DDRB |= 1 << DDB5;
 TIMSK0 |= 1 << OCIE0A;
 TCCR0A |= 1 << WGM00 | 1 << WGM01;
 TCCR0B |= 1 << WGM02;
 OCR0A = 16;
 sei();
 TCCR0B |= 1 << CS00;
 while(1)
 {
 if(timerCount >= 2956793)
 {
 PORTB ^= 1 << PORTB5;
 timerCount = 0;
 }
 }
 return 0;
}

What could be the issue with the latter?

Bence Kaulics
6,47312 gold badges35 silver badges61 bronze badges
asked May 12, 2015 at 20:23
\$\endgroup\$
2
  • \$\begingroup\$ "2956793" is out of range for a 16 bit int. Declare this as an unsigned long constant instead. Fix that, and get back to me :) \$\endgroup\$ Commented May 12, 2015 at 22:02
  • \$\begingroup\$ You should probably be using overflow interrupts instead by the way \$\endgroup\$ Commented May 12, 2015 at 22:06

1 Answer 1

2
\$\begingroup\$

In your second setting the timer is incrementing once for every clock cycle (prescaler is 1), but it only counts to 16. The AVR executes one instruction per clock cycle (except for branches) so there are less than 16 instructions available between each PWM interrupt.

The execution time of the ISR is longer than this (it may be variable as well) and that is causing your code to periodically miss interrupts, extending the apparent time it is taking to count to your target value. To fix the problem you need to either use the prescaler and count to a higher value, or if you can't do that (if you are using the hardware PWM generator), look at using OCR1B with a longer count to generate the interrupts instead.

Also you should disable interrupts when you clear timerCount because this is a 32-bit value that requires multiple instructions to update on an 8-bit platform.

answered May 12, 2015 at 20:48
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.