cancel
Showing results for 
Search instead for 
Did you mean: 

Tips for using STM32 low-power modes

MCU Support TD
ST Employee

Introduction

For those developing applications that use low power modes, trying to achieve the current consumption numbers listed in the datasheet and performing general debugging can be troublesome. The purpose of this article is to give an overall understanding on how to get accurate current measurements. Furthermore, to understand the easiest ways to debug your low-power applications. 

For the purpose of creating specific examples for the article, I am using the NUCLEO-L011K4 and X-NUCLEO-LPM01A with STM32CubeMonitor-Power. However, the idea behind the article is that most of the information here is applicable, regardless of the STM32 you are using. 

1. Summary of Low Power Modes

Below is a summary of low power from highest consumption to lowest consumption, as well as shortest to longest wake-up time. Most STM32 MCUs have these low power modes in common:

1.1 Sleep

A simple low power mode that only shuts off the core, has a fast wake-up time. All other clocks and peripherals are still active. This mode is good for interrupt-driven applications that are not overly power conscientious and have some idle time at the end of the main loop.

1.2 Stop

Shuts off the core as well as most clocks in the system other than specific low speed oscillators. It is also able to maintain GPIO output state and to work with some select peripherals. This mode can be the trickiest to work with since it has the most configurability. This mode allows for lower consumption, while still being able to react to a more limited subset of interrupts and features than sleep.

1.3 Standby and shutdown

Lower consumption with limited functionality. Only the real-time clock (RTC), backup domain, and predetermined wake-up pins can be used in this mode.

2. Common tips

2.1 Suspending SysTick before entering low power modes

For sleep and stop modes, you want to suspend the SysTick system timer before entering the low power mode. If SysTick is still running, it prevents the core from entering stop mode properly. Even in the case of sleep mode, you may not want to wake up from each SysTick interrupt occurring every 1 ms.

2.2 Achieving the lowest possible power consumption

Since sleep mode is fairly straight forward, this section is about optimizing standby and stop modes.

Standby is simpler to optimize since it has less available peripherals and the IO state is not maintained. The power consumption is simply a function of the number of enabled features in standby. This is mainly the RTC if it is used, but some MCUs have other standby features that consume power, such as selective SRAM retention. Other than these considerations, achieving the lowest power consumption with standby mode is just as simple as entering the mode with the least number of needed features configured prior.

Stop mode is trickier to optimize. Mainly because on top of having a configurable wake-up clock source and peripherals that can be used, it also maintains the state of the GPIOs upon entry. This means that the power consumption of the MCU also depends on the surrounding components in some cases. The general recommendation for entering stop mode is to set all unused GPIOs into analog mode. This mode deactivates the input Schmitt trigger and has no pull up or pull down, which ensures the lowest consumption from each pin.

GPIO_InitTypeDef GPIO_InitStructure;

__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableUltraLowPower();
HAL_PWREx_EnableFastWakeUp();
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);

//L011 only has Ports A B and C
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();

GPIO_InitStructure.Pin = GPIO_PIN_All;
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
GPIO_InitStructure.Pull = GPIO_NOPULL;

HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);

__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOC_CLK_DISABLE();

HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_ResumeTick();

 In the case that you would want to retain the configuration of certain GPIOs, you could opt to exclude them from this. For instance, if you wanted to keep the PC13 configured the same way as it was:

GPIO_InitTypeDef GPIO_InitStructure;

__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableUltraLowPower();
HAL_PWREx_EnableFastWakeUp();
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);

//L011 only has Ports A B and C
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();

GPIO_InitStructure.Pin = GPIO_PIN_All;
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
GPIO_InitStructure.Pull = GPIO_NOPULL;

HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.Pin = GPIO_PIN_All;
GPIO_InitStructure.Pin &= ~(GPIO_PIN_13);
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
GPIO_InitStructure.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);

__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();

HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_ResumeTick();

Something to note is that setting the pin to analog mode only ensures lowest consumption from the perspective of the STM32. There can be situations where setting the pin to analog could actually increase the power consumption of the system. Perhaps in one situation where a device connected to a GPIO is still on when the GPIO is set to analog mode, this could leave the pin in floating state and cause toggling on the other device. In this situation, it may be better to leave the GPIO in a known push-pull state, which has minimal consumption to begin with. Consider all external connections when deciding which configuration is best.

2.3 Ensuring accurate power measurement

When comparing your current measurements to those listed in the datasheet, it is important that you are only measuring the current going into the VDD lines of the MCU. This is an obvious statement, but it is worth mentioning. It is common for custom PCBs not to be designed with a series break for an ammeter on the VDD line. By making changes in the firmware you will of course still see reductions in the consumption of the system.  However, by not having the isolated current of the MCU, you can never be sure that the low power modes are implemented optimally. Our development boards usually have a jumper to allow you to place an ammeter in series with VDD. You can always use that to verify that the firmware is working optimally. If your application really needs to optimize power consumption, consider having a way to measure the current of the just MCU.

