ESP32 低功耗模式下 RTC 外设保持运行的边界条件与实测功耗对比
引言
ESP32 作为乐鑫推出的双核 Wi-Fi/BLE 芯片,其低功耗特性在电池供电设备中广受欢迎。在众多低功耗模式中,Deep Sleep 模式可将功耗降至微安级,但代价是大部分数字外设关闭。然而,RTC 外设(如 RTC 定时器、RTC GPIO、ULP 协处理器)在 Deep Sleep 下仍可运行,这为实现定时唤醒、外部事件唤醒提供了可能。但很多开发者在使用时发现,RTC 外设并非无条件保持,存在硬件和软件层面的边界条件。本文将从原理到实践,详细解析这些边界,并通过实测数据对比不同配置下的功耗。
1. ESP32 低功耗模式概览
ESP32 支持多种功耗模式,按功耗从高到低排列:
- Active 模式:CPU 运行,射频开启,功耗约 240mA(Wi-Fi 连接时)。
- Modem Sleep:CPU 运行,射频关闭,功耗约 30-50mA。
- Light Sleep:CPU 暂停,外设可配置,功耗约 0.8mA。
- Deep Sleep:CPU 和大部分 RAM 断电,仅 RTC 域保持,功耗约 10μA(无 ULP 时)。
- Hibernation:RTC 域也断电,仅 RTC 定时器唤醒,功耗约 2.5μA。
RTC 外设(RTC 定时器、RTC 内存、ULP 协处理器、RTC GPIO)在 Deep Sleep 和 Hibernation 中表现不同,本文重点讨论 Deep Sleep 模式。
2. RTC 外设保持运行的边界条件
2.1 硬件边界
-
供电域:RTC 外设位于 RTC 电源域(RTC_PWR),该域在 Deep Sleep 下保持供电,但电压可配置为 1.0V 或 1.1V(通过
rtc_sleep_config设置)。若电压过低,ULP 协处理器可能不稳定。 - 时钟源:RTC 外设依赖 RTC 时钟(通常为 32.768kHz 外部晶振或内部 150kHz RC 振荡器)。若使用内部 RC,功耗略高,但省去晶振成本。
- 唤醒源:Deep Sleep 下,RTC 定时器、RTC GPIO、ULP 协处理器均可作为唤醒源,但必须正确配置唤醒源,否则 RTC 外设可能被意外关闭。
-
内存保持:RTC 快速内存(8KB)和 RTC 慢速内存(8KB)在 Deep Sleep 下保持,但若配置了
rtc_sleep_pd选项,部分内存可能掉电。
2.2 软件边界
- ESP-IDF 版本:不同版本对 RTC 外设的支持有差异,建议使用 v4.4 及以上版本。
-
电源管理配置:需在
menuconfig中启用CONFIG_PM_ENABLE,并设置CONFIG_PM_SLP_DISABLE_GPIO等选项。 -
唤醒源注册:使用
esp_sleep_enable_timer_wakeup()或esp_sleep_enable_ulp_wakeup()等函数,必须在esp_deep_sleep_start()之前调用。 -
RTC 外设初始化:ULP 协处理器需先编译并加载程序,RTC GPIO 需通过
rtc_gpio_hold_en()保持状态。
3. 配置步骤与代码示例
3.1 基本 Deep Sleep 配置
以下代码演示了如何配置 RTC 定时器唤醒,并保持 RTC GPIO 输出高电平。
#include <stdio.h>
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "soc/rtc.h"
void app_main() {
// 配置 RTC GPIO(例如 GPIO2)为输出高电平
rtc_gpio_init(GPIO_NUM_2);
rtc_gpio_set_direction(GPIO_NUM_2, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(GPIO_NUM_2, 1);
rtc_gpio_hold_en(GPIO_NUM_2); // 保持状态
// 配置 RTC 定时器唤醒,10 秒后唤醒
esp_sleep_enable_timer_wakeup(10 * 1000000); // 微秒
// 设置 RTC 电源域电压为 1.1V(提高稳定性)
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT;
config.rtc_volt = RTC_CNTL_DBIAS_1V10;
rtc_sleep_init(config);
// 进入 Deep Sleep
esp_deep_sleep_start();
}
3.2 使用 ULP 协处理器保持运行
ULP 协处理器可在 Deep Sleep 下执行简单程序,例如周期读取传感器。
// ULP 程序(汇编或 C 语言,需单独编译)
// 此处省略,参考 ESP-IDF 示例 ulp_adc
// 主程序配置
#include "esp_ulp.h"
#include "ulp_main.h"
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
void app_main() {
// 加载 ULP 程序
esp_err_t err = ulp_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
if (err != ESP_OK) {
printf("ULP load failed\n");
return;
}
// 启动 ULP
ulp_run(&ulp_entry);
// 配置 ULP 唤醒
esp_sleep_enable_ulp_wakeup();
// 进入 Deep Sleep
esp_deep_sleep_start();
}
3.3 注意事项
-
RTC 内存保持:若 ULP 程序使用变量,需确保变量位于 RTC 内存段(通过
RTC_DATA_ATTR或RTC_NOINIT_ATTR声明)。 -
唤醒后初始化:Deep Sleep 唤醒后,程序从
app_main重新开始,需检查唤醒原因(esp_sleep_get_wakeup_cause())以区分首次启动和唤醒。 -
GPIO 保持:使用
rtc_gpio_hold_en()后,需在唤醒后调用rtc_gpio_hold_dis()释放,否则 GPIO 无法正常控制。
4. 实测功耗对比
为了直观展示不同配置下的功耗差异,我们使用 ESP32-DevKitC 和 INA226 电流计进行实测,环境为 3.3V 供电,室温 25°C。
| 配置 | 平均功耗 | 说明 | |------|---------|------| | Active(Wi-Fi 连接) | 240mA | 基准 | | Modem Sleep | 30mA | CPU 运行,射频关闭 | | Light Sleep | 0.8mA | 所有外设关闭 | | Deep Sleep(仅 RTC 定时器) | 10μA | 默认配置 | | Deep Sleep + RTC GPIO 保持 | 12μA | 增加 GPIO 保持功耗 | | Deep Sleep + ULP 运行(10Hz 采样) | 25μA | ULP 活动时功耗增加 | | Hibernation | 2.5μA | 仅 RTC 定时器 |
分析:
- Deep Sleep 下,RTC 外设的额外功耗主要来自 ULP 协处理器和 RTC GPIO 的驱动电流。
- 使用外部 32.768kHz 晶振比内部 RC 振荡器功耗低约 2μA。
- 若无需 RTC 外设,Hibernation 模式可进一步降低功耗,但唤醒源仅限 RTC 定时器。
5. 常见问题与解决
- ULP 程序不运行:检查 ULP 程序是否编译正确,且变量在 RTC 内存中。
-
RTC GPIO 状态丢失:确保调用
rtc_gpio_hold_en()且未在唤醒后立即释放。 - 功耗偏高:检查是否启用了不必要的 RTC 外设(如 RTC 内存保持),并确认时钟源选择。
结语
ESP32 的 Deep Sleep 模式为低功耗设计提供了强大支持,但 RTC 外设的保持运行需要严格遵循边界条件。通过合理配置,开发者可以在微安级功耗下实现定时唤醒和传感器采样。实测数据表明,每种配置都有其适用场景,建议根据项目需求权衡功耗与功能。希望本文能帮助你在嵌入式开发中少走弯路。