: Why does the deepsleep current on the Pyboard differ between MicroPython and Arduino code

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
Post Reply
appels
Posts: 13
Joined: Thu Dec 03, 2020 3:14 pm

: Why does the deepsleep current on the Pyboard differ between MicroPython and Arduino code

Post by appels » Mon Jul 05, 2021 2:58 pm

Hi, I’m using the Pyboard to compare Arduino code and MicroPython code.

I noticed that the deep sleep current differs between the two languages.
The deep sleep current I measured for MicroPython code is 10 µA and for Arduino code it is 40 µA.
This was very surprising to me, because I would asume that if one of them would be greater than the other it would be the deep sleep current of the MicroPython code.
I tried to look into this by digging in the source code, but I can’t seem to find why the currents aren't the same.

First of all here is the code that I used, it is just a simple program that puts the microcontroller to sleep for 1 second as soon as it wakes up:

This is the Arduino code:

Code: Select all

#include "STM32LowPower.h"
void setup() {
  LowPower.begin();
}

void loop() {
  LowPower.shutdown(1000);
}
And this is the MicroPython code:

Code: Select all

import machine
machine.deepsleep(1000)
When I looked into the MicroPython source code I found that “machine.deepsleep()” was implemented in modmachine.c in micropython-master/ports/stm32/. Like so:

Code: Select all

STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) {
    if (n_args != 0) {
        mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]};
        pyb_rtc_wakeup(2, args2);
    }
    powerctrl_enter_standby_mode();
    return mp_const_none;
}
This function calls “powerctrl_enter_standby_mode()” that is implemented in powerctrl.c in micropython-master/ports/stm32/. :

Code: Select all

void powerctrl_enter_standby_mode(void) {
    rtc_init_finalise();

    #if defined(MICROPY_BOARD_ENTER_STANDBY)
    MICROPY_BOARD_ENTER_STANDBY
    #endif

    // We need to clear the PWR wake-up-flag before entering standby, since
    // the flag may have been set by a previous wake-up event.  Furthermore,
    // we need to disable the wake-up sources while clearing this flag, so
    // that if a source is active it does actually wake the device.
    // See section 5.3.7 of RM0090.

    // Note: we only support RTC ALRA, ALRB, WUT and TS.
    // TODO support TAMP and WKUP (PA0 external pin).
    #if defined(STM32F0) || defined(STM32L0)
    #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_WUTIE | RTC_CR_TSIE)
    #define ISR_BITS (RTC_ISR_ALRAF | RTC_ISR_WUTF | RTC_ISR_TSF)
    #else
    #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_ALRBIE | RTC_CR_WUTIE | RTC_CR_TSIE)
    #define ISR_BITS (RTC_ISR_ALRAF | RTC_ISR_ALRBF | RTC_ISR_WUTF | RTC_ISR_TSF)
    #endif

    // save RTC interrupts
    uint32_t save_irq_bits = RTC->CR & CR_BITS;

    // disable register write protection
    RTC->WPR = 0xca;
    RTC->WPR = 0x53;

    // disable RTC interrupts
    RTC->CR &= ~CR_BITS;

    // clear RTC wake-up flags
    RTC->ISR &= ~ISR_BITS;

    #if defined(STM32F7)
    // disable wake-up flags
    PWR->CSR2 &= ~(PWR_CSR2_EWUP6 | PWR_CSR2_EWUP5 | PWR_CSR2_EWUP4 | PWR_CSR2_EWUP3 | PWR_CSR2_EWUP2 | PWR_CSR2_EWUP1);
    // clear global wake-up flag
    PWR->CR2 |= PWR_CR2_CWUPF6 | PWR_CR2_CWUPF5 | PWR_CR2_CWUPF4 | PWR_CR2_CWUPF3 | PWR_CR2_CWUPF2 | PWR_CR2_CWUPF1;
    #elif defined(STM32H7)
    EXTI_D1->PR1 = 0x3fffff;
    PWR->WKUPCR |= PWR_WAKEUP_FLAG1 | PWR_WAKEUP_FLAG2 | PWR_WAKEUP_FLAG3 | PWR_WAKEUP_FLAG4 | PWR_WAKEUP_FLAG5 | PWR_WAKEUP_FLAG6;
    #elif defined(STM32L4) || defined(STM32WB)
    // clear all wake-up flags
    PWR->SCR |= PWR_SCR_CWUF5 | PWR_SCR_CWUF4 | PWR_SCR_CWUF3 | PWR_SCR_CWUF2 | PWR_SCR_CWUF1;
    // TODO
    #else
    // clear global wake-up flag
    PWR->CR |= PWR_CR_CWUF;
    #endif

    // enable previously-enabled RTC interrupts
    RTC->CR |= save_irq_bits;

    // enable register write protection
    RTC->WPR = 0xff;

    #if defined(STM32F7)
    // Enable the internal (eg RTC) wakeup sources
    // See Errata 2.2.2 "Wakeup from Standby mode when the back-up SRAM regulator is enabled"
    PWR->CSR1 |= PWR_CSR1_EIWUP;
    #endif

    // enter standby mode
    HAL_PWR_EnterSTANDBYMode();
    // we never return; MCU is reset on exit from standby
}
And then this function calls “HAL_PWR_EnterSTANDBYMode()”


