低功耗设计实战:嵌入式设备待机电流优化指南

做电池供电的 IoT 设备,最头疼的不是代码怎么写,而是电池能撑多久。

我用 ESP32 做过一个土壤湿度监测节点,一开始待机 3 天就没电了。后来花了一周时间抠功耗,待机时间拉长到了 6 个月。今天就把这中间踩过的坑和学到的技巧,一次性讲清楚。

为什么待机电流这么重要

先算一笔账。假设你用一块 2000mAh 的锂电池:

  • 待机 10mA → 200 小时,约 8 天
  • 待机 1mA → 2000 小时,约 83 天
  • 待机 100μA → 20000 小时,约 2.3 年

差了 250 倍。

大多数 IoT 设备 99% 的时间都在待机,只有偶尔采集数据、发送报文时才醒过来。所以待机功耗直接决定了电池寿命。

硬件层面的优化

1. 选对芯片

不是所有 MCU 都适合电池供电。看看主流芯片的典型待机电流:

芯片 Deep Sleep 电流 唤醒时间
ESP32-C3 ~7μA (Hibernation) ~20ms
STM32L053 ~0.6μA (Stop 模式) ~5μs
nRF52840 ~1μA (System OFF) ~6μs
ATmega328P ~0.1μA (Power Down) ~65ms

如果续航是第一优先级,STM32L 系列和 nRF 系列是更好的选择。ESP32 的优势在于集成了 Wi-Fi/蓝牙,但代价是功耗相对较高。

2. 去掉不必要的负载

很多开发板上有你根本不需要的东西在持续耗电:

  • 状态 LED:每颗 2-5mA,关掉能省不少
  • USB 转串口芯片:CH340 静态电流约 5-10mA
  • LDO 稳压器:老旧 LDO 的静态电流可能高达 1-5mA

实战技巧: 用低静态电流的 LDO(如 MCP1702,Iq 仅 2μA)替换板载稳压器,或者直接用锂电池供电(3.0-4.2V 范围,很多 MCU 支持)。

3. 浮空引脚处理

未使用的 GPIO 如果处于浮空状态,可能导致内部电路反复翻转,增加 μA 级别的漏电。

做法: 所有未使用的引脚配置为输出低电平,或者内部下拉。

软件层面的优化

ESP32 的四种低功耗模式

ESP32 提供了丰富的功耗模式,从低到高:

模式 典型电流 保留内容 唤醒方式
Active (Wi-Fi) 80-240mA 全部
Modem Sleep ~20mA Wi-Fi 连接 自动
Light Sleep ~0.8mA RTC 内存 GPIO/定时器
Deep Sleep ~10μA RTC 内存 定时器/GPIO/触摸
Hibernation ~7μA 定时器

Light Sleep 实战

Light Sleep 适合需要频繁唤醒的场景,比如每秒采样一次传感器:

#include "esp_sleep.h"
#include "esp_pm.h"

void setup() {
    // 配置 Light Sleep 自动触发
    // 当 CPU 空闲时自动进入
    esp_pm_config_esp32_t pm_config = {
        .max_freq_mhz = 80,
        .min_freq_mhz = 80,
        .light_sleep_enable = true
    };
    esp_pm_configure(&pm_config);
}

void loop() {
    // 读取传感器
    float temp = read_sensor();

    // 发送数据
    send_data(temp);

    // 等待 1 秒后再次采集
    // CPU 空闲期间会自动进入 Light Sleep
    vTaskDelay(pdMS_TO_TICKS(1000));
}

Deep Sleep 实战

Deep Sleep 适合间歇性采集的场景,比如每 30 分钟发送一次数据:

#include "esp_sleep.h"

#define uS_TO_S 1000000ULL
#define SLEEP_TIME 1800  // 30 分钟

void setup() {
    // 配置定时器唤醒
    esp_sleep_enable_timer_wakeup(SLEEP_TIME * uS_TO_S);

    // 可选:GPIO 唤醒(比如按下按钮唤醒)
    // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1);

    // 可选:保存数据到 RTC Fast Memory
    RTC_DATA_ATTR uint32_t boot_count = 0;
    boot_count++;

    // 进入 Deep Sleep
    esp_deep_sleep_start();
}

void loop() {
    // 永远不会执行到这里
    // Deep Sleep 唤醒后从 setup() 重新开始
}

STM32 的 Stop 模式

STM32L 系列在 Stop 模式下电流可以低至 0.6μA:

#include "stm32l0xx_hal.h"

