STM32U5 STOP Mode: Treat Wakeup Current as a Firmware Interface
Low-power work on STM32U5 is not finished when the code enters STOP mode. In a product, the hard part is making the wakeup path repeatable, measurable, and owned by firmware instead of being a collection of CubeMX checkboxes and board-level assumptions.
Why STOP mode bugs survive until late testing
STM32U5 devices are built for aggressive low-power designs, but the product current is still the sum of firmware state, GPIO configuration, external circuits, clock recovery, and RTOS behaviour after wakeup. A board can look excellent in an ST example and still miss its battery budget by a factor of ten once sensors, pull-ups, level shifters, LEDs, debug headers and periodic tasks are present.
The most common failure mode is ownership confusion. Hardware expects firmware to leave pins quiet. Firmware assumes CubeMX generated the right low-power state. The application assumes waking from STOP is just a long interrupt latency. The result is a product that passes functional tests but fails shelf-life, standby-current or intermittent wakeup testing.
Make low power an explicit interface
I prefer to treat low power as a small platform interface with three responsibilities: prepare the board, enter the selected power mode, and restore the runtime contract after wakeup. That interface should know which pins are allowed to wake the MCU, which peripherals must be quiesced, which rails remain powered, and which clocks need to be rebuilt.
On STM32U5, STOP variants retain enough state to be practical for event-driven products, but the details still matter. GPIOs left as digital inputs can leak through external networks. A peripheral clock that is not restored can produce a failure that appears unrelated to low power. SysTick and RTOS timekeeping can distort scheduling after a long sleep if the design does not define what “time passed” means.
A small STOP entry shape
The following code is intentionally modest. It shows the structure I want in a client codebase: one place prepares the pins, one place enters STOP2, and one place restores clocks and peripheral ownership. The exact pins and peripherals must match the real board.
#include "stm32u5xx_hal.h"
static void app_prepare_gpio_for_stop(void)
{
GPIO_InitTypeDef gpio = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
gpio.Mode = GPIO_MODE_ANALOG;
gpio.Pull = GPIO_NOPULL;
gpio.Pin = GPIO_PIN_All;
HAL_GPIO_Init(GPIOA, &gpio);
HAL_GPIO_Init(GPIOB, &gpio);
HAL_GPIO_Init(GPIOC, &gpio);
/* Reconfigure only the real wakeup pins after the blanket analog pass. */
gpio.Pin = GPIO_PIN_13;
gpio.Mode = GPIO_MODE_IT_FALLING;
gpio.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &gpio);
}
void app_enter_stop2_until_event(void)
{
app_prepare_gpio_for_stop();
HAL_SuspendTick();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
HAL_ResumeTick();
SystemClock_Config(); /* Restore PLL/sysclk after STOP. */
MX_GPIO_Init(); /* Restore product pin ownership. */
MX_I2C1_Init(); /* Re-init peripherals whose clocks were gated. */
}
The blanket analog configuration is not a rule to copy blindly. It is a review forcing function. Every pin that is not allowed to float quietly must be named again: wakeup input, retained chip-select, rail enable, interrupt line, or debug exception. That is much easier to audit than scattered low-power tweaks in several drivers.
Practical example: battery sensor node with an external interrupt
Consider a battery-powered industrial sensor using an STM32U5, an external accelerometer interrupt, an I2C environmental sensor and a radio module. The node spends most of its life asleep, wakes on motion or a periodic RTC event, samples data, transmits a compact packet, and returns to STOP. A naive implementation enters STOP after the last application task finishes. A production implementation has a board sleep contract.
Before STOP, the radio driver must finish or abort any transfer, the I2C bus must not be mid-transaction, sensor interrupt lines must be configured with the expected pulls, and non-wakeup GPIOs should be moved to their lowest-leakage state. After wakeup, firmware must re-establish the system clock, rebuild peripheral drivers that depend on gated clocks, sample and clear the wakeup reason, and publish a health counter. If wakeup came from the accelerometer, the first I2C read may need a device-specific settling delay. If wakeup came from RTC, the application may skip expensive sensor work when the battery is low.
Measure the transitions, not only the steady state
Datasheet current numbers are useful, but product failures often hide in transitions. A device may spend only microamps in STOP but burn milliamps for too long after wakeup because the firmware waits on a fixed delay, restarts clocks inefficiently, or retries a peripheral that is still powered down. I would log wakeup reason, time-to-first-sample, time-to-radio-ready, and the number of failed peripheral reinitializations.
For bench work, combine a current probe or power analyzer with firmware trace points. Put GPIO markers around “prepare sleep”, “entered STOP”, “woke”, “clock restored”, and “application ready”. This turns low-power debugging from guesswork into a timing diagram that both firmware and hardware engineers can discuss.
Practical checklist
- List every wakeup source and decide whether it is level, edge, RTC, EXTI or peripheral-driven.
- Review every GPIO against the schematic: analog, retained output, pull-up, pull-down, wake input or external rail control.
- Disable or park peripherals before STOP; do not leave I2C/SPI transactions half-owned by an RTOS task.
- Suspend SysTick or define how RTOS time is handled during sleep.
- Restore system clocks explicitly after STOP and reinitialize peripherals that depend on them.
- Measure wakeup latency and energy, not only static STOP current.
- Expose counters for wake reason, failed restores, unexpected resets and time since last successful sleep cycle.
How I would approach this on a client project
I would start with the schematic, not the CubeMX file. For each net connected to the MCU, I would write down its sleep state, owner, leakage risk and wakeup role. Then I would implement a single board low-power module and make application code request sleep through that module only. On the bench, I would measure three cases separately: MCU-only baseline, board-level STOP with external circuits populated, and full application sleep/wakeup cycles under realistic radio and sensor timing.
The deliverable is not “STOP2 enabled”. It is a repeatable sleep contract: the product can explain why it woke, recover its clocks and peripherals, and prove that standby current and wake energy stay inside the budget across firmware releases.
Comments
Have a concrete STM32U5 low-power case or a wakeup bug from the field? Send a short note by email.