Arduino refuses to wake up from sleep via external interrupt

Hello,

I have a very weired problem about waking up an arduino with an external interrupt.

I have an ADXL345 from Adafruit connected to the Arduino, with the 2 Interrupt pins of the ADXL connected to pin 2 and 3 of the Arduino.

Arduino Pin 2 and 3 are configurated as INPUT, SPI is working

The ADXL is confugurated to send a LOW-Level Interrupt to the arduino if it detects Inactivity. Then an Interrupt-Service routine is executed and put the Arduino to sleep and the Low-Level of the ADXL will go to High, therefore no new interrupt will be generated (Code will be on the Bottom of the Post).

Before the Arduino will go to sleep the ADXL is configurated to trigger the same ISR, but this time if it detects any Activity.
If the ISR is triggered, the Arduino will wake up and continues.

I use one ISR for both, the Sleeping and the Wakeup because the other ISR is reserved for getting the Data out of the ADXL’s internal FiFo (any time it hits its Sample limit the ISR is triggered and download the Data from the ADXL). loop()-Funktion is empty.

this is the Code snippet for assigning the Interrupts:

  noInterrupts();
  attachInterrupt(0, ADXL_ISR_General, LOW); //0 entspricht pin2
  attachInterrupt(1, ADXL_ISR_FiFo, LOW);  //1 entspricht pin3
  interrupts();

The Programm works 95% of the Times, but sometimes the Arduino will go to sleep and never wake up. I checked the Interrupt-cable in this situation and it is definately on LOW level.

I tried for about 2 Weeks now, but can’t figure out what may be causing the instability.

I checked via 2 LEDs, and found out that the Arduino won’t exit the “sleep_cpu()” it just stays in sleep mode…

Here is the code of the Sleep/Wakeup ISR:

void ADXL_ISR_General() {

  noInterrupts(); //Interrupts Abschalten um die Ausführung des folgenden Codes nicht zu behindern --> Hier gehen messdaten verloren, angesichts der Hohen bandbreite und der Vielzahl an Daten Verschmerzbar

  EIFR = 0x01;//Löschen des 0er Interrupts
  SPI.setDataMode(SPI_MODE3); //Data_Mode3 für den ADXL

  uint8_t interrupt = adxl.getInterruptSource(); //Interruptgrund erfragen

  if (adxl.triggered(interrupt, ADXL345_INACTIVITY)) //Inactivität --> Hier wird der uC Heruntergefahren
  {

    Sleep();

  }


  //if (adxl.triggered(interrupt, ADXL345_SINGLE_TAP)) //Stoß --> Hier wird der uC Hochgefahren
  if (adxl.triggered(interrupt, ADXL345_ACTIVITY))
  {
    //Wakeup(); //wird nach dem aufwachen direkt aufgerufen
    adxl.ActivityINT(false); //Aktivity interrupt ausschalten
  }

  EIFR = 0x01;//Löschen des 0er Interrupts

  interrupts(); // Interrupts wiedereinschalten
}

void Sleep()
{
  RED_ON; //Rote LED an
#ifdef DEBUG
  YELLOW_ON;
#endif
  digitalWrite(4, LOW); //GPS aus
  //adxl.singleTapINT(true); //Tap-Interrupt anschalten zum wieder aufwachen
  adxl.ActivityINT(true);


  adxl.setRegisterBit(ADXL345_INT_ENABLE, 1, 0); //disable Watermark interrupt
  adxl.InactivityINT(false);              //disable Inactivity-Interrupt

#ifndef DEBUG
  RED_OFF;
  digitalWrite(7, LOW);
#endif


  file.write('#'); //Schreibe eine Notification in das Log das nun in den schlafmodus übergegenagen wird
  file.write('S');
  file.write(10); //LF
  file.sync();



  set_sleep_mode(SLEEP_MODE_STANDBY); //set the Sleep-Mode Bit (choose wich sleep mode we want)
  //set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  // Set sleep enable (SE) bit:
  sleep_enable();
  //Disable the Brown-Out-Detector
  sleep_bod_disable();
  //
  //
  interrupts(); // Interrupts wiedereinschalten damit der uC auch aufwacht
  //
  // Put the device to sleep:
  sleep_cpu();
  //
  // ## Sleeping! ##

  // ## <<< THIS CODE WILL NEVER BE REACHED SOMETIMES >>>
  
#ifdef DEBUG
  YELLOW_OFF;
#endif


  //
  // Upon waking up, sketch continues from this point.
  sleep_disable();

  noInterrupts();

  Wakeup();


}

