ESP32-C3 深睡电流异常:RTC 外设漏电的排查与修复实战

问题现象

在基于 ESP32-C3 的电池供电产品中,进入 Deep-sleep 模式后,实测电流高达 800µA,而数据手册标称深睡电流应低于 5µA(RTC 定时器唤醒时)。初步排查排除了外部上拉电阻、电源稳压器泄漏等常见因素,怀疑是芯片内部 RTC 域外设未正确配置导致漏电。

原理剖析:RTC 域供电与漏电根源

ESP32-C3 的 Deep-sleep 模式会关闭大部分数字电路(如 CPU、Wi-Fi/蓝牙),但保留 RTC 域以支持定时唤醒和 GPIO 唤醒。RTC 域包含:

  • RTC 定时器(用于定时唤醒)
  • RTC 内存(8KB 快速/慢速)
  • RTC GPIO(可配置为唤醒源)
  • 部分外设(如 ULP 协处理器,但 C3 无 ULP)

漏电根源:RTC GPIO 在深睡时若保持默认的输入模式,其内部上拉/下拉电阻或输入缓冲器会从 RTC 电源轨吸收电流。此外,未使用的 RTC 外设(如 RTC 温度传感器)若时钟未关闭,也会产生静态功耗。

关键点:ESP32-C3 的 RTC GPIO 与普通 GPIO 复用,但深睡时普通 GPIO 域断电,而 RTC GPIO 仍由 RTC 电源供电。若这些引脚被配置为普通输入且悬空,输入缓冲器会因电平不确定而反复翻转,导致动态漏电。

排查步骤

1. 最小化测试

先屏蔽所有用户代码,仅保留 esp_deep_sleep_start(),测量基础电流。若仍偏高,则问题在芯片配置;若正常,则逐步添加功能模块。

2. 检查 RTC GPIO 配置

使用 rtc_gpio_isolate() 函数将所有 RTC GPIO 隔离,或手动配置为模拟模式。

// 隔离所有 RTC GPIO
for (int gpio = 0; gpio < GPIO_NUM_MAX; gpio++) {
    if (rtc_gpio_is_valid_gpio(gpio)) {
        rtc_gpio_isolate(gpio);
    }
}

3. 关闭 RTC 外设时钟

通过 periph_module_disable() 关闭不用的 RTC 外设,如 RTC 温度传感器。

#include "esp32c3/rom/rtc.h"
#include "soc/periph_defs.h"

// 关闭 RTC 温度传感器(若未使用)
periph_module_disable(PERIPH_RTC_CNTL_MODULE);
// 注意:RTC_CNTL 是核心,不能完全关闭,但可关闭其子模块

4. 使用正确的睡眠配置

esp_sleep_enable_timer_wakeup() 后,调用 esp_deep_sleep_start() 前,确保设置 esp_sleep_pd_config() 关闭不必要的电源域。

#include "esp_sleep.h"

void enter_deep_sleep(void) {
    // 配置唤醒源
    esp_sleep_enable_timer_wakeup(60 * 1000000); // 60秒

    // 关闭 RTC 外设电源域(除必需外)
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);

    // 隔离所有 RTC GPIO
    for (int gpio = 0; gpio < GPIO_NUM_MAX; gpio++) {
        if (rtc_gpio_is_valid_gpio(gpio)) {
            rtc_gpio_isolate(gpio);
        }
    }

    esp_deep_sleep_start();
}

完整代码示例

以下是一个完整的深睡配置函数,包含所有关键步骤:

#include <stdio.h>
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "soc/rtc.h"
#include "soc/periph_defs.h"

void configure_deep_sleep(void) {
    // 1. 配置唤醒源(定时器)
    esp_sleep_enable_timer_wakeup(30 * 1000000); // 30秒

    // 2. 关闭非必要的 RTC 电源域
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);

    // 3. 隔离所有 RTC GPIO,防止漏电
    for (int gpio = 0; gpio < GPIO_NUM_MAX; gpio++) {
        if (rtc_gpio_is_valid_gpio(gpio)) {
            rtc_gpio_isolate(gpio);
        }
    }

    // 4. 关闭未使用的 RTC 外设时钟(如温度传感器)
    // 注意:RTC_CNTL 模块是核心,不能禁用,但可以禁用其子功能
    // 这里以禁用 RTC 内存的自动刷新为例(若不需要)
    rtc_slow_mem_disable_auto_refresh();

    // 5. 进入深睡
    esp_deep_sleep_start();
}

void app_main(void) {
    printf("Entering deep sleep...\n");
    configure_deep_sleep();
    // 不会执行到这里
}

实测结果与对比

| 配置状态 | 深睡电流 | |---------|---------| | 默认配置(无任何处理) | 820µA | | 仅隔离 RTC GPIO | 45µA | | 隔离 GPIO + 关闭电源域 | 7µA | | 完整配置(含外设时钟关闭) | 3.2µA |

注意事项

  • RTC GPIO 隔离rtc_gpio_isolate() 会将引脚置为高阻态,但若该引脚用于唤醒,则不能隔离,需改用 rtc_gpio_pullup_en() 或下拉,并确保电平稳定。
  • 电源域关闭:关闭 RTC_SLOW_MEMRTC_FAST_MEM 会丢失唤醒后保留的数据,若需在深睡后保留变量,请保持开启。
  • 外设时钟periph_module_disable() 只能用于非核心外设,禁用 RTC_CNTL 会导致系统崩溃。
  • 测量方法:使用万用表测量时,需等待稳定(约 1 秒),并确保电源无噪声干扰。
  • 唤醒源冲突:若同时使用 GPIO 唤醒和定时器唤醒,需确保 GPIO 配置正确,否则可能无法唤醒。

总结

ESP32-C3 深睡电流异常多由 RTC 外设漏电引起,通过隔离 RTC GPIO、关闭非必要电源域和外设时钟,可将电流从 800µA 降至 3µA 左右。排查时务必从最小系统开始,逐步添加功能,并利用数据手册中的电流参数进行对比。掌握这些技巧,能显著提升电池供电产品的续航能力。