ESP32 低功耗模式下 RTC 外设保持工作的边界条件与实测电流分析

在物联网边缘节点、可穿戴设备等电池供电场景中,ESP32 的低功耗模式(尤其是 Deep Sleep)是延长续航的关键。然而,许多开发者误以为 Deep Sleep 下所有外设都停止工作,实际上 RTC 域内的部分外设(如 RTC 定时器、RTC GPIO、ULP 协处理器)仍可运行,但存在严格的边界条件。本文将从硬件架构出发,解析这些条件,并通过实测数据展示不同配置下的电流差异。

1. ESP32 电源域与低功耗模式概述

ESP32 的电源域分为 VDD_SDIOVDD3P3_RTCVDD3P3_CPU 等,其中 RTC 域(RTC Power Domain)在 Deep Sleep 下保持供电,而 CPU 域和数字外设域则断电。低功耗模式主要分为:

  • Active:正常工作,电流约 80-240 mA(取决于射频活动)。
  • Modem Sleep:CPU 运行,Wi-Fi/蓝牙关闭,电流约 3-8 mA。
  • Light Sleep:CPU 暂停,数字外设时钟门控,RTC 域保持,电流约 0.8-1.2 mA。
  • Deep Sleep:CPU 和大部分数字外设断电,仅 RTC 域供电,电流可低至 10-150 µA(取决于 RTC 外设配置)。

关键点:在 Deep Sleep 下,RTC 外设(如 RTC 定时器、RTC GPIO、ULP 协处理器)是否工作,取决于它们是否被显式使能,以及时钟源是否选择为外部低速晶振(32.768 kHz)或内部 RC 振荡器(150 kHz)。

2. RTC 外设保持工作的边界条件

2.1 电源域与时钟源

RTC 域包含以下模块:

  • RTC 定时器(用于定时唤醒)
  • RTC GPIO(用于外部唤醒)
  • ULP 协处理器(可执行简单程序)
  • RTC 内存(8 KB 快速 + 8 KB 慢速)

这些模块的供电由 RTC_CNTL 控制,但它们的时钟源必须明确配置。ESP32 提供两种 RTC 时钟源:

  • 外部 32.768 kHz 晶振(精度高,功耗略高)
  • 内部 150 kHz RC 振荡器(精度低,功耗更低)

边界条件 1:若使用外部晶振,必须在进入 Deep Sleep 前通过 rtc_clk_32k_enable(true) 使能,否则 RTC 定时器无法工作。

边界条件 2:ULP 协处理器只能在 Deep Sleep 下运行,且其程序必须存储在 RTC 慢速内存中,并设置 ulp_set_wakeup_period 等参数。若未正确配置,ULP 将无法启动。

2.2 唤醒源配置

RTC 外设保持工作的目的是为了唤醒主 CPU。ESP32 支持多种唤醒源,但并非所有唤醒源在 Deep Sleep 下都可用:

  • RTC 定时器唤醒:可用,需设置 esp_sleep_enable_timer_wakeup(time_in_us)
  • RTC GPIO 唤醒:可用,但仅支持 EXT0(单引脚)和 EXT1(多引脚)模式,且引脚必须配置为输入模式。
  • 触摸传感器唤醒:可用,但需要 RTC 域支持。
  • ULP 协处理器唤醒:可用,但 ULP 程序需正确编写。

边界条件 3:若使用 GPIO 唤醒,必须将引脚配置为 RTC_GPIO 模式,且不能使用普通 GPIO 中断。

2.3 软件配置流程

以下是一个典型的 Deep Sleep + RTC 定时器 + GPIO 唤醒的配置示例:

#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "soc/rtc.h"

void app_main() {
    // 1. 配置 RTC 时钟源为外部晶振(可选)
    rtc_clk_32k_enable(true);

    // 2. 配置 GPIO 唤醒(EXT1 模式,GPIO 4 和 GPIO 5)
    const gpio_num_t wakeup_pins[] = {GPIO_NUM_4, GPIO_NUM_5};
    const gpio_pullup_t pullup[] = {GPIO_PULLUP_ENABLE, GPIO_PULLUP_ENABLE};
    esp_sleep_enable_ext1_wakeup((1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5), ESP_EXT1_WAKEUP_ANY_HIGH);

    // 3. 配置 RTC 定时器唤醒(10 秒)
    esp_sleep_enable_timer_wakeup(10 * 1000000);

    // 4. 配置 ULP 协处理器(可选)
    // 此处省略 ULP 程序加载,假设已加载到 RTC 慢速内存
    // ulp_run(&ulp_entry);

    // 5. 进入 Deep Sleep
    esp_deep_sleep_start();
}

