Low-Power Design & Energy Budgets
Battery-powered devices live or die by their energy budget. Learn MCU power modes (run, sleep, stop, standby, shutdown), dynamic vs static power, race-to-sleep, wake-up sources and latency, peripheral and GPIO leakage, duty-cycling radios and sensors, measuring µA currents, and building a complete battery-life budget.
A coin-cell sensor that should last five years has an average current budget of just a few microamps. Achieving that means the MCU spends > 99.9% of its time in deep sleep, wakes on an RTC alarm, does its work in milliseconds, and goes back to sleep — while every pin, pull-up and regulator is checked for leakage.
Power optimisation is a system job: firmware, hardware and even the choice of battery chemistry interact. The numbers here let you predict battery life before building anything.
An analogy
Think of a lighthouse keeper who naps between ships: the lighthouse is dark most of the time, flashes briefly when needed, and the keeper's alarm clock (RTC) or the ship's horn (pin interrupt) wakes him. The shorter the flashes and the deeper the naps, the longer the oil lasts.
Where the current goes
CMOS logic draws current mainly when switching (dynamic power ∝ frequency and V²) plus leakage (static power, grows with temperature). An MCU running at 64 MHz might draw ~6 mA; in STOP mode with RAM retained ~2 µA; in shutdown ~30 nA. External parts — LEDs, pull-ups, regulators, sensors, radios — often draw more than the MCU itself.
Power modes
Deeper modes save more but cost wake-up time and lose state:
- Sleep: CPU clock stopped, peripherals run; wake in cycles (WFI/WFE)
- Stop: most clocks off, RAM and registers kept; wake in µs; a few µA
- Standby: RAM lost (except backup registers); wake ≈ reset; < 1 µA
- Shutdown: only wake-up pins/RTC; tens of nA
Race to sleep and duty cycling
Because static and peripheral overheads are paid for as long as you are awake, it is often better to run fast and sleep sooner. Batch work (sample 10 readings, send once), use DMA and peripherals that operate autonomously in low-power modes (LPUART, LPTIM, comparator wake-up), and keep radios off as long as possible — a BLE advertisement or LoRa packet dominates most budgets.
Measuring and verifying
Use a source-measure unit or power profiler (e.g. Nordic PPK2, Joulescope) with µA–mA dynamic range, and integrate charge over a full cycle. Check leakage per GPIO, pull-up resistors (3.3 V / 10 kΩ = 330 µA!), regulator quiescent current, and debug interfaces left enabled (debug in low-power modes keeps clocks alive). Budget battery self-discharge, temperature derating and end-of-life voltage.
Formulas
- Dynamic power:
P_dyn = α·C·V²·f (halve V → quarter power) - Average current (periodic task):
I_avg = Q_active / T + I_sleep·(1 − t_active/T) - Battery life:
t = C_bat × derating / I_avg - Energy per wake:
E = V·I_active·t_active + wake-up and radio overheads - Race-to-sleep test:
Faster is better if (I_fast · t_fast) < (I_slow · t_slow)
Worked example: Sensor-node battery life
Given: Active 8 mA for 20 ms every 10 s; Sleep 2 µA; 1000 mAh
Find: Average current and life.
- I_avg = 8 × 0.002 + 0.002 × 0.998 = 0.0180 mA
- t = 1000 / 0.018 = 55 568 h
Answer: ≈ 18 µA → 6.3 years (before derating)
Worked example: Race to sleep
Given: Task = 1 M cycles; 16 MHz: 5 mA; 64 MHz: 17 mA (incl. 1 mA static)
Find: Charge per task.
- 16 MHz: 62.5 ms × 5 mA = 312.5 µA·s
- 64 MHz: 15.6 ms × 17 mA = 265.6 µA·s
Answer: Running at 64 MHz uses 15% less charge
Worked example: Hidden pull-up
Given: 10 kΩ pull-up on a button held to ground by a closed switch
Find: Current.
- 3.3 V / 10 kΩ
Answer: 330 µA — 150× the MCU's STOP current!
RTC-driven sense-and-sleep loop (STM32L4-style HAL)
int main(void)
{
HAL_Init();
clock_config_msi_4mhz(); /* low-power clock is enough */
gpio_all_analog(); /* unused pins: analog = lowest leak */
rtc_wakeup_every(10); /* wake every 10 s */
for (;;) {
sensor_power(ON); /* switch sensor supply via GPIO */
HAL_Delay(2); /* sensor settling */
int16_t t = sensor_read_temp();
sensor_power(OFF);
if (radio_needed(t)) radio_send(t); /* radio: biggest cost */
HAL_SuspendTick(); /* stop SysTick waking us */
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); /* ~2 µA */
HAL_ResumeTick();
clock_config_msi_4mhz(); /* clocks must be restored after STOP */
}
}Everything not needed is off: sensor power switched by a GPIO, unused pins set to analog mode, SysTick suspended. After STOP mode the clock configuration must be restored before continuing.
Common mistakes
- Wrong: Leaving unused GPIOs floating. — Right: Floating inputs toggle and leak; set them to analog or a defined level.
- Wrong: Forgetting external parts (LEDs, regulators, pull-ups). — Right: Budget every component; switch sensor power with a GPIO/load switch.
- Wrong: Measuring sleep current with the debugger attached. — Right: Detach debug; it keeps clocks and power domains alive.
Applications
- Wearables: Months of operation on small Li-Po cells.
- Smart meters: 10–15-year life on primary lithium batteries.
- Asset trackers: Motion wake-up from accelerometer interrupts.
- Energy harvesting: Solar/vibration-powered nodes with µJ budgets.
Key takeaways
- I_avg = charge per event / interval + sleep current.
- Sleep current dominates at low duty cycle.
- Race to sleep; switch off everything unused.
- Measure with a proper power profiler, debugger detached.
Going further
- DVFS: Scale voltage with frequency (voltage ranges on STM32L).
- Energy harvesting PMICs: MPPT and storage on supercapacitors.
- Battery modelling: Pulse loads, internal resistance and temperature effects on usable capacity.