0
votes

I tried to create a CTC timer interrupt on my ATmega32U4 leonardo board. When I continuously check the value of OCF1A I have no problem detecting when the output reaches the desired value, however once I move the code into an interrupt, the interrupt never triggers.
Timer setup:

#include <avr/io.h>

void setupTimer()
{
    TCCR1B |= (1 << WGM12); // CTC mode
    TCCR1B |= ((0 << CS10) | (0 << CS11) | (1 << CS12)); // set up prescaler
    OCR1A = 6249; // 100 ms set up output compare value for interrupt
    TIMSK1 |= (1 << OCIE1A); // enable interrupt on clock compare
}

The loop that works:

setupTimer();
for (;;) {
    if (TIFR1 & (1 << OCF1A)) {
        PORTC ^= (1 << PORTC7);
        TIFR1 = (1 << OCF1A);
    }
}

The interrupt that does not work:

#include <avr/interrupt.h>

ISR(TIMER1_COMPA_vect) {
    PORTC ^= (1 << PORTC7);
}

I must be missing something as from what I have seen in the tutorials the above code should work. One interesting observation here is that if I have both the loop and the interrupt in my code at once if I call sei(), the LED does not blink as if the OCF1A register was cleared prematurely.
I'm pretty sure it is irrelevant in this case but the fuses are as follows: E:CB, H:D8, L:FF.

I use avr-g++ to compile and the code is spread out between several files.

3
I find code much easier to read if it uses the _BV() macro for bitmasks instead of manually shifting 1.Cactus

3 Answers

2
votes

Given that someone got here through google two years after this question was asked I suppose I should share my own findings on this matter.

The code provided in my question is correct and assuming that there is a call to sei()somewhere after the setupTimer() the interrupt should trigger correctly. The issue was just as c0redumb described in his answer - the bootloader was messing with some registers and thus preventing the code from running correclty. However my solution to this problem was slightly different as in my case, the interrupt would not trigger even after unplugging and re-plugging the board (it is possible that the bootloader has changed in the two years since I have asked this question).

The simplest way to prevent a conflict between the code and the bootloader is to simply remove the bootloader. By using USBasp programmer one can simply load their own code onto the board and thus be sure that it is the only thing running on the CPU.

0
votes

You have two problems:

  1. You need to make sure main() doesn't return even when it's not doing anything except waiting for the interrupt

  2. You need to enable interrupts via sei() after setting everything up.

Here's a working example (I changed the LED port to PB5 because I've tested this on an Arduino Uno and that already has an LED built-in)

#include <avr/interrupt.h>
#include <avr/io.h>

void main ()
{
    DDRB |= 1 << DDB5;

    TCCR1B |= 1 << WGM12;
    TCCR1B |= 1 << CS12;
    OCR1A = 6249;
    TIMSK1 |= 1 << OCIE1A;

    sei();
    for(;;);
}

ISR(TIMER1_COMPA_vect)
{
    PORTB ^= 1 << PORTB5;
}
0
votes

This issue puzzles me today as well. Through search I found your question. I did some more searches all around and found no answer to this one. I had thought that I must have forgotten to enable some circuit or set some flags. Finally, with an LED as my debugger, I have found the cause.

The problem is with the bootloader, not your code. To get it to work, you just unplug the board from USB (after having written code through bootloader), and re-plugin so the bootloader jumps directly to your code at powerup, and it works there. The bootloader must have done some fancy footwork in uploading the code, and didn't work all well afterward in this situation.

As a reference, I used a ProMicro board which I believe has Caterina bootloader same as the Leonardo board you used.