Arduino Forum

Using Arduino => Microcontrollers => Topic started by: JChristensen on Jan 25, 2013, 05:32 pm

Title: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 05:32 pm
I have a 1 Hz square wave connected to INT1, free-running so it is asynchronous WRT the ┬ÁC. Using the following sketch, the interrupt appears to fire as soon as it's enabled; I consistently see 999 as the first number output. Then the time to the second interrupt varies, and is less than one second. I was expecting the opposite, i.e. the first number should be some random value less than one second, and then the rest should be one second apart.

I've been poring over the datasheet and I can't find anything that would indicate this behaviour, hoping someone can help. I've checked the interrupt flag (INTF1 in EIFR) to ensure it's not set going in, and it's not, have tried resetting it anyway, no joy, etc. etc.

Code: [Select]
#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/

#define LED LED_BUILTIN

volatile boolean int1Flag;
boolean ledState;

void setup(void)
{
    delay(1000);
    Serial.begin(115200);
    pinMode(LED, OUTPUT);

    EICRA = _BV(ISC11);          //external interrupt 1 on falling edge
    EIMSK = _BV(INT1);           //enable external interrupt 1
}

void loop(void)
{
    if (int1Flag) {
        int1Flag = false;
        digitalWrite(LED, ledState = !ledState);
        Serial << millis() << endl;
    }
}

ISR(INT1_vect)
{
    int1Flag = true;
}


Typical output that I see:

Code: [Select]

999
1652
2652
3652
4652
--------
999
1056
2056
3056
4056
-------
999
1841
2840
3841
4840


I would have expected:

Code: [Select]
652
1652
2652
3652
4652
--------
56
1056
2056
3056
4056
--------
841
1841
2840
3841
4840
Title: Re: External interrupt fires early?
Post by: fungus on Jan 25, 2013, 06:03 pm
Did you try setting the pin as an input and using the pullup resistor?
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 06:28 pm

Did you try setting the pin as an input and using the pullup resistor?


Yes I did, it didn't make a difference.
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 06:55 pm
I also tried using an Arduino to supply the 1 Hz signal, same results. Would have been surprised if that had made a difference, but it eliminates another possibility, if a remote one.
Title: Re: External interrupt fires early?
Post by: Riva on Jan 25, 2013, 07:06 pm
what happens if you put varying length delays before enabling the interrupt, does that alter the 999 value.
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 07:27 pm

what happens if you put varying length delays before enabling the interrupt, does that alter the 999 value.


Yes, for example, with delay(2000) instead, I get:

Code: [Select]
1999
2787
3787
4788
5788
Title: Re: External interrupt fires early?
Post by: Coding Badly on Jan 25, 2013, 07:46 pm

Board?  (I don't think that matters but, at this point, it's a good idea to assume nothing.)
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 07:55 pm


Board?  (I don't think that matters but, at this point, it's a good idea to assume nothing.)


Chip-on-a-breadboard with Optiboot, telling the IDE it's an Uno :D  Would be easy enough to try a real one, though.
Title: Re: External interrupt fires early?
Post by: Coding Badly on Jan 25, 2013, 08:14 pm

Try this...

Code: [Select]
    noInterrupts();
    EICRA = _BV(ISC11);          //external interrupt 1 on falling edge
    EIMSK = _BV(INT1);           //enable external interrupt 1
    EIFR = _BV(INTF1);
    interrupts();
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 25, 2013, 10:45 pm

Try this...


That did it! I had tried resetting the interrupt flag, and I had tried inhibiting interrupts, but I hadn't put them together. Investigating further, it sure looks like the act of enabling the INT1 interrupt causes the interrupt flag to be set. This code

Code: [Select]
#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/

#define LED LED_BUILTIN

volatile boolean int1Flag;
boolean ledState;
uint8_t eifr1, eifr2;

void setup(void)
{
    delay(1000);
    Serial.begin(115200);
    pinMode(LED, OUTPUT);

    cli();
    EICRA = _BV(ISC11);          //external interrupt 1 on falling edge
    eifr1 = EIFR;
    EIMSK = _BV(INT1);           //enable external interrupt 1
    eifr2 = EIFR;
    EIFR = _BV(INTF1);
    sei();
   
    Serial << "EIFR1=0x" << _HEX(eifr1) << " EIFR2=0x" << _HEX(eifr2) << endl;
}

