Back to Blog
TutorialsFebruary 25, 202611 min read

Power Optimized Firmware Development: Techniques for Battery-Powered Embedded Devices

Master power optimized firmware development for battery-powered embedded systems. Covers sleep modes, clock gating, peripheral management, and bare metal embedded programming techniques.

Power Optimized Firmware Development: Techniques for Battery-Powered Embedded Devices

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.

Power OptimizationLow PowerBatterySleep ModesEmbedded Systems

Rajdatt

Lead Embedded Systems Engineer at EmbedCrest Technology

Delivering enterprise grade embedded systems, IoT, and Edge AI engineering solutions.

FAQ

Frequently Asked Questions

What is the best MCU for ultra-low-power IoT applications?

The STM32U5 (Cortex-M33, 110 nA shutdown, 16 uA/MHz active) and Nordic nRF54L (Cortex-M33, 100 nA shutdown) lead in power efficiency. For BLE applications specifically, the nRF52840 offers excellent radio power combined with low sleep current.

How do you achieve multi-year battery life on a coin cell?

Keep average current under 10 uA. This requires: deep sleep between readings (60+ seconds), minimal radio transmit time (under 50ms), efficient sensor interfaces (DMA-based I2C/SPI), and powering down all unused peripherals. CR2032 at 10 uA average lasts approximately 2.5 years.

Does reducing clock frequency always save power?

Not necessarily. Lower clock frequencies mean longer active times. The total energy depends on current * time. If a task at 80 MHz takes 1ms at 15mA (15 uJ) but at 8 MHz takes 10ms at 3mA (30 uJ), the higher frequency is more energy efficient. This is called "race to sleep".

Ready to Build Your Embedded Solution?

From Edge AI to industrial IoT, our engineering team delivers end to end embedded systems solutions. Let us discuss your project requirements.

Get in Touch