ArduinoCore-samd icon indicating copy to clipboard operation
ArduinoCore-samd copied to clipboard

External interruption not being triggered with CHANGE, RISING or FALLING while on standby (sleep) mode.

Open javorosas opened this issue 8 years ago • 19 comments

I want to be able to wake up the MKR1000 from an external interrupt. So far I've been trying different configurations and the only interrupt types that work are HIGH and LOW.

So CHANGE, RISING and FALLING are not triggering the external interruption while the board is in sleep mode. Here is my code:

#include <RTCZero.h>

const byte button = 5;
bool ledIsOn = false;
volatile bool shouldBeSleeping = false;

RTCZero rtc;

void setup() {
  rtc.begin();
  pinMode(button, INPUT);
  Serial.begin(115200);
  attachInterrupt(button, interr, RISING);
  Serial.println("== Interrupt test ==");
}

void loop() {
  if (shouldBeSleeping) {
    Serial.println("Now going to sleep");
    rtc.standbyMode();
  }
  digitalWrite(LED_BUILTIN, ledIsOn = !ledIsOn);
  delay(1000);
}

void interr() {
  Serial.println("Interruption triggered");
  if (shouldBeSleeping) {
    Serial.println("This line is never executed");
  }
  shouldBeSleeping = !shouldBeSleeping;
}

javorosas avatar May 06 '16 19:05 javorosas

If you are not using an external pull-up resistor, the internal pull-up resistor must be enabled. This is for active low button. pinMode(button, INPUT_PULLUP);

If your button is active low, interrupt edge detection should be FALLING attachInterrupt(button, interr, FALLING);

Tested on an Arduino Zero and it is working fine.

rocketscream avatar May 07 '16 09:05 rocketscream

I am using an external pull-down resistor. My button is active high. My problem is not in triggering the interruption, that works fine. What I cannot do is triggering the interruption while on sleep mode. In the code above, that's the second time I press the button.

javorosas avatar May 07 '16 19:05 javorosas

There's something weird going on with the MKR1000.

I connected an FTDI to the Serial pins on my Arduino Zero then loaded the following sketch via the programming port:

#include <RTCZero.h>

const byte button = 5;
bool ledIsOn = false;

RTCZero rtc;

void setup() {                  
  rtc.begin();
  pinMode(button, INPUT_PULLDOWN);
  Serial1.begin(115200);
  attachInterrupt(button, interr, RISING);
  Serial1.println("== Interrupt test ==");
}

void loop() {
  Serial1.println("Now going to sleep");
  Serial1.flush();
  rtc.standbyMode();
}

void interr() {
  Serial1.println("Interruption triggered");
  digitalWrite(LED_BUILTIN, ledIsOn = !ledIsOn);
}

All is good, however the following does not work:

  • Loading sketch onto Zero via Native USB port, and powering. I suspect because of the USB clock interfering.
  • Loading sketch on the the MKR1000, then powering off USB or external Lipo battery.

sandeepmistry avatar May 27 '16 21:05 sandeepmistry

Maybe the Native USB issue is related to #103

AloyseTech avatar May 27 '16 21:05 AloyseTech

Here's an updated sketch that works for me:

#include <RTCZero.h>

const byte button = 5;
bool ledIsOn = false;

RTCZero rtc;

void setup() {
  // Configure the regulator to run in normal mode when in standby mode
  // Otherwise it defaults to low power mode and can only supply 50 uA
  SYSCTRL->VREG.bit.RUNSTDBY = 1;

  // Enable the DFLL48M clock in standby mode
  SYSCTRL->DFLLCTRL.bit.RUNSTDBY = 1;

  // Disable the USB device, this avoids USB interrupts
  // mainly the SOF every 1ms.
  // Note: you'll have to double tap the reset button to load new sketches
  USBDevice.detach();

  rtc.begin();
  pinMode(button, INPUT_PULLDOWN);


  Serial1.begin(115200);
  attachInterrupt(button, interr, RISING);
  Serial1.println("== Interrupt test ==");
}

void loop() {
  Serial1.println("Now going to sleep");
  Serial1.flush();
  rtc.standbyMode();
}