void loop(void)
{
    if (int1Flag) {
        int1Flag = false;
        digitalWrite(LED, ledState = !ledState);
        Serial << millis() << endl;
    }
}

ISR(INT1_vect)
{
    int1Flag = true;
}


results in

Code: [Select]
EIFR1=0x0 EIFR2=0x2
1321
2322
3322
4323


I also discovered that using the Arduino interrupt functions causes the same undesirable behavior as my original code, so I feel just a little better:

Code: [Select]
#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/

#define LED LED_BUILTIN

volatile boolean int1Flag;
boolean ledState;

void setup(void)
{
    delay(1000);
    Serial.begin(115200);
    pinMode(LED, OUTPUT);
    attachInterrupt(1, myInt1Handler, FALLING);
}

void loop(void)
{
    if (int1Flag) {
        int1Flag = false;
        digitalWrite(LED, ledState = !ledState);
        Serial << millis() << endl;
    }
}

void myInt1Handler(void)
{
    int1Flag = true;
}



Title: Re: External interrupt fires early?
Post by: Coding Badly on Jan 26, 2013, 01:36 am
That did it!


Wow.  That's strange.  And annoying.  I know some of the interrupt types are automatically cleared when enabled.  I wonder if External Interrupts work differently by design, accident, or mistake.
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 01:42 am

That did it!


Wow.  That's strange.  And annoying.  I know some of the interrupt types are automatically cleared when enabled.  I wonder if External Interrupts work differently by design, accident, or mistake.


Agree, not at all what I would have expected. Clearing on enable might make a little more sense. I wondered if it was some kind of latent thing, maybe while the bootloader did its thing or whatever, but it just doesn't make sense if the interrupt wasn't enabled along the way.

I sure appreciate your help on this one!
Title: Re: External interrupt fires early?
Post by: retrolefty on Jan 26, 2013, 01:43 am
Does changing the interrupt direction to raising edge give the same behaviour?

Quote
//external interrupt 1 on falling edge


Lefty
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 01:59 am

Does changing the interrupt direction to raising edge give the same behaviour?

Quote
//external interrupt 1 on falling edge


Lefty


Yes, I tried that too. Just verified it again as well.

