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?

\$\endgroup\$
  • \$\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\$ – KyranF May 12 '15 at 22:02
  • \$\begingroup\$ You should probably be using overflow interrupts instead by the way \$\endgroup\$ – KyranF May 12 '15 at 22:06
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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.