void Wakeup()
{

  SPI.setDataMode(SPI_MODE3); //Data_Mode3 für den ADXL

  //sleep_disable();
  RED_OFF;
  GPS_ON;
  adxl.InactivityINT(true); //enable Inactivity-Interrupt
  //adxl.singleTapINT(false); //Tap-Interrupt ausschalten
  //adxl.ActivityINT(false); //Aktivity interrupt ausschalten --> Wurde in den eigentlichen IRQ gelegt, um zu garantieren das der IRQ erst NACH dem auslesen der interrupt-source abgeschaltet wird

  adxl.setRegisterBit(ADXL345_INT_ENABLE, 1, 1); //enable Watermark interrupt
  interrupts();
}

These are the used makros:

//Schnellere Port-Zugriffe
#define RED_ON PORTD |= 128
#define RED_OFF PORTD &= ~128
#define RED_TOGGLE PORTD ^= 128

#define YELLOW_ON PORTD |= 64
#define YELLOW_OFF PORTD &= ~64
#define YELLOW_TOGGLE PORTD ^= 64

#define GPS_ON PORTD |= 16
#define GPS_OFF PORTD &= ~16

Arduino runs on 3,7 to 4V @ 16Mhz (Custom PCB, Powered by LiPo), voltage ripple on arduino Vcc pin is about ±50mV wich should be OK…

Thanks for your time,
cad435

  noInterrupts();
  attachInterrupt(0, ADXL_ISR_General, LOW); //0 entspricht pin2
  attachInterrupt(1, ADXL_ISR_FiFo, LOW);  //1 entspricht pin3
  interrupts();

You do not need to disable interrupts in order to add more interrupt handlers.

Are you CERTAIN that LOW can be used to wake the Arduino up?

void ADXL_ISR_General() {

  noInterrupts(); //Interrupts Abschalten um die Ausführung des folgenden Codes nicht zu behindern --> Hier gehen messdaten verloren, angesichts der Hohen bandbreite und der Vielzahl an Daten Verschmerzbar

Interrupts are already disabled in an interrupt service routine.

Comments at the end of a line of code are meant to be short. If you need to write a novel, put the novel before the code.

Hello, thanks for the Reply.

Somewhere I read disabling Interrupts before adding interrupt-Handler should be done, to provide safety to not mess up when an interrupt is fired during the ISR-Vector assigning instructions, but I can't remember were i found this statement, If you insist I'll search for it...

And yes, the ONLY way to wake up a Sleeping Arduino from the deeper sleep-modes is the LOW-Interrupt, anything else won't work...

cad435

If you insist I'll search for it...

I think I must insist. It feels to me like you are doing far too much diddling with the interrupt system. It is rarely necessary to disable and enable interrupts, except when copying data from variables that can be modified in an ISR or changing the value of such variables.

Well, I searched for half an hour now, but can't find the statement...

If I encounter the passage I'll let you know, but anyway I do not think this is the problem wich bothers me after all...

cad435

Hey if you had not figured it out I just had some success.

I did what PaulS mentioned and I stopped diddling with the interrrupts.

All I did was set the arduino interrupt up in the setup portion.

attachInterrupt(digitalPinToInterrupt(3), wakeUp, FALLING) (my code uses switch open to wake up)

Also, check the syntax reference page for attachInterrupt() on this website. It has recommended methods to reference the PIN to attach and talks about NOT Recommend too.

It appears that pinMode(3,INPUT_PULLUP) and pinMode(3,INPUT) both work in my case, however, I am going to use INPUT_PULLUP because I think this will save the battery fast because it enables an internal resistor instead of a 5v or 3.3v (whatever) short.

I also gutted the ISR (Interrupt/Wake code portions) of everything except setting the mode for sleep and jamming it into sleep. No serialPrint or LED control like some of the code written on these tutorials.

In other words, keep the ISR SUPER simple.

Lesson learned ! Good work.