Assuming we are measuring only the MCU current, there is still another important aspect to getting accurate measurements. Always remove the debug probe and power cycle before measuring current. Even when the debug pins are disabled in firmware, if the debug probe was previously connected, there is still some debug silicon active in the MCU that takes up energy. By removing the debug probe and power cycling, you should see numbers that are in line with the datasheet. After flashing and attaching the debugger, the current sits at a little over 100uA in stop mode

MCUSupportTD_0-1702918556927.png

After detaching the probe and power cycling, the current is down to under 1uA

MCUSupportTD_1-1702918713976.png

It is simple to see this when measuring the isolated MCU current. In a system seeing the 100uA drop could appear random if one was not aware of this phenomenon.

2.4 Debugging low-power modes

For debugging low-power applications, there are two debug configurations that you should be aware of. In the CubeIDE debug configuration for your project, under the debugger tab:

MCUSupportTD_0-1702918956534.png

The "Reset behavior" configuration is important because if your firmware has previously set the debug pins to analog mode, the "Reset Behavior" must be set to "Connect under reset."

"Debug in low power modes" allows for the debugger to stay attached in low power modes (assuming you have not set the debug pins to analog mode). This allows you to have breakpoints set in multiple ISRs while the device is in a low power mode. This can be useful for understanding which ISRs are being hit, and in what order. Adding any new breakpoints during execution or stopping execution also counts as an interrupt from the perspective of the core. It causes the MCU to exit the low power mode. 

When you find that your peripherals work in run mode but are not working in stop mode, one of the key points to check would be the clock source for that peripheral. Since stop typically shuts off all oscillators that are not low speed oscillators, it is possible that the peripheral you are using is taking its clock source from a clock that was shut off. For example, this LPTIM allows for configurations that would prevent it from running in stop mode, so you would need to select either LSE or LSI as the clock source.

MCUSupportTD_1-1702919543374.png

 

Or sometimes there are specific features for a particular MCU that allow certain peripherals to use high speed internal oscillators exclusively in stop mode. These kinds of implementations are MCU specific, so you need to refer to the reference manual for your chosen MCU. Generally, LSE and LSI oscillators are always available in stop mode.

2.5 Masking Interrupts before entering sleep or stop

In sleep and stop modes, the core waits for an interrupt. Once an interrupt is received, if interrupts are not masked, the first code that is executed is the code within the respective IRQ Handler. By masking interrupts, you can ensure that some code always executes before the IRQ Handler does:

HAL_SuspendTick();
__disable_irq();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/*
 * Code here will execute before the IRQ Handler associated 
 * with the interrupt that woke the core.
 */
HAL_ResumeTick();
__enable_irq(); // Once interrupts are unmasked, the IRQ Handler can execute

This can be helpful in a few ways, but here are two examples of how this can be used.

Example 1: Measure application idle time

In an interrupt-driven application, you can enter sleep mode at the end of the main loop and mask the interrupts to help estimate how much time your application is idle. The example below uses a GPIO toggle that could be measured on a scope. You could also implement a software measurement using the TIM peripherals or debug cycle counters:

/*
 * PB10 is arbitrarily selected as a measurement signal
 * 
 * Could also use a timer as a means of software measurement
 */
HAL_SuspendTick();
__disable_irq();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, 1);
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, 0);
HAL_ResumeTick();
__enable_irq();

Example 2: Reconfigure system clock after exiting stop before executing IRQ Handler

When exiting stop mode, typically the IRQ Handler code executes with the core clock by the wake-up clock specified before entering stop mode. Using interrupt masking, you can reconfigure the system clock before the IRQ Handler executes.

// Somehwere previously the wakeup clock is configured
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
...
...
HAL_SuspendTick();
__disable_irq();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
SystemClock_Config();
HAL_ResumeTick();
__enable_irq(); 

2.6 Using the sleep-on-exit feature

I would like to briefly mention a lesser known feature. Sleep-on-exit makes it so when the core finishes executing an IRQ Handler, it goes back into the low power mode instead of going back into the main loop.

HAL_PWR_EnableSleepOnExit();
HAL_PWR_DisableSleepOnExit();

The first way this can be useful is for applications that are totally interrupt based and have an empty main while loop. Sleep-on-exit means the core will not need to waste time going back into the main loop to go to sleep.

This feature can also be used conditionally. Below is some pseudo code describing the use case. Assume that the callback is called from an IRQ Handler:

void IRQ_CallbackFunc(void) {
  if (condition) {
    HAL_PWR_DisableSleepOnExit();
  }
  else {
  // Other stuff happens here and core goes right back to sleep
  }
}

 

Related links

 

Comments
BarryWhit
Senior III

Excellent Post. Thank you!

Version history
Last update:
‎2024-06-04 05:39 AM
Updated by: