Power optimization in embedded systems is the discipline of minimizing energy consumption to maximize battery life, reduce thermal output, and meet regulatory requirements for energy efficiency in battery-powered IoT devices, wearables, and remote sensors. A holistic power optimization strategy addresses all levels: hardware design (voltage regulator selection, component choice, PCB layout), firmware architecture (sleep mode management, clock gating, interrupt-driven design), communication scheduling (duty cycling radios, minimizing transmit time), and peripheral management (powering down unused sensors and interfaces). Modern ultra-low-power MCUs like the STM32L4/U5, Nordic nRF52/nRF54, and Silicon Labs EFR32 offer multiple sleep modes consuming from 1.8 uA in deep sleep to sub-100 nA in shutdown mode with RTC retention. Achieving multi-year battery life on a coin cell (CR2032, 225 mAh) requires keeping the average current consumption below 5-10 uA, which demands careful measurement using tools like the Nordic Power Profiler Kit II, Otii Arc, or Joulescope. Every microsecond of unnecessary active time and every milliamp of wasted current directly reduces battery life.
How Do MCU Sleep Modes Work?
Modern MCUs provide a hierarchy of low-power modes with different trade-offs between power consumption and wake-up latency. Active mode (running at full clock speed) consumes the most power, typically 10-100 mA depending on clock frequency and peripherals. Sleep/Idle mode stops the CPU clock but keeps peripherals and SRAM powered, reducing consumption to 1-5 mA. Stop mode (STM32 terminology) or System ON sleep (Nordic) powers down most of the chip, retaining SRAM and register state, consuming 1-10 uA with sub-millisecond wake-up. Standby/Shutdown mode retains only a small amount of backup RAM and RTC, consuming 100-500 nA but requiring a full reboot on wake-up. The optimal strategy is to spend the maximum possible time in the deepest sleep mode and minimize the active duty cycle.
/* STM32L4 Low-Power Mode Configuration */
// Configure STOP2 mode (1.3 uA typical)
void enter_stop2_mode(uint32_t wakeup_seconds) {
// Disable all unnecessary peripherals
HAL_ADC_DeInit(&hadc1);
HAL_SPI_DeInit(&hspi1);
// Configure RTC wakeup timer
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,
wakeup_seconds * 2048, // 2048 Hz RTC clock
RTC_WAKEUPCLOCK_RTCCLK_DIV16);
// Reduce regulator to low-power mode
HAL_PWREx_EnableLowPowerRunMode();
// Disable SysTick to prevent unnecessary wakeup
HAL_SuspendTick();
// Enter STOP2 mode
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
// Execution resumes here after wakeup
HAL_ResumeTick();
SystemClock_Config(); // Reconfigure clocks
}
// Typical duty-cycled sensor application
while (1) {
// Active phase: ~50ms at 80 MHz = ~15 mA
sensor_read();
process_data();
if (should_transmit()) {
radio_transmit(); // ~10 mA for BLE, 100ms
}
// Sleep phase: 60 seconds at 1.3 uA
enter_stop2_mode(60);
}What Firmware Techniques Reduce Power Consumption?
Key firmware-level power optimization techniques:
- Interrupt-driven design: Never poll. Use interrupts for UART, SPI, GPIO, and timer events. Polling wastes CPU cycles and prevents sleep.
- DMA for data transfers: Use DMA for ADC sampling, SPI/I2C transactions, and UART communication. The CPU can sleep while DMA handles data movement.
- Clock gating: Disable peripheral clocks when not in use. On STM32, each APB/AHB peripheral clock can be individually controlled.
- Dynamic clock scaling: Run at the lowest frequency that meets your processing deadline. Power consumption scales linearly with clock frequency.
- Batch processing: Accumulate multiple sensor readings before processing and transmitting, maximizing sleep time between bursts.
- RAM retention management: On MCUs with banked RAM, power down unused SRAM banks in deep sleep to save 0.5-2 uA per bank.
How Do You Measure Real Power Consumption?
Accurate power measurement is essential because MCU current consumption varies by six orders of magnitude (100 nA in shutdown to 100 mA in active with radio). Standard multimeters lack the dynamic range and bandwidth for this measurement. Dedicated tools like the Nordic Power Profiler Kit II ($99) provide continuous current measurement from 200 nA to 1 A with microsecond resolution. The Qoitech Otii Arc provides higher precision and longer recording capability. For the most detailed analysis, use a Joulescope ($499-999) which offers 2 nA resolution and real-time energy computation. When optimizing, create a power budget spreadsheet listing each operating mode, its duration per cycle, current consumption, and the resulting average current. This allows you to identify which phases dominate total energy consumption and focus optimization efforts where they will have the greatest impact.
What Is a Practical Power Budget Example?
Consider a BLE environmental sensor reporting temperature and humidity every 60 seconds. Active phase: wake up (1ms at 5mA), read sensor via I2C (10ms at 3mA), process data (5ms at 8mA), BLE advertise and transmit (30ms at 10mA). Sleep phase: STOP2 mode for 59.954 seconds at 1.5uA. Average current = (0.001*5 + 0.01*3 + 0.005*8 + 0.03*10 + 59.954*0.0015) / 60 = 0.0073 mA = 7.3 uA. With a CR2032 (225mAh), battery life = 225/0.0073 = 30,822 hours = 3.5 years. This demonstrates how even brief active periods dominate the energy budget, and why reducing transmit time from 30ms to 15ms would extend battery life by approximately 10%.
Key takeaway: Achieving multi-year battery life on coin cells requires keeping average current below 5-10 uA through deep sleep modes (sub-microamp), interrupt-driven firmware design (never poll), DMA-based data transfers, and aggressive duty cycling where the device sleeps for 99%+ of the time. Measure real consumption with dedicated profiling tools, not multimeters.
How Did We Achieve 5-Year Battery Life on a Coin Cell?
At EmbedCrest, we developed a BLE environmental sensor for a commercial building management company that required 5-year operation on a single CR2032 coin cell (225 mAh nominal, 190 mAh effective at low-current discharge). The design used an nRF52832 SoC with a Bosch BME280 temperature/humidity/pressure sensor on I2C and an SGP30 VOC sensor powered through a load switch. The firmware was entirely interrupt-driven: a 60-second RTC timer woke the MCU from System OFF mode (0.3 uA), the MCU powered on the BME280 via load switch, triggered a forced measurement, read results via I2C using EasyDMA, powered off the sensor, encoded the data in a 12-byte BLE advertisement payload, transmitted a single non-connectable advertisement, and returned to System OFF within 45 ms total active time. The Nordic Power Profiler Kit II measured: 0.3 uA sleep current, 4.2 mA average during the 45 ms active window (with 8.5 mA peak during BLE TX at 0 dBm). The calculated average current was 3.45 uA, yielding a theoretical battery life of 6.3 years. In field testing over 14 months, extrapolated battery life confirmed 5.2 years accounting for self-discharge and temperature effects.
What Are the Most Overlooked Power Consumers?
Several power consumers are frequently overlooked in initial designs. Pull-up resistors on I2C lines consume P = V^2/R continuously. With 4.7 kOhm pull-ups at 3.3V and both SDA and SCL pulled high during idle, that is 1.4 mA of continuous drain, more than a 1000x increase over MCU deep-sleep current. Use MCU internal pull-ups only during transactions, disable them between accesses, or use an I2C bus switch (TCA9548A) to disconnect peripherals. Voltage regulator quiescent current varies enormously: a standard LDO (AMS1117) draws 5-10 mA quiescent, while an ultra-low-quiescent LDO (TPS7A02) draws 25 nA. LED power indicators are a silent killer: a typical LED at 1 mA draws more than the entire MCU in deep sleep. Remove power LEDs from production PCBs or gate them behind a GPIO. Flash memory leakage current on external SPI flash (W25Q128) can reach 1-5 uA in standby. Use the deep power-down command to reduce leakage to 10-50 nA. Always create a complete power tree diagram listing every component, its operating mode, and current draw in each system state.
How Do You Debug Unexpected Power Consumption?
When measured power consumption exceeds calculations, use systematic elimination to identify the culprit. First, disconnect all external peripherals and measure bare MCU sleep current. If it exceeds the datasheet value, check for floating GPIO pins (each floating input can add 1-100 uA due to half-VDD biasing of the input buffer). Configure all unused GPIOs as outputs driven low or as inputs with pull-downs. Second, reconnect peripherals one at a time, measuring the incremental current after each. Third, use a current profiler (Nordic PPK II, Joulescope) to capture the time-domain current waveform during a complete duty cycle. Look for unexpected wake-ups (rogue interrupts), unexpectedly long active periods (blocking I2C transactions instead of DMA), and elevated baseline current between events (peripheral not returning to low-power state). On STM32, check that all peripheral clocks are disabled before entering STOP mode using __HAL_RCC_*_CLK_DISABLE macros. On nRF52, verify that the HFCLK (high-frequency crystal) stops during System ON idle and that no tasks are requesting the HFCLK through the clock management API.


