cinterruptavratmel

AVR Timer and Hardware interrupts


I try to activate a timer at the first detection on falling edge of hardware interrupt detection and to deactivate the timer after 8 runs. But when I stop the timer, the hardware interrupt triggers again and starts the timer immediately.

In the image you can ignore the blue signal. The purple signal is timer1 toggling the pin. Green is hardware interrupt toggling the pin.

All it has to do is trigger at the first falling edge, then toggling a pin in a period of time.

My question is: why does hardware interrupts trigger twice?

Signals

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

volatile int cnt = 0;
uint8_t data[8];

int main(void)
{
    // TIMER 1
    DDRB |= (1 << PORTB0);      // Set LED as output
    DDRB |= (1 << PORTB1);      // Set LED as output
    DDRB |= (1 << PORTB2);      // Set LED as output
    DDRD &= ~(1 << PORTD2);     // Set LED as INPUT
    PORTD |= (1 << PORTD2);     // PULLUP resistor
    
    TCCR1B |= (1 << WGM12);     // Configure timer 1 for CTC mode
    TIMSK1 &= ~(1 << OCIE1A);   // Disable CTC interrupt
    //TIMSK1 |= (1 << OCIE1A);  // Enable CTC interrupt
    OCR1A   = 1200;             // Set CTC compare value to 15873Hz at 16MHz AVR clock, with a prescaler of 1
    //TCNT0 = 0;
    TCCR1B |= ((1 << CS10));        // Start timer at Fcpu/1
    
    // Interrupt
    EICRA |= (1 << ISC01) | (1 << ISC11);       //ENABLE INT0 and INT1 ON falling EDGE
    EIMSK |= (1 << INT0);       //ENABLE INT0
    EIMSK |= (1 << INT1);       //ENABLE INT0
    sei();                      //  Enable global interrupts

    while (1)
    {
    }
}

ISR(TIMER1_COMPA_vect)
{
    cnt = cnt + 1;
    PORTB ^= (1 << PORTB0);                 // D8       

    if (cnt > 7)
    {
        TIMSK1 &= ~(1 << OCIE1A);               // stop CTC interrupt
        EIMSK |= (1 << INT0);                   //Enable INT0
        EIMSK |= (1 << INT1);                   //Enable INT0
        return;
    }
}

ISR(INT0_vect)                  //External interrupt_zero ISR
{
    EIMSK &= ~(1 << INT0);      //Disable INT0
    PORTB ^= (1 << PORTB2);     // Toggle the LED
    TCNT1 = 0;
    cnt = 0;
    TIMSK1 |= (1 << OCIE1A);    // Enable CTC interrupt
}

ISR(INT1_vect)                  //External interrupt_zero ISR
{
    PORTB ^= (1 << PORTB2);     // Toggle the LED
    EIMSK &= ~(1 << INT1);      //Disable INT0
    TCNT1 = 0;
    cnt = 0;
    TIMSK1 |= (1 << OCIE1A);    // Enable CTC interrupt
}

Solution

  • The falling edge sets the interrupt flag even if you disable it. This is called a "pending" interrupt. As soon as the interrupt is enabled, its service routine is called (given that all other enabling conditions are met).

    You need to clear this pending flag before you enable the interrupt.