Then if we look at the Arduino implementation (it uses a library from github called STM32LowPower) we see that “LowPower.shutdown()” is implemented in STM32LowPower.cpp in STM32LowPower-master/src/. Like so:

Code: Select all

void STM32LowPower::shutdown(uint32_t ms)
{
  if ((ms != 0) || _rtc_wakeup) {
    programRtcWakeUp(ms, SHUTDOWN_MODE);
  }
  LowPower_shutdown();
}
This function calls “LowPower_shutdown()” that is implemented in low_power.c in STM32LowPower-master/src/. :

Code: Select all

void LowPower_shutdown()
{
  __disable_irq();
#if defined(STM32L4xx) || defined(STM32L5xx)
  /* LSE must be on to use shutdown mode */
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == SET) {
    HAL_PWREx_EnterSHUTDOWNMode();
  } else
#endif
  {
    LowPower_standby();
  }
}
Which calls “LowPower_standby()” that is also implemented in low_power.c in STM32LowPower-master/src/.

Code: Select all

void LowPower_standby()
{
  __disable_irq();

#if defined(STM32L0xx) || defined(STM32L1xx)
  /* Enable Ultra low power mode */
  HAL_PWREx_EnableUltraLowPower();

  /* Enable the fast wake up from Ultra low power mode */
  HAL_PWREx_EnableFastWakeUp();
#endif

  HAL_PWR_EnterSTANDBYMode();
}
And finally this function also calls “HAL_PWR_EnterSTANDBYMode()”


So my question is why do the deep sleep currents differ. Both the MicroPython and Arduino code call this “HAL_PWR_EnterSTANDBYMode()” function. Which is one of the Hardware Abstraction Layer functions provided by STM to get into the 'standby low-power mode'.
So one would think that the deep sleep current would be exactly the same. But it isn’t, so does anyone know why this is the case?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: : Why does the deepsleep current on the Pyboard differ between MicroPython and Arduino code

Post by Roberthh » Mon Jul 05, 2021 3:16 pm

30 uA is not much. That is about the current though a 100kOhm resistor ar 3.3V. So the difference could be caused by an I/O port at a different level or mode.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: : Why does the deepsleep current on the Pyboard differ between MicroPython and Arduino code

Post by pythoncoder » Tue Jul 06, 2021 5:44 am

Yes. A floating input can consume power. For absolute minimum I believe all I/O should be set to output (where possible). On a Pyboard bear in mind the I2C pullups. Leave ports with physical pullups as inputs.
Peter Hinch
Index to my micropython libraries.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: : Why does the deepsleep current on the Pyboard differ between MicroPython and Arduino code

Post by jimmo » Tue Jul 06, 2021 7:25 am

appels wrote:
Mon Jul 05, 2021 2:58 pm
This was very surprising to me, because I would asume that if one of them would be greater than the other it would be the deep sleep current of the MicroPython code.
Why's that? ;) (At the end of the day, sleep mode is sleep mode -- doesn't matter what code was running previously). What matters is which peripherals are enabled or other current sinks/sources on the board (as others have pointed out, floating GPIO is a good candidate).

Post Reply