Interrupt not working after detach and re-attach

Hi there,

i’m currently playing around with the sleep mode and i found the following code somewhere

#include <avr/sleep.h>
#include <avr/power.h>


int pin2 = 2;


/***************************************************
 *  Name:        pin2Interrupt
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Service routine for pin2 interrupt  
 *
 ***************************************************/
void pin2Interrupt(void)
{
  /* This will bring us back from sleep. */
  
  /* We detach the interrupt to stop it from 
   * continuously firing while the interrupt pin
   * is low.
   */
  detachInterrupt(digitalPinToInterrupt(pin2));
}


/***************************************************
 *  Name:        enterSleep
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Enters the arduino into sleep mode.
 *
 ***************************************************/
void enterSleep(void)
{
  
  /* Setup pin2 as an interrupt and attach handler. */
  attachInterrupt(digitalPinToInterrupt(pin2), pin2Interrupt, HIGH);
  delay(100);
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  sleep_enable();
  
  sleep_mode();
  
  /* The program will continue from here. */
  
  /* First thing to do is disable sleep. */
  sleep_disable(); 
}


/***************************************************
 *  Name:        setup
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Setup for the Arduino.           
 *
 ***************************************************/
void setup()
{
  Serial.begin(9600);
  
  /* Setup the pin direction. */
  pinMode(pin2, INPUT);
  pinMode(13, OUTPUT);
  
  Serial.println("Initialisation complete.");
}



/***************************************************
 *  Name:        loop
 *
 *  Returns:     Nothing.
 *
 *  Parameters:  None.
 *
 *  Description: Main application loop.
 *
 ***************************************************/
int loops=0;
void loop()
{
  delay(1000);
  loops++;
  
  Serial.print("Awake for ");
  Serial.print(loops, DEC);
  Serial.println(" loops");

  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
  
  if(loops == 3)
  {
    Serial.println("Entering sleep");
    delay(200);
    loops = 0;
    enterSleep();
  }
  
}

The problem is that the interrupt works excatly once and then never again. My guess is that after the detach within the interrupt function, the interrupt activation in enterSleep doesn’t work again.

I’m using a micro arduino build from xsource with settings “Arduino Pro or Pro Mini”, “ATmega328 5V, 16 Mhz”

Do you trigger your interrupt by just putting 5V on pin2 manually? Are you super super super Super fast to remove it?

What do you think happens if the interrupt occurs during the delay(100); in the code below?

attachInterrupt(digitalPinToInterrupt(pin2), pin2Interrupt, HIGH);
  delay(100);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

(Or after the attachInterrupt() and before the sleep_mode() )

Hey, thanks for the answer. I'm trying understand what you say.

For my understanding the following happens:

  1. i let it blink 3 times and then it goes to sleep mode after the interrupt is enabled
  2. I connect 5V and pin2 manually
  3. The interrupt is triggert and disabled (so i don't need to be super fast, it can't be triggert again)
  4. It wakes up (see comment in the sleep function)
  5. It blinks 3 times and again it goes to sleep
  6. i'm not able to wake it up again / the interrupt which should be activated again in the sleep function doesn't work

So i understand that it can't work if the interrupt is triggert while execution is within the sleep function. But for my understanding i have 3 seconds (the main loop) to remove the connection between 5V and pin 2. because only after that the interrupt is enabled again. Where is my error?

Yes you are almost correct but the flag telling the CPU that there is an interrupt pending will be set as soon as you leave the ISR if you are still connected to 5V (hence my mention of being fast) and your next attachInterrupt will trigger the ISR right away, detaching it and then you go to a looooooong sleep that only reset can wake up.

Check Nick’s paragraph on how interrupts are queued (actually the whole doc is worth reading)

The ones that set a flag could be regarded as being queued, as the interrupt flag remains set until such time as the interrupt routine is entered, at which time the processor clears the flag. Of course, since there is only one flag, if the same interrupt condition occurs again before the first one is processed, it won’t be serviced twice.

Something to be aware of is that these flags can be set before you attach the interrupt handler. For example, it is possible for a rising or falling level interrupt on pin D2 to be “flagged”, and then as soon as you do an attachInterrupt the interrupt immediately fires, even if the event occurred an hour ago. To avoid this you can manually clear the flag. For example:

EIFR = 1; // clear flag for interrupt 0
EIFR = 2; // clear flag for interrupt 1

Or, for readability:

EIFR = bit (INTF0); // clear flag for interrupt 0
EIFR = bit (INTF1); // clear flag for interrupt 1

However the “low level” interrupts are continuously checked, so if you are not careful they will keep firing, even after the interrupt has been called. That is, the ISR will exit, and then the interrupt will immediately fire again. To avoid this, you should do a detachInterrupt immediately after you know that the interrupt fired.

Nick’s example of this is going into sleep mode:

#include <avr/sleep.h>                  

// interrupt service routine in sleep mode
void wake ()
{
  sleep_disable ();         // first thing after waking from sleep:
  detachInterrupt (0);      // stop LOW interrupt
}  // end of wake

void sleepNow ()
{
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   
  noInterrupts ();          // make sure we don't get interrupted before we sleep
  sleep_enable ();          // enables the sleep bit in the mcucr register
  attachInterrupt (0, wake, LOW);  // wake up on low level
  interrupts ();           // interrupts allowed now, next instruction WILL be executed
  sleep_cpu ();            // here the device is put to sleep
}  // end of sleepNow

The improved version solves this problem by disabling interrupts before doing the attachInterrupt call. Since you are guaranteed that one instruction will be executed after re-enabling interrupts, we are sure that the sleep_cpu call will be done before the interrupt occurs.

Also check the arduino’s Web Site on sleeping and common mistake made that sends your CPU to deep coma (sleep you can’t wake up from besides reset) for good practices with sleep and ISR

HI,

thanks for taking the time to clarify this. This is really helpful!