Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
javorosas opened this issue May 6, 2016 · 19 comments
Assignees

Comments

@javorosas
Copy link

javorosas commented May 6, 2016

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;
}
@rocketscream
Copy link

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.

@javorosas
Copy link
Author

javorosas commented May 7, 2016

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.

@sandeepmistry
Copy link
Contributor

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.

@AloyseTech
Copy link

Maybe the Native USB issue is related to #103

@sandeepmistry
Copy link
Contributor

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.

@rocketscream
Copy link

rocketscream commented Jun 1, 2016

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.

@sandeepmistry
Copy link
Contributor

@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 ...

@casundra
Copy link

casundra commented Jun 1, 2016

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.

@rocketscream
Copy link

@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.

@javorosas javorosas changed the title External interruption not being triggered with CHANGE, RISING or FALLING while on sleep mode. External interruption not being triggered with CHANGE, RISING or FALLING while on standby (sleep) mode. Jul 20, 2016
@FRVisser
Copy link

FRVisser commented Sep 1, 2016

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.

@sandeepmistry
Copy link
Contributor

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.

@HamishB
Copy link

HamishB commented Oct 25, 2016

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

@trlafleur
Copy link

Sandeepmistry

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

Thanks

@leehonan
Copy link

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

@tsaG1337
Copy link

tsaG1337 commented Jul 3, 2017

+1 from me as well.

@trlafleur
Copy link

trlafleur commented Jul 3, 2017 via email

@nimasaj
Copy link

nimasaj commented Mar 17, 2018

@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.

@LowPowerLab
Copy link

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

@hansipete
Copy link

hansipete commented Nov 8, 2021

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);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests