STANDBY sleep mode on SAMD51?

We've been discussing modifying Adafruit's SleepyDog library to get it to work with STANDBY sleep mode for the SAMD51 boards. Has anyone managed to get this working? The pertinent discussion is here:

https://forums.adafruit.com/viewtopic.php?f=62&t=145747

Thanks in advanced!

Hi Casey10110,

I've tested both the SAMD21 and SAMD51 boards.

The issue is that that either something is waking up the SAMD51 (Itsy Bitsy M4) immediately after a __WFI() (Wait For Interrupt) command, or the __WFI simply isn't working. The SAMD21 (Arduino Zero) on the other hand functions correctly.

I tried disabling all the EIC (External Interrupt Controller) NVIC (Nested Vector Interrupt Controller) interrupts back to the CPU core, but that didn't make any difference. I've also tried disabling the EIC peripheral.

The __WFE (Wait For Event) function doesn't appear to work either.

On the SAMD51 Atmel (now Microchip) have also muddied the waters by using the SLEEPCFG (Sleep Config) register in the Power Manager peripheral:

PM->SLEEPCFG.bit.SLEEPMODE = 0x4;                 // Set up standby sleep mode on __WFI()
while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4);         // Wait for it to take

....rather than setting the SLEEP mask in the ARM core SCR (System Control Register):

SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;            // Set up the CPU to enter low power STANDBY mode (rather than IDLE mode)

As yet, I haven't found a solution.

Are you connected to usb? Are you turning it off?

Hi westfw,

The boards are powered by the native USB port, but I'm not using Serial/SerialUSB.

I'm using some test code that puts the processor into deep sleep using the __WFI() function. Bringing pin A1 to HIGH wakes up the processor and triggers the ISR to switch on the LED on D13.

After 1 second delay the LED is turned off in the loop() and the processor goes into deep sleep oncemore.

On the SAMD21 this works perfectly, the processor goes to sleep on executing the __WFI() function. The SAMD51 on the other hand appears to ignore it and just continues round the loop() seeming without pausing. (I tested this by toggling another digital output pin not shown in the code below).

Perhaps (not for the first time) I'm missing something obvious?

// Put the CPU core into deep sleep and awake with a logic HIGH interrupt on analog pin A1
void setup() 
{ 
  pinMode(13, OUTPUT);                          // Set digital pin 13 to an output
  pinMode(A1, INPUT_PULLDOWN);                  // Set analog pin A1 to an input with an internal pull-down resistor
  attachInterrupt(A1, interrupt, HIGH);         // Set up an interrupt on analog pin A1 on a logic HIGH level
  //SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;            // Set up the SAMD21 to enter low power STANDBY mode
  PM->SLEEPCFG.bit.SLEEPMODE = 0x4;             // Set up SAMD51 to enter low power STANDBY mode
  while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4);     // Wait for it to take
}

void loop() 
{
  delay(1000);                                  // Wait for 1 second
  digitalWrite(13, LOW);                        // Set D13 output to logic LOW
  __DSB();                                      // Complete data memory operations
  __WFI();                                      // Put the SAMDx1 into deep sleep Zzzzzzz....
}

void interrupt()                                // Interrupt Service Routine (ISR)
{
  digitalWrite(13, HIGH);                       // Set D13 output to logic HIGH
}

I've been testing the SAMD51 in STANDBY mode, on both the Metro M4 and Itsy Bitsy M4, but neither enter deep sleep after receiving the __WFI() command. However, the SAMD21 (on my Arduino Zero) functions correctly.

After some research I found that the USB peripheral module is set to run on standby (RUNSTDBY) on both the Arduino Zero and the M4 boards. On the SAMD51 boards it appears as though interrupts from this peripheral are keeping the processor awake. If I add these lines to the beginning of my sketch, to shutdown the USB, the SAMD51 goes into deep sleep and both external and watchdog interrupts wake it up correctly:

USB->DEVICE.CTRLA.bit.ENABLE = 0;                   // Shutdown the USB peripheral
while(USB->DEVICE.SYNCBUSY.bit.ENABLE);             // Wait for synchronization

The question remains however, as to why this occurs on the SAMD51 boards and not on the SAMD21 Arduino Zero.

I've raised the issue on Github: https://github.com/adafruit/Adafruit_SleepyDog/issues/18.

I've generated a pull request on Adafruit_SleepyDog Github repo: https://github.com/adafruit/Adafruit_SleepyDog/pull/19.

It just disables the SAMD51 USB peripheral's Run-On-Standby (RUNSTDBY) bit, allowing the microcontroller to enter deep sleep.

Hi all, with the adafruit's SleepyDog library, how much of a current was drawn by the microcontroller? Could anyone please give me a number?

Hi razor101,

Hi all, with the adafruit's SleepyDog library, how much of a current was drawn by the microcontroller? Could anyone please give me a number?

As srnet mentioned in a previous post (https://forum.arduino.cc/index.php?topic=615936.0), according to the SAMD51 datasheet it's typically around 43uA in LDO (linear voltage regulator) mode and about 26uA in Buck (switching voltage regulator) mode. (The SAMD51 silicon contains both on-chip voltage regulator options, to power its core).

Some SAMD51 boards include an additional inductor to support Buck mode, others don't.

The current drawn however will very much depend on the SAMD51 board you're using and any other on-board devices, for example a single power-on LED is going to draw far more current than the microcontroller in standby.

Short of desoldering all other non-microcontroller essential devices and components off your board and testing with an ammeter, the datasheet is all you have to go on.

razor101:
Hi all, with the adafruit’s SleepyDog library, how much of a current was drawn by the microcontroller? Could anyone please give me a number?

What do Adafruit say it should be ?

Hi all. Thank you very much for replying.

srnet: What do Adafruit say it should be ?

I was not able to find any value mentioned by Adafruit.

@MartinL I did test the Adafruit SleepyDog library with your suggestions. I can clearly see the microcontroller that I am using(SAMD51G19A) is going to sleep mode. I have soldered only the microcontroller and powering it up by 2.8v along with an ammeter connected in series. I can see a variation from ~10mA to ~1.4mA in active to sleep modes respectively.

When looked in the library, the code executed is:

#if defined(__SAMD51__)
    PM->SLEEPCFG.bit.SLEEPMODE = 0x4;         // Standby sleep mode
    while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4); // Wait for it to take

Is this all what I have to do? I did try to twerk some settings according to the datasheet. Such as

fast wake-up disabled (PM.STDBYCFG.FASTWKUP=0x0),
no peripheral running
No System RAM retained (PM.STDBYCFG.RAMCFG=0x2).
8KB backup RAM retained

Still I cannot obtain a value of 43uA for the standalone microcontroller.

Update:

By initializing the pins to tri-state buffers, I was able to reach a current consumption of 120uA. Any idea to reach double digits?

Thank you

The SAMD51 in addition to STANDBY, also offers the even lower power HIBERNATE and BACKUP sleep modes as well:

• Hibernate sleep mode: PDCORESW power domain is turned OFF. The backup power domain is kept powered to allow few features to run (RTC, 32KHz clock sources, and wake-up from external pins). The PDSYSRAM power domain can be retained according to software configuration.

• Backup sleep mode: Only the backup domain is kept powered to allow few features to run (RTC, 32KHz clock sources, and wake-up from external pins). The PDBKUPRAM power domain can be retained according to software configuration.

PM->SLEEPCFG.bit.SLEEPMODE = 0x5;         // Hibernate sleep mode
PM->SLEEPCFG.bit.SLEEPMODE = 0x6;         // Backup sleep mode

However, the datasheet states that in these modes only the RTC, 32kHz clock sources and wake from external interrupt remain active. This probably discounts using the Watchdog (WDT) timer and requires using the RTC instead.

I am interested in the STANDBY mode. The issue I have is when I test the standalone microcontroller as per the specific operating conditions set by the datasheet, the current consumption I get is around 120uA. not a typical value of 43uA.

Hi razor101,

The SAMD51 datasheet states this 43µA value is only typical at 25ºC. This current consumption varies with temperature, increasing to a maximum of 869µA at 85ºC.

Typical values are simply provided by the manufacturer to give an indication of what can be expected, but are in no way guaranteed.

If you don’t require the raw speed of the SAMD51, then it might be worth considering the more power efficient SAMD21, (although they’re not quite pin-for-pin compatible).

Hi MartinL,

All this time I was programming with the atmel ice and even though I stopped the debugging, I was not focusing on the current drawn by the atmel ice when it was in contact with the cortex debug contacts. That was a stupid mistake.

However, after initializing all the pins to tri state buffers and in standby sleep mode, I am now able to get a current consumption of 42uA. I did try the Hibernate mode and it consumed around 12uA. Thank you all

Neat. Thanks for the follow up. It would not have occurred to me that the debugger would cause higher power consumption, but it does make sense!

Hello Everyone :slight_smile:

i used the example from MartinL (Post #3)
and other posts i found
and experimented a little bit with it on my Adafruit ItsyBitsy M4 Express board (SAMD51) in combination with my Linux Laptop (System details at the end of the post)

// experiments with sleepmode on
// Adafruit ItsyBitsy M4 Express (SAMD51)
// basd on
// https://forum.arduino.cc/index.php?topic=600359.msg4079526#msg4079526
// Put the CPU core into deep sleep and awake with a
// logic LOW interrupt on analog pin A3
// = Button to GND

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);

    pinMode(A3, INPUT_PULLUP);
    attachInterrupt(A3, interrupt, LOW);

    // Set up SAMD51 to enter low power STANDBY mode
    PM->SLEEPCFG.bit.SLEEPMODE = 0x4;
    // Wait for it to take
    while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4);

    delay(1000);
    Serial.begin(115200);
    // wait for Arduino Serial Monitor
    const uint32_t timeout = 5*1000;
    uint32_t ts_start = millis();
    while((!Serial) && ((millis() - ts_start) < timeout)) {}

    Serial.println();
    Serial.println("test__SAMD51_low_power_sleep_minimal.ino");
    Serial.println();
}

void loop() {
    Serial.println("wait: ");
    for (size_t i = 10; i > 0; i--) {
        Serial.printf("%2d", i);
        digitalWrite(LED_BUILTIN, HIGH);
        delay(500);
        Serial.print(".");
        digitalWrite(LED_BUILTIN, LOW);
        delay(500);
    }
    Serial.println();

    go_to_sleep();

    Serial.println("iam awake again!");
    delay(1000);
}

void go_to_sleep() {
    Serial.println("go to sleep.");
    USBDevice.detach();
    USBDevice.end();
    USBDevice.standby();

    digitalWrite(LED_BUILTIN, LOW);
    delay(50);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);

    // Complete data memory operations
    __DSB();
    // Put the SAMDx1 into deep sleep Zzzzzzz....
    __WFI();

    // HERE THE CODE RETURNS AFTER SLEEP!

    USBDevice.init();
    delay(5000);
    USBDevice.attach();
}

void interrupt() {
    digitalWrite(LED_BUILTIN, HIGH);
}

with this sketch i get the following output in dmesg --follow

[34491.456359] usb 2-1.2: new full-speed USB device number 104 using ehci-pci
[34491.576273] usb 2-1.2: New USB device found, idVendor=239a, idProduct=802b, bcdDevice= 1.00
[34491.576323] usb 2-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[34491.576333] usb 2-1.2: Product: Adafruit ItsyBitsy M4
[34491.576336] usb 2-1.2: Manufacturer: Adafruit LLC
[34491.577154] cdc_acm 2-1.2:1.0: ttyACM0: USB ACM device

# here USBDevice.detach(); is called.

[34503.517326] usb 2-1.2: USB disconnect, device number 104
[34503.519379] cdc_acm 2-1.2:1.0: failed to set dtr/rts

# here i wake the board with the button
# the red LED_BUILTIN goes on immediatly

# 5sec later
# USBDevice.attach(); is called.

[34515.770672] usb 2-1.2: new full-speed USB device number 105 using ehci-pci
[34515.850599] usb 2-1.2: device descriptor read/64, error -32
[34516.038768] usb 2-1.2: device descriptor read/64, error -32
[34516.226546] usb 2-1.2: new full-speed USB device number 106 using ehci-pci
[34516.310555] usb 2-1.2: device descriptor read/64, error -32
[34516.498459] usb 2-1.2: device descriptor read/64, error -32
[34516.606726] usb 2-1-port2: attempt power cycle
[34517.210402] usb 2-1.2: new full-speed USB device number 107 using ehci-pci
[34517.626441] usb 2-1.2: device not accepting address 107, error -32
[34517.706437] usb 2-1.2: new full-speed USB device number 108 using ehci-pci
[34518.122333] usb 2-1.2: device not accepting address 108, error -32
[34518.122592] usb 2-1-port2: unable to enumerate USB device

so the sleeping works as expected.
But the USB does not correctly reattach afterwards.

anyone any ideas how to track this down?

sunny greetings
stefan

Dell Latitude E6510
Operating System: Kubuntu 19.04
KDE Plasma Version: 5.15.4
KDE Frameworks Version: 5.56.0
Qt Version: 5.12.2
Kernel Version: 5.0.0-21-generic
OS Type: 64-bit
Processors: 8 × Intel® Core™ i7 CPU Q 720 @ 1.60GHz
Memory: 7,7 GiB of RAM

short follow up regarding Power needs:

same board as before:
Adafruit ItsyBitsy M4 Express

nothing changed or de-soldered.

used sketch
very similar to last post - but sets onboad dotstar to 0,0,0.

// experiments with sleepmode on SAMD51
// basd on
// https://forum.arduino.cc/index.php?topic=600359.msg4079526#msg4079526
// Put the CPU core into deep sleep and awake with a
// logic LOW interrupt on analog pin A3

#include <Adafruit_DotStar.h>

// OnBoardDotstar
Adafruit_DotStar board_dotstar = Adafruit_DotStar(1, 8, 6, DOTSTAR_BGR);
uint32_t board_dotstar_standby_color = Adafruit_DotStar::Color(0, 0, 0);
uint32_t board_dotstar_active_color = Adafruit_DotStar::Color(0, 0, 0);

void setup() {
    // For boards with "native" USB support (e.g. not using an FTDI chip or
    // similar serial bridge), Serial connection may be lost on sleep/wake,
    // and you might not see the "I'm awake" messages. Use the onboard LED
    // as an alternate indicator -- the code turns it on when awake, off
    // before going to sleep.
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH); // Show we're awake

    board_dotstar.begin();
    board_dotstar.setPixelColor(0, board_dotstar_active_color);
    board_dotstar.show();

    // Set analog pin A1 to an input with an internal pull-down resistor
    pinMode(A3, INPUT_PULLUP);
    // Set up an interrupt on analog pin A1 on a logic HIGH level
    attachInterrupt(A3, interrupt, LOW);

    // Set up SAMD51 to enter low power STANDBY mode
    PM->SLEEPCFG.bit.SLEEPMODE = 0x4;
    // Wait for it to take
    while(PM->SLEEPCFG.bit.SLEEPMODE != 0x4);

    // with the usb device completele disabled the sleeping works..
    // // Shutdown the USB peripheral
    // USB->DEVICE.CTRLA.bit.ENABLE = 0;
    // // Wait for synchronization
    // while(USB->DEVICE.SYNCBUSY.bit.ENABLE);

    delay(1000);
    Serial.begin(115200);
    // wait for Arduino Serial Monitor (native USB boards)
    const uint32_t timeout = 5*1000;
    uint32_t ts_start = millis();
    while((!Serial) && ((millis() - ts_start) < timeout)) {}

    Serial.println();
    Serial.println("test__SAMD51_low_power_sleep.ino");
    Serial.println();
}

void loop() {
    Serial.println("wait: ");
    for (size_t i = 10; i > 0; i--) {
        Serial.printf("%2d", i);
        digitalWrite(LED_BUILTIN, HIGH);
        delay(500);
        Serial.print(".");
        digitalWrite(LED_BUILTIN, LOW);
        delay(500);
        // clear line
        // Serial.print("\b\b  \b\b");
        // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
        // Erase in Line
        Serial.printf("\033[2K");
        // Cursor Horizontal Absolute
        // does not work..
        // Serial.printf("\033[1G");
        Serial.printf("\r");
        // Cursor Position
        // Serial.printf("\033[H");
    }
    Serial.println();
    go_to_sleep();
    board_dotstar.setPixelColor(0, board_dotstar_active_color);
    board_dotstar.show();
    Serial.println("iam awake again!");
    delay(1000);
}

void go_to_sleep() {
    Serial.println("go to sleep.");
    board_dotstar.setPixelColor(0, board_dotstar_standby_color);
    board_dotstar.show();


    Serial.println("USBDevice.detach");
    USBDevice.detach();
    Serial.println("USBDevice.end");
    USBDevice.end();
    USBDevice.standby();
    // Set D13 output to logic LOW
    digitalWrite(LED_BUILTIN, LOW);
    delay(50);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    delay(50);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    // Complete data memory operations
    __DSB();
    // Put the SAMDx1 into deep sleep Zzzzzzz....
    __WFI();

    // Try to reattach USB connection on "native USB" boards (connection is
    // lost on sleep). Host will also need to reattach to the Serial monitor.
    // Seems not entirely reliable, hence the LED indicator fallback.
    USBDevice.init();
    USBDevice.attach();
}

// Interrupt Service Routine (ISR)
void interrupt() {
    // Set LED_BUILTIN output to logic HIGH
    digitalWrite(LED_BUILTIN, HIGH);
}

board running: ~22mA
board sleeping: 1,5mA
i think that is a nice value for ‘not optimized in any way’ :slight_smile:
the FastLED-Wiki lists the Standby-Current of 1 APA102 with about 0.9ma@5v…

Sunny Greetings
stefan

Hi, sorry to awaken an old thread...

I'm unable to revive the USB Serial after awaking from sleep. Did anyone solve that? Sleeping itself seems to work. I do USB debugging so no serial is a killer.

It's a Feather M4 Express with solar+LiPo. My power budget will accommodate a couple milliamps so the schemes here will be more than adequate. Thanks!