Put Nano 33 BLE into power ON sleep mode

Hi all,

I've been trying to get my Arduino Nano 33 BLE into a low power sleep mode, which should wake on a specific interrupt/"event" (such as a push button) and then simply continue. However, this does not seem to work as expected, yet.

Note: I am specifically referring to the "system ON" sleep mode as described in the NRF52840 reference/docs from the Nordic website, NOT a simple delay as i need to actually save power, nor the "system OFF" mode (NRF_POWER->SYSTEMOFF = 1) as I do not want a reset.

What I have tried so far:

There does not seem to be a nice Arduino solution for this problem with this board anyway. But, looking at the ArduinoLowPower library source and NRF52840 SDK, I believe that the proper way to reach this kind of sleep state should be using sd_app_evt_wait(); like so:

#include "nrf_gpio.h"
#include "nrf_nvic.h"
#include "nrf_soc.h"
#include "pinDefinitions.h"

#define PIN_PUSH_BUTTON 2

void setup()
{
    //Configure RGB LED
    pinMode(22, OUTPUT);
    pinMode(23, OUTPUT);
    pinMode(24, OUTPUT);
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH);
    digitalWrite(24, LOW);

    nrf_gpio_cfg_sense_input(g_APinDescription[PIN_PUSH_BUTTON].name, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
}

void loop()
{
    digitalWrite(24, HIGH);

    sd_app_evt_wait();

    digitalWrite(24, LOW);
}

This compiles fine, however, as I can tell from the LED, sd_app_evt_wait(); always immediately wakes up again.

If I go to "system OFF" mode instead, via

void loop()
{
    digitalWrite(24, HIGH);

    NRF_POWER->SYSTEMOFF = 1;

    digitalWrite(24, LOW);
}

the LED stays off until the reset when I press the push button.

Does someone know, whether sd_app_evt_wait(); is just not fully supported by the mbed core for some reason or whether there is some other default interrupt that I did not consider?

I did some further research and the problem is likely related to some unhandled "event"/interrupt, as "system OFF" only wakes from a very limited set of interrupts, in contrast to "system ON".
Also, if I put noInterrupts(); just before sd_app_evt_wait();, it does not wake up again (i.e. the function call itself works in principle).

Additionally, I tried to clear all IRQs that I could find in the headers before going to sleep (+ some FPU-related fix):

sd_nvic_ClearPendingIRQ(Reset_IRQn);
sd_nvic_ClearPendingIRQ(NonMaskableInt_IRQn);
sd_nvic_ClearPendingIRQ(HardFault_IRQn);
sd_nvic_ClearPendingIRQ(MemoryManagement_IRQn);
sd_nvic_ClearPendingIRQ(BusFault_IRQn);
sd_nvic_ClearPendingIRQ(UsageFault_IRQn);
sd_nvic_ClearPendingIRQ(SVCall_IRQn);
sd_nvic_ClearPendingIRQ(DebugMonitor_IRQn);
sd_nvic_ClearPendingIRQ(PendSV_IRQn);
sd_nvic_ClearPendingIRQ(SysTick_IRQn);
sd_nvic_ClearPendingIRQ(POWER_CLOCK_IRQn);
sd_nvic_ClearPendingIRQ(RADIO_IRQn);
sd_nvic_ClearPendingIRQ(UARTE0_UART0_IRQn);
sd_nvic_ClearPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
sd_nvic_ClearPendingIRQ(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn);
sd_nvic_ClearPendingIRQ(NFCT_IRQn);
sd_nvic_ClearPendingIRQ(GPIOTE_IRQn);
sd_nvic_ClearPendingIRQ(SAADC_IRQn);
sd_nvic_ClearPendingIRQ(TIMER0_IRQn);
sd_nvic_ClearPendingIRQ(TIMER1_IRQn);
sd_nvic_ClearPendingIRQ(TIMER2_IRQn);
sd_nvic_ClearPendingIRQ(RTC0_IRQn);
sd_nvic_ClearPendingIRQ(TEMP_IRQn);
sd_nvic_ClearPendingIRQ(RNG_IRQn);
sd_nvic_ClearPendingIRQ(ECB_IRQn);
sd_nvic_ClearPendingIRQ(CCM_AAR_IRQn);
sd_nvic_ClearPendingIRQ(WDT_IRQn);
sd_nvic_ClearPendingIRQ(RTC1_IRQn);
sd_nvic_ClearPendingIRQ(QDEC_IRQn);
sd_nvic_ClearPendingIRQ(COMP_LPCOMP_IRQn);
sd_nvic_ClearPendingIRQ(SWI0_EGU0_IRQn);
sd_nvic_ClearPendingIRQ(SWI1_EGU1_IRQn);
sd_nvic_ClearPendingIRQ(SWI2_EGU2_IRQn);
sd_nvic_ClearPendingIRQ(SWI3_EGU3_IRQn);
sd_nvic_ClearPendingIRQ(SWI4_EGU4_IRQn);
sd_nvic_ClearPendingIRQ(SWI5_EGU5_IRQn);
sd_nvic_ClearPendingIRQ(TIMER3_IRQn);
sd_nvic_ClearPendingIRQ(TIMER4_IRQn);
sd_nvic_ClearPendingIRQ(PWM0_IRQn);
sd_nvic_ClearPendingIRQ(PDM_IRQn);
sd_nvic_ClearPendingIRQ(MWU_IRQn);
sd_nvic_ClearPendingIRQ(PWM1_IRQn);
sd_nvic_ClearPendingIRQ(PWM2_IRQn);
sd_nvic_ClearPendingIRQ(SPIM2_SPIS2_SPI2_IRQn);
sd_nvic_ClearPendingIRQ(RTC2_IRQn);
sd_nvic_ClearPendingIRQ(I2S_IRQn);
sd_nvic_ClearPendingIRQ(FPU_IRQn);
sd_nvic_ClearPendingIRQ(USBD_IRQn);
sd_nvic_ClearPendingIRQ(UARTE1_IRQn);
sd_nvic_ClearPendingIRQ(QSPI_IRQn);
sd_nvic_ClearPendingIRQ(CRYPTOCELL_IRQn);
sd_nvic_ClearPendingIRQ(PWM3_IRQn);
sd_nvic_ClearPendingIRQ(SPIM3_IRQn);