注意:若使用 ULP,需在编译时包含 ulp 组件,并确保 ULP 程序地址正确。

3. 实测电流分析

为了量化不同配置下的功耗,我们使用 ESP32-DevKitC V4 开发板,供电电压 3.3V,通过高精度万用表(精度 0.1 µA)测量进入 Deep Sleep 后的稳定电流。测试环境为室温 25°C,关闭所有外设(如 LED、串口)。

3.1 测试场景与结果

| 场景 | 配置说明 | 实测电流(µA) | |------|----------|----------------| | 场景1 | 纯 Deep Sleep,无 RTC 外设,内部 RC 时钟 | 12.5 | | 场景2 | Deep Sleep + RTC 定时器(外部晶振) | 18.2 | | 场景3 | Deep Sleep + RTC 定时器(内部 RC) | 14.8 | | 场景4 | Deep Sleep + RTC GPIO 唤醒(EXT1,2 个引脚) | 20.1 | | 场景5 | Deep Sleep + ULP 协处理器(每 1 秒采样一次) | 25.6 | | 场景6 | Light Sleep(CPU 暂停,Wi-Fi 关闭) | 850 |

3.2 结果分析

  • 场景1 vs 场景2:启用外部晶振使 RTC 定时器工作,电流增加约 5.7 µA,这是因为外部晶振需要驱动电路。若对时间精度要求不高,使用内部 RC 可节省约 3.4 µA。
  • 场景4:GPIO 唤醒使能后,RTC IO 的输入缓冲器保持供电,导致电流增加约 7.6 µA。若使用 EXT0(单引脚),电流可降低至约 16 µA。
  • 场景5:ULP 协处理器运行导致电流显著增加,但相比唤醒 CPU 后执行任务(数十 mA),仍非常节能。ULP 的功耗与采样频率和指令复杂度直接相关。
  • 场景6:Light Sleep 电流远高于 Deep Sleep,因为数字外设域仍部分供电,适合需要快速唤醒的场景(唤醒时间 < 1 ms)。

3.3 优化建议

  • 选择时钟源:若 RTC 定时器仅用于周期性唤醒,且对时间精度要求不高(如传感器采样),优先使用内部 RC 振荡器。
  • 减少唤醒源:只使能必要的唤醒源,避免多余 GPIO 输入缓冲器耗电。
  • ULP 程序优化:ULP 程序尽量使用低功耗指令(如 halt),并延长采样周期。
  • 使用 Light Sleep 替代:若需要频繁唤醒(如每 100 ms),Light Sleep 可能更合适,因为 Deep Sleep 的唤醒时间(约 1-3 ms)会带来额外功耗。

4. 注意事项

  • 电源稳定性:Deep Sleep 下电流极低,若电源设计不佳,可能因纹波导致 RTC 外设异常。建议在 VDD3P3_RTC 引脚附近放置 1 µF 和 0.1 µF 去耦电容。
  • GPIO 浮空:未使用的 RTC GPIO 必须配置为下拉或上拉,否则漏电流会显著增加(实测可达 50 µA)。
  • 唤醒后初始化:从 Deep Sleep 唤醒后,系统会从 app_main 重新启动,需在代码中判断唤醒原因(esp_sleep_get_wakeup_cause()),以执行不同初始化流程。
  • ULP 内存限制:ULP 程序只能使用 RTC 慢速内存(8 KB),且数据区需谨慎分配,避免溢出。
  • 温度影响:内部 RC 振荡器受温度影响较大(精度约 ±5%),若产品工作温度范围宽,建议使用外部晶振。

5. 总结

ESP32 在低功耗模式下保持 RTC 外设工作并非“免费午餐”,需要开发者深入理解电源域、时钟源和唤醒源的边界条件。通过合理配置,可以在功耗和功能之间取得平衡。实测数据表明,纯 Deep Sleep 可低至 12.5 µA,而启用 RTC 定时器和 GPIO 唤醒后约 20 µA,仍适合大多数电池供电场景。建议开发者根据实际需求,选择最简配置,并利用 ULP 协处理器处理简单任务,以进一步降低系统功耗。