void interr() {
  Serial1.println("Interruption triggered");
  digitalWrite(LED_BUILTIN, ledIsOn = !ledIsOn);
}

The 3 major additions are:

  1. Allowing the regulator to run in standby mode. By default in standby mode the regulator operates in low power mode and can only supply up to 50 uA. (from AT11491: Peripheral Power Consumption in Standby Mode for SAM D Devices)

  2. Enable the DFLL48M clock in standby mode. The EIC uses this clock, so it's needs to be enabled in standby mode.

  3. Detach USB, only needed if board is powered via native USB.

The RTCZero library setups the external 32 kHz clock (XOSC32K), and enables it during standby. This part was missing in the core for DFLL48M. We'll need to find a nicer way for handling this. I didn't spend too much time looking into why things work on the Zero programming port, but imagine the EDBG keeps the 48 MHz clock running or something.

sandeepmistry avatar May 31 '16 19:05 sandeepmistry

Sandeep,

I went through this few weeks ago but didn't manage to run a physical test. As far I know, you do not need a clock source for asynchronous event (when only level trigger is required with no filtering). So, if LOW & HIGH is used in the attachInterrupt(), that should work. The RTC module only consumes about 5 uA and EIC is also about 5 uA when running which is well below the 50 uA maximum current provided by the built-in regulator in low power mode.

My findings are more or less same with the report here.

rocketscream avatar Jun 01 '16 02:06 rocketscream

@rocketscream thanks for the link!

I tried changing the EIC to use GCLK_CLKCTRL_GEN_GCLK2 (which is the XOSC32K started by RTCZero) instead of GCLK_CLKCTRL_GEN_GCLK0, and this worked!

We'll need to figure out a plan to incorporate low power into the core ...

sandeepmistry avatar Jun 01 '16 17:06 sandeepmistry

I've been playing around with minimizing power in sleep mode. I have the RTC and the EIC both running off the XOSC32K. The EIC is running because I'm using falling-edge interrupts to wake. I found that leaving the pins set as nothing/analog inputs made a significant difference in current - my board went from 100uA during sleep to 40uA. It's not exactly apples to apples because I'm not using the Zero hardware anymore, but 60uA is 60uA.

I commented out this line in wiring.c that sets all pins to digital inputs:

for ( ul = 0 ; ul < NUM_DIGITAL_PINS ; ul++ ) { pinMode( ul, INPUT ) ; }

I then do my own configuration when I need to set up a pin as a digital input.

casundra avatar Jun 01 '16 17:06 casundra

@sandeepmistry A good question would be: If we were to clock the EIC using GCLK_CLKCTRL_GEN_GCLK2 even in normal running mode, would it affect anything? This is the simplest without messing up with anything else related to standby and RTC.

rocketscream avatar Jun 02 '16 11:06 rocketscream

casundra commented on Jun 1

I commented out this line in wiring.c that sets all pins to digital inputs:

for ( ul = 0 ; ul < NUM_DIGITAL_PINS ; ul++ ) { pinMode( ul, INPUT ) ; }

Why is this in there anyways? I always get it out to get the power down.

For the CHANGE, RISING or FALLING while on sleep mode problem, Gabriel Notman suggested using this code:

#include <RTCZero.h>
RTCZero rtc;
void setup() {
    pinMode(D3, INPUT_PULLDOWN);
    attachInterrupt(D3, d3Callback, CHANGE);
    EIC->CONFIG[1].bit.FILTEN1 = 1; // D3=eic9
    EIC->WAKEUP.vec.WAKEUPEN = (1<<9);
    rtc.begin();
    rtc.setTime(0, 0, 0);
    rtc.setDate(0, 0, 0);
    rtc.attachInterrupt(rtcCallback)
    rtc.setAlarmSeconds(0);
    rtc.enableAlarm(MATCH_SS);
    // The RTCZero library will setup generic clock 2 to XOSC32K/32
    // and we'll use that for the EIC.
    GCLK->CLKCTRL.reg = uint16_t(
        GCLK_CLKCTRL_CLKEN |
        GCLK_CLKCTRL_GEN_GCLK2 |
        GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC_Val )
    );
    while (GCLK->STATUS.bit.SYNCBUSY) {}
}

