Page 1 of 1

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

Posted: Mon Jul 05, 2021 2:58 pm
by appels
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?

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

Posted: Mon Jul 05, 2021 3:16 pm
by Roberthh
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.

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

Posted: Tue Jul 06, 2021 5:44 am
by pythoncoder
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.

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

Posted: Tue Jul 06, 2021 7:25 am
by jimmo
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).