void enter_stop_mode(void) {
    // 关闭不需要的时钟
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    // ... 关闭所有不用的 GPIO 时钟

    // 进入 Stop 模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, 
                          PWR_STOPENTRY_WFI);

    // 唤醒后重新配置时钟
    SystemClock_Config();
}

功耗测量方法

没有测量就没有优化。你需要一个准确的方法来测量待机电流。

方法一:万用表(入门)

把万用表串入供电回路,直接读数。简单但精度有限,万用表的采样率太低,看不到电流尖峰。

方法二:示波器 + 采样电阻(推荐)

在供电回路串联一个 10Ω 采样电阻,用示波器测量电阻两端电压:

I = V / R = V(采样电阻) / 10Ω

示波器的采样率高,可以看到唤醒瞬间的电流尖峰——这些尖峰往往占总功耗的很大一部分。

方法三:专用功耗分析仪(专业)

像 Nordic Power Profiler Kit II 或者 Joulescope 这种设备,可以精确到 nA 级别,还能实时绘制功耗曲线。预算充足的话强烈建议入手。

常见坑和解决方案

坑一:Wi-Fi 连接后忘记断开

// 错误写法 - Wi-Fi 一直保持连接
WiFi.begin(ssid, password);
send_data();
delay(30000);  // 这 30 秒 Wi-Fi 一直在耗电!

// 正确写法 - 用完就断
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED);
send_data();
WiFi.disconnect(true);  // 断开 Wi-Fi
WiFi.mode(WIFI_OFF);    // 关闭 Wi-Fi 模块
esp_deep_sleep_start();

坑二:传感器待机功耗被忽略

很多传感器即使不工作也有静态电流。比如 DHT22 待机时约 1.5mA,对于整个系统来说这可能是最大的耗电大户。

解决方案: 用 GPIO 控制传感器的供电,需要时才上电:

#define SENSOR_POWER_PIN 4

void setup() {
    pinMode(SENSOR_POWER_PIN, OUTPUT);
    digitalWrite(SENSOR_POWER_PIN, HIGH);  // 给传感器供电
    delay(500);  // 等待传感器稳定

    float temp = read_dht22();

    digitalWrite(SENSOR_POWER_PIN, LOW);   // 关闭传感器供电
}

坑三:RTC GPIO 唤醒配置错误

ESP32 的 Deep Sleep 可以用 GPIO 唤醒,但引脚有限制——只能用 RTC GPIO(GPIO 0/2/4/12-15/25-27/32-39),而且需要配置 ULP 协处理器或者 ext0/ext1 唤醒源。

// ext0 唤醒:只能用一个引脚,电平触发
esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1);  // 高电平唤醒

// ext1 唤醒:可以用多个引脚,支持 OR/AND 逻辑
esp_sleep_enable_ext1_wakeup(
    GPIO_SEL_35 | GPIO_SEL_34,  // 两个引脚
    ESP_EXT1_WAKEUP_ALL_LOW      // 全部低电平触发
);

坑四:ADC 校准导致电流异常

ESP32 的 ADC 在 Deep Sleep 期间如果启用了 ULP 协处理器读取 ADC,需要确保正确配置,否则可能导致唤醒后电流异常。

// 启用 ADC 校准
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, 
                          ADC_WIDTH_BIT_12, 1100, &adc_chars);

优化效果对比

这是我实际优化一个土壤监测节点的过程:

阶段 待机电流 电池寿命
初始状态(开发板 + Wi-Fi 常开) 15mA 5.5 天
去掉 LED + 改用独立供电 8mA 10 天
启用 Light Sleep 1.2mA 69 天
改用 Deep Sleep + 定时采集 15μA 15 个月
去掉板载稳压器 + 直连锂电池 8μA 28 个月

从 5 天到 28 个月,优化了 170 倍。核心思路就是:能关的都关掉,该睡的时候赶紧睡。

总结

低功耗设计不是一蹴而就的,而是一个持续优化的过程。记住几个核心原则:

  1. 选对芯片:电池供电项目优先考虑 STM32L、nRF 系列
  2. 硬件做减法:去掉 LED、换低 Iq 稳压器、处理浮空引脚
  3. 软件该睡就睡:Light Sleep 适合频繁唤醒,Deep Sleep 适合间歇采集
  4. 传感器按需供电:用 GPIO 控制传感器电源
  5. 测量验证:用示波器或功耗分析仪确认优化效果

希望这篇博客文章对您有所帮助!