__set_FPSCR(__get_FPSCR() & ~(0x0000009F));      
(void) __get_FPSCR();
sd_nvic_ClearPendingIRQ(FPU_IRQn);

But this made no difference as well.

Any advice?

Thanks!

Welcome to the forum.

I suspect you will not be able to use this feature while you have the Arduino plugged into the PC. The Arduino Nano 33 BLE uses native USB. That means the USB stack runs in the background on the same processor as your sketch.

1 Like

Interesting, although I have actually tested this with direct 3.3V supply and USB unplugged and it did not go to sleep either.

But I am not sure whether I tried all variants (explicit clearing of IRQs etc.) then, so I will check that again and will let you know.

I found the solution to this problem.

Actually the desired sleep mode works fine, but the controller is constantly woken up by events from the real-time clock RTC1. This interrupt is apparently enabled by default as it seems to be used for the delay() function and probably other stuff. To make use of the sleep mode you have to disable this interrupt before sleep (and enable afterwards again, delay() and probably other stuff will not work properly otherwise).

Interestingly, if RTC1 IRQ is re-enabled after sleep, sd_app_evt_wait(); has to be called several times to make it work (clearing the IRQ as mentioned above does not help). I guess one should hence use normal interrupts instead of the nrf_gpio_cfg_sense_input and put sd_app_evt_wait(); in a loop that breaks when your wake-up interrupt flags are set.

Here is a minimal working example:

#include "nrf_nvic.h"
#include "nrf_soc.h"

#define PIN_PUSH_BUTTON 2

volatile bool wakeUp = false;

void isrButton()
{
    wakeUp = true;
}

void setup()
{
    // configure RGB LED
    pinMode(22, OUTPUT);
    pinMode(23, OUTPUT);
    pinMode(24, OUTPUT);
    digitalWrite(22, HIGH);
    digitalWrite(23, HIGH);
    digitalWrite(24, LOW);
}

void loop()
{
    // LED off
    digitalWrite(24, HIGH);

    // use push button to wake up via interrupt
    attachInterrupt(digitalPinToInterrupt(PIN_PUSH_BUTTON), isrButton, FALLING);

    // disable RTC1 interrupts
    sd_nvic_DisableIRQ(RTC1_IRQn);

    // sleep until wake-up flag has been set by button interrupt-service routine
    while (!wakeUp)
        sd_app_evt_wait();

    // enable RTC1 interrupts
    sd_nvic_EnableIRQ(RTC1_IRQn);

    // reset interrupt flag
    detachInterrupt(digitalPinToInterrupt(PIN_PUSH_BUTTON));
    wakeUp = false;

    // LED on
    digitalWrite(24, LOW);

    // do stuff...
}

That works fine now.

1 Like

@Klaus_K FYI, I just tried again with USB connected and serial connection running and the sleep mode did not seem to get interrupted by this.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.