Original on the arduino forum.

FRVisser avatar Sep 01 '16 12:09 FRVisser

Why is this in there anyways? I always get it out to get the power down.

For compatibility with AVR core behaviour. On boards like the Uno, all pins default to input.

sandeepmistry avatar Sep 01 '16 12:09 sandeepmistry

GabrielNotman wrote: "One thing you should note is that the edge event interrupts require a specific clock and that clock is disabled in deep sleep mode. For your wake interrupts you should use HIGH or LOW which will work correctly in deep sleep mode."

https://forum.arduino.cc/index.php?topic=337289.msg2360013#msg2360013

HamishB avatar Oct 25 '16 08:10 HamishB

Sandeepmistry

Has any effort been made to fix this in the core code base yet??

Thanks

trlafleur avatar Dec 27 '16 01:12 trlafleur

I just tripped up on this problem. + 1 for fixing it ASAP...

leehonan avatar May 24 '17 04:05 leehonan

+1 from me as well.

tsaG1337 avatar Jul 03 '17 21:07 tsaG1337

Me too....

~~ _/) ~~~~ _/) ~~~~ _/) ~~~~ _/) ~~

Tom Lafleur

On Mon, Jul 3, 2017 at 2:43 PM, tsaG1337 [email protected] wrote:

+1 from me as well.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/arduino/ArduinoCore-samd/issues/142#issuecomment-312738668, or mute the thread https://github.com/notifications/unsubscribe-auth/AEXCKyQItkNTwYz7Q59Yh7encELBQK45ks5sKWCYgaJpZM4IZJDE .

trlafleur avatar Jul 03 '17 21:07 trlafleur

@casundra, Could you please elaborate on it? I'm using MKRFox1200 and "wiring.c" in installation directory of Arduino on my computer doesn't have such line of code. In deep sleep mode MKRFox1200 is using 440uA which is a lot compared to your numbers. I'm waking up this board on timely periods (every 15 minutes). A copy of my code is availble at https://shrib.com/?v=nc#7LWTtmiA76zpJI64CIiC.

nimasaj avatar Mar 17 '18 21:03 nimasaj

+1 to implement a fix. Note that some SAMD boards are running CRYSTALLESS (internal 32k oscillator).

LowPowerLab avatar Aug 27 '20 17:08 LowPowerLab

I also had hard times getting this to work for a Seeduino XIAO.

Found a solution at https://tinycircuits.com/blogs/learn/tinyscreen-external-interrupt-sleep-mode that is working for me.

Minimal example below:

#include <RTCZero.h>
RTCZero rtc;

#define WAKE_UP_PIN 1

void RTCwakeHandler() {
}

void wakeHandler() {
}

void setup(void)
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW); // turn on (inverted)
  delay(1000);
  
  pinMode(WAKE_UP_PIN, INPUT_PULLUP); // 
  
  rtc.begin();

  // set Interrupts
  rtc.attachInterrupt(RTCwakeHandler);
  attachInterrupt(WAKE_UP_PIN, wakeHandler, RISING); // == on release

  #if defined(ARDUINO_ARCH_SAMD)
    // https://github.com/arduino/ArduinoCore-samd/issues/142
    // Clock EIC in sleep mode so that we can use pin change interrupts
    // The RTCZero library will setup generic clock 2 to XOSC32K/32
    // and we'll use that for the EIC. Increases power consumption by ~50uA
    GCLK->CLKCTRL.reg = uint16_t(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC_Val ) );
    while (GCLK->STATUS.bit.SYNCBUSY) {}
  #endif

  digitalWrite(LED_BUILTIN, HIGH); // turn off (inverted)
  
  setNextAlarmSeconds(10);
  rtc.standbyMode();
}


void loop() {
  for(int i=0; i<10; i++){
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
  }

  setNextAlarmSeconds(10);
  rtc.standbyMode();
}

void setNextAlarmSeconds(int secs){
  rtc.setAlarmSeconds((rtc.getSeconds()+secs) % 60);
  rtc.enableAlarm(rtc.MATCH_SS);
}

hansipete avatar Nov 08 '21 15:11 hansipete