I found these two comments in an application note (http://www.atmel.com/Images/doc8468.pdf), but they don't fit the scenario here. It looks to me like enabling the interrupt causes the interrupt.

Quote
4. When changing the ISCn bit, an interrupt can occur. Therefore, it is
recommended to first disable INTn by clearing its Interrupt Enable bit in the
EIMSK Register.
5. Before enabling an interrupt, it is recommended to clear the flag bit of the
corresponding interrupt because when the flag bit is set, the interrupt will be
triggered the moment we enable the interrupt.
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 02:06 am
AHA! That app note was right. Setting the sense control bit (ISCxx) causes the interrupt. Using the code below, I didn't see it earlier, but by adding delay(1) I see it now. Wasn't seeing it before no doubt because the code was picking up EIFR before the four clock cycles it takes to respond to an interrupt.

Code: [Select]
#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/

#define LED LED_BUILTIN

volatile boolean int1Flag;
boolean ledState;
uint8_t eifr1, eifr2;

void setup(void)
{
    delay(1000);
    Serial.begin(115200);
    pinMode(LED, OUTPUT);

    cli();
    EICRA = _BV(ISC11);          //external interrupt 1 on falling edge
    delay(1);            //   <------- AHA!
    eifr1 = EIFR;
    EIMSK = _BV(INT1);           //enable external interrupt 1
    eifr2 = EIFR;
    EIFR = _BV(INTF1);
    sei();
   
    Serial << "EIFR1=0x" << _HEX(eifr1) << " EIFR2=0x" << _HEX(eifr2) << endl;
}

void loop(void)
{
    if (int1Flag) {
        int1Flag = false;
        digitalWrite(LED, ledState = !ledState);
        Serial << millis() << endl;
    }
}

ISR(INT1_vect)
{
    int1Flag = true;
}


Output:

Code: [Select]
EIFR1=0x2 EIFR2=0x2
1909
2910
3909
4910
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 02:21 am
So the cli() and sei() can be dispensed with, and the minimal code to do the job ends up like this, assuming the interrupt is disabled going in:

Code: [Select]

   EICRA = _BV(ISC11);          //external interrupt 1 on falling edge
   _delay_loop_1(2);            //allow time for the interrupt caused by setting ISCxx (#include <util/delay_basic.h>)
   EIFR = _BV(INTF1);           //clear the interrupt flag
   EIMSK = _BV(INT1);           //enable external interrupt 1


Edit 26Jan2013: Playing with this again today, it looks like the call to _delay_loop_1() is not needed. Might have gotten myself wrapped around the axle a bit there last night :smiley-red:


I wonder if External Interrupts work differently by design, accident, or mistake.


Knowing what I know now, it seems it would have to be one of the latter. Maybe someone can think of a reason that it should work this way, but at the moment, I am at a loss  :smiley-roll:
Title: Re: External interrupt fires early?
Post by: Coding Badly on Jan 26, 2013, 07:51 am
Quote
...the minimal code to do the job ends up like this...


Nice!
Title: Re: External interrupt fires early?
Post by: westfw on Jan 26, 2013, 10:36 am
Quote
Maybe someone can think of a reason that it should work this way

From a HW point of view, I can imagine circuitry that detects falling and rising edges, as well as levels, that would be active all the time (whether or not interrupts are enabled.)  Enabling the pin change interrupt causes an immediate interrupt, because sure enough there HAS BEEN a falling edge some time in the past, even though it wasn't set up to cause interrupts.  I think the timer interrupts work like this too.  If you've been running the timer for a while without paying attention to the pieces that cause interrupts, the "overflow" bit will be set (you never cleared it, after all) and will cause an interrupt as soon as you enable timer overflow interrupts.
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 03:25 pm

Quote
Maybe someone can think of a reason that it should work this way

From a HW point of view, I can imagine circuitry that detects falling and rising edges, as well as levels, that would be active all the time (whether or not interrupts are enabled.)  Enabling the pin change interrupt causes an immediate interrupt, because sure enough there HAS BEEN a falling edge some time in the past, even though it wasn't set up to cause interrupts.  I think the timer interrupts work like this too.  If you've been running the timer for a while without paying attention to the pieces that cause interrupts, the "overflow" bit will be set (you never cleared it, after all) and will cause an interrupt as soon as you enable timer overflow interrupts.


Good point. And indeed as one example the datasheet has a similar warning that changing the settings for the analog comparator can cause an interrupt. Unless I missed it though, they must have forgotten to mention this one in connection with external interrupts.

I did google around and also searched the forum a bit to see if anyone had noticed this before, and didn't come up with anything. On the one hand that's hard to believe, but being a sort of boundary condition thing, people might just tend to code around it if it makes a difference to them rather than digging into it.

As I also discovered (code in #9 above), the Arduino interrupt functions also cause this spurious interrupt. Do you guys think it's worth raising an issue on? Seems pretty serious to me, I don't expect a freebie interrupt just because I enable them. Simple enough fix, but I was looking at WInterrupts.c and I see it would have to be made in a couple dozen places! :smiley-eek:
Title: Re: External interrupt fires early?
Post by: JChristensen on Jan 26, 2013, 03:35 pm
I see there already is an issue, #510, https://github.com/arduino/Arduino/issues/510
Title: Re: External interrupt fires early?
Post by: Coding Badly on Jan 26, 2013, 09:11 pm
From a HW point of view, I can imagine circuitry that detects falling and rising edges, as well as levels, that would be active all the time...


But the interrupt flag can be cleared when the interrupt is enabled.  I tested either a timer related interrupt or the pin change interrupts for that behaviour (can't remember which or why) and the flag is indeed cleared when the interrupt is enabled.

Out of paranoia, I have a tendency to always clear the flag.

I'm starting to suspect the behaviour is by-design.  Working the way it does eliminates a race condition in applications that have to be able to enable/disable external interrupts (an RTOS might need to do that).