·15 min read·BASHCAT 技術團隊·embedded

The Complete Guide to Low-Power Embedded System Design

An in-depth analysis of low-power design techniques for embedded systems, covering chip selection, circuit design, and firmware optimization to help you build battery-powered devices that last for years.

#Low Power#Embedded Systems#STM32#nRF52#Battery#IoT#Power-Efficient Design

The Complete Guide to Low-Power Embedded System Design

IoT devices typically require battery power, and enabling devices to operate for months or even years is a core challenge. This article shares practical low-power design experience across three dimensions: chip selection, circuit design, and firmware optimization.

Power Consumption Fundamentals

Battery Life Calculation

The basic formula for battery life:

Battery Life (hours) = Battery Capacity (mAh) / Average Current (mA)

However, real-world scenarios are more complex and require considering:

  • Periodic switching between operating modes
  • Battery self-discharge (approximately 1-3% per month)
  • Temperature effects
  • Battery aging

Power Consumption Source Analysis

Typical power consumption distribution for an IoT device:

Module Operating Current Percentage
MCU Computation 5-50 mA 20%
Wireless Communication 10-100 mA 50%
Sensors 1-10 mA 15%
Power Conversion 5-15% loss 15%

Chip Selection

Ultra-Low-Power MCU Comparison

Chip Standby Current Operating Current Wake-up Time
STM32L4 30 nA 28 uA/MHz 5 us
nRF52840 0.4 uA 51 uA/MHz 3 us
ESP32-S3 7 uA 240 uA/MHz 250 us
MSP430FR 350 nA 100 uA/MHz 3.5 us

Selection Recommendations

  • BLE required: Nordic nRF52 series
  • WiFi required: ESP32-C6 (low-power WiFi)
  • Sensor-only applications: STM32L0/L4 series
  • Ultra-low power: MSP430FR series

Circuit Design Techniques

1. Power Switches

Use MOSFETs or load switch ICs to control peripheral power:

                    ┌─────────────────┐
  VCC ──────┬───────┤ LDO 3.3V        │
            │       └────────┬────────┘
            │                │
            │       ┌────────┴────────┐
            │       │ MCU (Always ON)  │
            │       └────────┬────────┘
            │                │ GPIO
            │                ▼
            │       ┌────────────────┐
            └───────┤ Load Switch IC  │
                    │ (TPS22860)     │
                    └────────┬───────┘
                             │
                    ┌────────┴────────┐
                    │ Sensors/Modules  │
                    │ (0 current when  │
                    │  powered off)    │
                    └─────────────────┘

2. Pull-up Resistor Optimization

I2C pull-up resistor selection affects power consumption:

  • 10k ohm: Standard choice, balancing power consumption and signal quality
  • 4.7k ohm: High-speed transmission, higher power consumption
  • Internal pull-up: Lowest power consumption, but signal quality may suffer

3. Decoupling Capacitors

Use appropriate decoupling capacitors to reduce high-frequency noise:

// Typical configuration
// - 100nF: per VCC pin
// - 10uF: power input
// - 1uF: sensitive analog circuits

Firmware Optimization

1. Sleep Mode Selection

Using STM32L4 as an example, there are multiple low-power modes:

// STM32L4 low-power mode selection
void enter_low_power_mode(LowPowerMode mode) {
    switch (mode) {
        case LP_SLEEP:
            // CPU only stopped, peripherals continue running
            // Current: ~200 uA
            HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,
                                    PWR_SLEEPENTRY_WFI);
            break;

        case LP_STOP0:
            // CPU and most clocks stopped
            // Current: ~12 uA
            HAL_PWREx_EnterSTOP0Mode(PWR_STOPENTRY_WFI);
            break;

        case LP_STOP1:
            // Lower power STOP mode
            // Current: ~5 uA
            HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
            break;

        case LP_STOP2:
            // Lowest power STOP mode
            // Current: ~1.5 uA
            HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
            break;

        case LP_STANDBY:
            // Only RTC and a few registers retained
            // Current: ~280 nA
            HAL_PWR_EnterSTANDBYMode();
            break;

        case LP_SHUTDOWN:
            // Lowest power, requires external wake-up
            // Current: ~30 nA
            HAL_PWREx_EnterSHUTDOWNMode();
            break;
    }
}

2. Clock Management

Dynamically adjust clock frequency:

// Reduce clock frequency to save power
void reduce_clock_speed(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    // Switch to MSI 4MHz (low-power clock source)
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
    RCC_OscInitStruct.MSIState = RCC_MSI_ON;
    RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;  // 4 MHz
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    // Configure system clock
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);

    // Disable unnecessary high-speed clocks
    __HAL_RCC_PLL_DISABLE();
    __HAL_RCC_HSE_CONFIG(RCC_HSE_OFF);
}

3. Peripheral Power Management

Enable peripherals only when needed:

// Sensor measurement cycle
void sensor_measure_cycle(void) {
    // 1. Enable sensor power
    HAL_GPIO_WritePin(SENSOR_PWR_GPIO, SENSOR_PWR_PIN, GPIO_PIN_SET);

    // 2. Wait for stabilization (refer to datasheet)
    HAL_Delay(10);

    // 3. Enable ADC
    __HAL_RCC_ADC_CLK_ENABLE();
    HAL_ADC_Init(&hadc1);

    // 4. Read data
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 100);
    uint16_t value = HAL_ADC_GetValue(&hadc1);

    // 5. Disable ADC
    HAL_ADC_DeInit(&hadc1);
    __HAL_RCC_ADC_CLK_DISABLE();

    // 6. Disable sensor power
    HAL_GPIO_WritePin(SENSOR_PWR_GPIO, SENSOR_PWR_PIN, GPIO_PIN_RESET);
}

Practical Example: 3-Year Battery Life Design

Design target: CR2450 battery (620mAh), operating for 3+ years.

Power Budget

Target lifespan: 3 years = 26,280 hours
Battery capacity: 620 mAh (considering 80% usable) = 496 mAh
Maximum average current: 496 / 26,280 = 18.9 uA

Duty Cycle Design

State Current Duration Cycle
Deep Sleep 1 uA 59 seconds Every minute
Sensor Measurement 500 uA 100 ms Every minute
BLE Advertising 8 mA 3 ms Every 10 minutes

Average Current Calculation

Sensor: (500 uA x 0.1s) / 60s = 0.83 uA
BLE: (8 mA x 0.003s) / 600s = 0.04 uA
Sleep: 1 uA x (59.9/60) = 0.998 uA
Total: 0.83 + 0.04 + 1.0 ≈ 1.87 uA

Estimated lifespan: 496 mAh / 0.00187 mA = 265,240 hours = 30 years
(Actual lifespan will be reduced to 3-5 years due to battery self-discharge)

Field Experience: Measured Power on an nRF52840 + Zephyr Wearable

The numbers above mostly come from datasheets and back-of-the-envelope math, but what you actually measure on real hardware is often a fair bit off from the spreadsheet. Here are the average currents we measured with a power analyzer on a wearable multi-sensor project built around the nRF52840 + Zephyr RTOS (Nordic nRF Connect SDK), running off a Li-ion cell at 4.2V, with an active BLE connection and a 247-byte MTU:

Subsystems enabled Measured avg current @4.2V
BLE connection only 1.2 ~ 1.4 mA
BLE + analog front-end (ECG) 2.0 ~ 2.4 mA
BLE + analog front-end + IMU 2.5 ~ 3.0 mA
Full feature set (incl. flash writes) 3.2 ~ 3.6 mA

A few hard-won lessons:

  1. Bring up one subsystem at a time, not everything at once. We enabled BLE, the analog front-end, the IMU, and flash writes one by one, measuring after each addition. That's the only way to learn what each subsystem truly costs.
  2. Confirm the DC/DC converter is actually on. The nRF52840 has an integrated DC/DC, but it isn't necessarily enabled by default. Once we turned it on with CONFIG_BOARD_ENABLE_DCDC=y (or the corresponding regulator devicetree node), the current during radio activity dropped noticeably.
  3. BLE power boils down to "connection interval x how much work you do per wake-up." Bumping the MTU to 247 lets you push the same payload in fewer connection events, which shortens the total time the radio stays awake — more effective than simply stretching the connection interval. Going to TX +8dBm raises peak current, but if it buys you a more stable link with fewer retransmissions, the average current can actually go down. You have to measure and weigh it.
  4. Strip out every debug path in the shipping build. Disable CONFIG_RTT_CONSOLE, CONFIG_LOG_BACKEND_RTT, CONFIG_SHELL, and CONFIG_UART_CONSOLE, and enable CONFIG_NRF_APPROTECT_LOCK. Turning off those debug backends alone dropped our standby current by an order of magnitude.

Lesson learned: low power isn't something you finish at design time — it's an iterative process of measuring and trimming, item by item, with a power analyzer in hand. The theory points you in the right direction; only real measurements tell you the truth.

Common Troubleshooting

Abnormally High Power Consumption?

  1. Check floating pins: Set unused GPIOs to input with pull-down
  2. Check debug interface: SWD may continuously draw power
  3. Check LEDs: Ensure status LEDs are turned off
  4. Check LDO quiescent current: Select ultra-low quiescent current LDOs

Cannot Wake Up After Sleep?

  1. Verify RTC clock source configuration is correct
  2. Confirm wake-up source (GPIO, RTC, UART) settings
  3. Check interrupt priorities

Low-power design is a discipline that requires coordinated hardware and software efforts. If you have a battery-powered product requirement, feel free to contact us to discuss.

$ tail -n 1 /var/log/bashcat/posts

More from the workshop.