5
\$\begingroup\$

I've run into an issue where, it appears, my interrupt only fires once, then simply refuses to fire again on my ATMega32U2. I have the following (stripped down) code:

void init(void) {
    DDRB = 0xff;
    PORTB = 0x00;

    TCCR0A = (1 << WGM01);
    // 1024 prescaler
    TCCR0B = ((1 << CS02) | (1 << CS00));
    // Interrupt every 4096 clocks
    OCR0A = 3;
    // Enable timer compare match interrupt
    TIMSK0 != (1 << OCIE0A);
}

ISR(TIMER0_COMPA_vect) {
    PORTB++;
}

int main(void) {
    init();

    sei();

    while(1);

    return 0;
}

When I plug an LED into each pin of PORTB, the first pin is high, while the rest are low. The most I can deduce from this is PORTB is incremented to 0x01 by the interrupt firing once, then left alone as the interrupt never fires again using the TIMER0_COMPA_vect vector.

However, if I replace the compare interrupt with an overflow interrupt (TIMER0_OVF_vect) and configure the registers differently, it works fine; PORTB cycles through 0 - 255 very quickly as I'd expect. This is my working code with the type of interrupt I don't want:

// Different timer config
void init(void) {
    DDRB = 0xff;
    PORTB = 0x00;

    TCCR0B |= (1 << CS01);

    // Enable timer 0 interrupt
    TIMSK0 |= (1 << TOIE0); 
}

// This time, it's an OVERFLOW vector, not a compare vector
ISR(TIMER0_OVF_vect) {
    PORTB++;
}

int main(void) {
    init();

    sei();

    while(1);

    return 0;
}

I've read what there is to read on Google, however the only fixes posted are logical errors that I don't think I have. I've read this forum post and had a look at the timer configuration registers, and they're exactly the same as my first example. I've also read this question, but that didn't help.

I've been following the tutorial on this page (PDF) for the TLC5940 LED driver chip and am on Chapter 4 (refactoring code to use interrupts). Please note that I don't want to use the ready built library; I'm using this as a learning experience.

\$\endgroup\$
6
\$\begingroup\$

This may just be a typo in your post, but the line where you enable the Timer Compare interrupt says:

TIMSK0 != (1 << OCIE0A);

... when it should say ...

TIMSK0 |= (1 << OCIE0A);

It's subtle, but if that's what your code actually was, then you were comparing TIMSK0 to a constant rather than assigning to it (and therefore never actually enabling the interrupt).

On a sidenote, you can more concisely write:

TIMSK0 |= _BV(OCIE0A);

... instead of ..

TIMSK0 |= (1 << OCIE0A);

in avr-libc, if you're into that sort of thing :-).

\$\endgroup\$
  • \$\begingroup\$ whts that _BV?? I havent seen that.. \$\endgroup\$ – 0xakhil Jan 11 '12 at 11:09
  • 2
    \$\begingroup\$ But (1 << OCIE0A) is standard idiomatic C, and _BV(OCIE0A) uses an ugly unpronounceable macro that I've never seen before. No thanks. \$\endgroup\$ – markrages Jan 11 '12 at 16:52
  • 1
    \$\begingroup\$ That should also be throwing a warning, though I'm not familiar with the compiler it's using. On my setup with GCC (4.5.1), it throws a "value computed is not used" warning. \$\endgroup\$ – shenles Jan 11 '12 at 17:34
  • 2
    \$\begingroup\$ @0xakhil - It's a macro in avr-libc that gets the 'bit value' of the number. For instance, _BV(3) turns into 0b0000 0100, same as ( 1 << 3 ). It's pronounced "underscore Bee Vee". \$\endgroup\$ – Kevin Vermeer Jan 11 '12 at 18:58
  • 1
    \$\begingroup\$ @markrages _BV is used throughout the avr-libc documentation. Using the macro gets rid of a lot of repeating yourself - there is no "information" in the "1 <<" part of the (1 << 3) idiom when your desire is simply to set a single bit at a given position. Less typing may result in fewer run time bugs. For example, it might prevent you from accidentally typing (1 < 3) when you meant (1 << 3) \$\endgroup\$ – vicatcu Jan 17 '12 at 18:26

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.