ESP32 系列芯片提供三种可配置的睡眠模式,针对这些睡眠模式,我们提供了了多种低功耗解决方案,用户可以结合具体需求选择睡眠模式并进行配置。三种睡眠模式如下:
目前 ESP32 的 Modem-sleep 仅工作在 Station 模式下,连接路由器后生效。Station 会周期性在工作状态和睡眠状态两者之间切换。
ESP32 通过 Wi-Fi 的 DTIM Beacon 机制与路由器保持连接。在 Modem-sleep 模式下,系统可以自动被唤醒,无需配置唤醒源。
一般路由器的 DTIM Beacon 间隔为 100 ms ~ 1,000 ms。
DTIM (Delivery Traffic Indication Message): 使用无线路由器时无线发送数据包的频率。
在 Modem-sleep 模式下,ESP32 会在两次 DTIM Beacon 间隔时间内,关闭 Wi-Fi 模块电路,达到省电效果,在下次 Beacon 到来前自动唤醒。睡眠时间由路由器的 DTIM Beacon 时间决定。Modem-sleep 模式可以保持与路由器的 Wi-Fi 连接,并通过路由器接收来自手机或者服务器的交互信息。
通过以下接口配置 Modem-sleep 模式,type
可选参数:
WIFI_PS_NONE
: 不使用 Modem-sleep 模式WIFI_PS_MIN_MODEM
: ESP32 接收 Beacon 的间隔与路由器的 DTIM 间隔相同,即 1 个路由器间隔WIFI_PS_MAX_MODEM
: ESP32 接收 Beacon 的间隔可由程序进行配置,间隔周期 wifi_sta_config_t
结构体中 listen_interval
值决定,单位为 路由器的 Beacon 间隔,默认值为 3(即 3 个路由器 Beacon 间隔)typedef enum {
WIFI_PS_NONE, /**< No power save */
WIFI_PS_MIN_MODEM, /**< Minimum modem power saving. In this mode, station wakes up to receive beacon every DTIM period */
WIFI_PS_MAX_MODEM, /**< Maximum modem power saving. In this mode, interval to receive beacons is determined by the listen_interval parameter in wifi_sta_config_t */
} wifi_ps_type_t;
esp_err_t esp_wifi_set_ps(wifi_ps_type_t type);
type
参数为 WIFI_PS_MAX_MODEM
,ESP32 接收 Beacon 的间隔 listen_interval
配置方法:#define LISTEN_INTERVAL 3
wifi_config_t wifi_config = {
.sta = {
.ssid = "SSID",
.password = "Password",
.listen_interval = LISTEN_INTERVAL,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
Modem-sleep 一般用于 CPU 持续处于工作状态并需要保持 Wi-Fi 连接的应用场景,例如,使用 ESP32 本地语音唤醒功能,CPU 需要持续采集和处理音频数据。
Light-sleep 的工作模式与 Modem-sleep 相似,不同的是,除了关闭 Wi-Fi 模块电路以外,在 Light-sleep 模式下,还会关闭时钟并暂停内部 CPU,比 Modem-sleep 功耗更低。有两种方式使 CPU 进入 Light-sleep 模式:
参考示例: light_sleep
开发者可以使系统强制进入 Light-sleep 模式,即调用强制 Light-sleep 接口,强制关闭 Wi-Fi 模块电路并暂停内部 CPU。强制休眠后,能通过定时器、 GPIO(RTC IO 和 Digital IO)和 UART 唤醒。从 Light-sleep 唤醒后,会从进入休眠的位置继续执行程序。强制进入 Light-sleep 模式后,可以保持与路由器的 Wi-Fi 连接,但不能通过路由器接收来自手机或者服务器的交互信息。
注意:
- 强制 Light-sleep 接口调用后,并不会立即休眠,而是等到系统空闲后才进入休眠,
- 强制进入 Light-sleep 模式后,不能接收 IP 网络发送的网络数据。
esp_light_sleep_start()
函数进入 Light-sleep 模式。在没有配置唤醒源的情况下也可以进入轻度睡眠状态,在这种情况下,芯片将无限期地处于 Light-sleep 模式,直到外部复位。/* Enter sleep mode */
esp_light_sleep_start();
/* Execution continues here after wakeup */
强制 Light-sleep 模式可用于需要保持与路由器的连接,不需要实时响应路由器发来的数据的场景。
参考示例: power_save
开发者可配置系统在空闲时自动进入 Light-sleep 模式,并能在需要 CPU 工作时自动唤醒,无需配置唤醒源。在配置为自动 Light-sleep 后,可以保持与路由器的 Wi-Fi 连接,并通过路由器接收来自手机或者服务器的交互信息,对用户体验没有影响。
通常自动 Light-sleep 会与 Modem-sleep 模式 以及电源管理功能共同使用,电源管理功能允许系统根据 CPU 负载动态调节 CPU 频率以降低功耗。
若系统应用中有小于 DTIM Beacon 间隔时间的循环定时,系统将不能进入 Light-sleep 模式。
esp_err_t esp_pm_configure(const void* vconfig)
接口配置电源管理功能,参数 light_sleep_enable
为 true 时使能自动休眠功能。使能 自动 Light-sleep 功能,需要使能 CONFIG_FREERTOS_USE_TICKLESS_IDLE 和 CONFIG_PM_ENABLE
#if CONFIG_PM_ENABLE
// Configure dynamic frequency scaling:
// maximum and minimum frequencies are set in sdkconfig,
// automatic light sleep is enabled if tickless idle support is enabled.
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
.light_sleep_enable = true
#endif
};
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
#endif // CONFIG_PM_ENABLE
自动 Light-sleep 模式可用于需要保持与路由器的连接,可以实时响应路由器发来的数据的场景。并且在未接收到命令时,CPU 可以处于空闲状态。比如 Wi-Fi 开关的应用,大部分时间 CPU 都是空闲的,直到收到控制命令,CPU 才需要进行 GPIO 的操作。
参考示例: deep_sleep
相对于其他两种模式,系统无法自动进入 Deep-sleep,需要由用户调用接口函数 esp_deep_sleep_start()
进入 Deep-sleep 模式。在该模式下,芯片会断开所有 Wi-Fi 连接与数据连接,进入 Deep-sleep 模式,只有 RTC 存储器和 RTC 外设以及 ULP 协处理器可以工作。从 Deep-sleep 唤醒后,CPU 将软件复位重启。
esp_deep_sleep_start()
函数进入 Deep-sleep 模式。在没有配置唤醒源的情况下也可以进入 Deep-sleep 状态,在这种情况下,芯片将无限期地处于 Deep-sleep 模式,直到外部复位。/* Enter deep sleep */
esp_deep_sleep_start();
Deep-sleep 可以用于低功耗的传感器应用,或者大部分时间都不需要进行数据传输的情况。设备可以每隔一段时间从 Deep-sleep 状态醒来测量数据并上传,之后继续进入 Deep-sleep。也可以将多个数据存储于 RTC memory(RTC memory 在 Deep-sleep 模式下仍然可以保存数据),然后一次发送出去。
针对 强制 Lighe-sleep 和 Deep-sleep 需要配置唤醒源的情况,有以下唤醒方式可以选择:
部分唤醒方式仅支持 Light-sleep
External wakeup (ext0)
RTC IO 模块包含当其中 一个 RTC IO 的电平为唤醒电平(可配置为逻辑高或低)触发唤醒的逻辑。
RTC IO 是 RTC 外设电源域的一部分,因此如果使能该唤醒源,RTC 外设将在睡眠期间保持上电状态。在 ESP32 的修订版 0 和 1 中,此唤醒源与 ULP 和触摸唤醒源不兼容。
由于在此模式下启用了 RTC IO 模块,因此也可以使用内部上拉或下拉电阻。
esp_sleep_enable_ext0_wakeup()
函数可用于启用此唤醒源。
External wakeup (ext1)
RTC 控制器包含使用 多个 RTC IO 触发唤醒的逻辑。两个逻辑功能之一可用于触发唤醒:
ESP_EXT1_WAKEUP_ANY_HIGH
)ESP_EXT1_WAKEUP_ALL_LOW
)该唤醒源由 RTC 控制器实现。因此,RTC 外设和 RTC 存储器可以在此模式下断电。
但是,如果 RTC 外设断电,内部上拉和下拉电阻将被禁用。要使用内部上拉或下拉电阻,请在睡眠期间请求 RTC 外设电源域保持上电。
esp_sleep_enable_ext1_wakeup()
函数可用于启用此唤醒源。
Touch pad wakeup
RTC IO 模块包含触摸传感器中断时触发唤醒的逻辑。您需要在芯片开始睡眠之前配置触摸传感器中断。
当 RTC 外设未被强制上电时,ESP32 的修订版 0 和 1 仅支持此唤醒模式。
esp_sleep_enable_touchpad_wakeup()
函数可用于启用此唤醒源。
GPIO wakeup (light sleep only)
除了上面描述的 EXT0 和 EXT1 唤醒源之外,在 Light-sleep 模式下还有一种从外部输入唤醒的方法。通过该唤醒源,每个引脚可以使用 gpio_wakeup_enable()
函数单独配置为高电平或低电平唤醒。与 EXT0 和 EXT1 唤醒源(只能与 RTC IO 一起使用)不同,此唤醒源可用于任何 IO(RTC 或数字)。
esp_sleep_enable_gpio_wakeup()
函数可用于启用此唤醒源。
参考示例: system/ulp
ULP 协处理器可以在芯片处于睡眠模式时运行,并且可以用于轮询传感器,监视 ADC 或触摸传感器值,并在检测到特定事件时唤醒芯片。ULP 协处理器是 RTC 外设电源域的一部分,它运行存储在 RTC 慢速存储器中的程序。如果请求此唤醒模式,RTC 慢速存储器将在睡眠期间启动。在 ULP 协处理器开始运行程序之前,RTC 外设将自动上电; 程序停止运行后,RTC 外设将再次自动关闭。
当 RTC 外设未被强制上电时,ESP32 的修订版 0 和 1 仅支持此唤醒模式。
esp_sleep_enable_ulp_wakeup()
函数可用于启用此唤醒源。
RTC 控制器具有内置定时器,可用于在预定义的时间后唤醒芯片。时间以微秒精度指定,但实际分辨率取决于为 RTC SLOW_CLK 选择的时钟源。
此唤醒模式不需要在睡眠期间打开 RTC 外设或 RTC 存储器。
esp_sleep_enable_timer_wakeup()
函数可用于启用此唤醒源。
仅支持 Light-sleep 模式。当 ESP32 从外部设备接收 UART 输入时,通常需要在输入数据可用时唤醒芯片。UART 外设包含一项功能,当看到 RX 引脚上的一定数量的上升沿时,可以将芯片从 Light-sleep 状态唤醒。可以使用 uart_set_wakeup_threshold()
函数设置此上升沿数。请注意,唤醒后 UART 不会接收触发唤醒的字符(及其前面的任何字符)。这意味着外部设备通常需要在发送数据之前向 ESP32 发送额外字符以触发唤醒。
esp_sleep_enable_uart_wakeup()
函数可用于启用此唤醒源。
每个 IO pad(包括 RTC pad)都有单独的 hold 功能,由 RTC 寄存器控制。pad 的 hold 功能被置上后,pad 在置上 hold 那一刻的状态被强制保持,无论内部信号如何变化,修改 IO_MUX 配置或者 GPIO 配置,都不会改变 pad 的状态。应用如果希望在看门狗超时触发内核复位和系统复位时或者 Deep-sleep 时 pad 的状态不被改变,就需要提前把 hold 置上。
在 Deep-sleep 模式下使用 Hold 功能,可能会增加功耗。并且如果使用内部上拉或下拉电阻,需要在睡眠期间使 RTC 外设电源域保持上电。
在睡眠模式下,部分引脚的电源域可能关掉了,所以无法 Hold 功能无法生效,这时需要确认能否打开电源域。例如:GPIO16、17 会在 light sleep 下关闭电源域 VDD_SDIO,所以无法 Hold,这时需要在 esp_light_sleep_start() 中修改代码打开电源域。
针对 RTC IO,可对单个 RTC Pad 使能 Hold 功能:
rtc_gpio_init(27);
rtc_gpio_set_direction(27, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(27, 1);
rtc_gpio_pullup_en(27);
rtc_gpio_hold_en(27);
针对 数字 IO,会对所有 数字 IO 使能 Hold 功能:
gpio_pad_select_gpio(26);
gpio_set_direction(26, GPIO_MODE_OUTPUT);
gpio_set_level(26, 1);
gpio_hold_en(26);
/*When the chip is in Deep-sleep mode, all digital gpio will hold the state before sleep, and when the chip is woken up, the status of digital gpio will not be held.*/
gpio_deep_sleep_hold_en();
进入 Deep-sleep 后,功耗较高
有这些原因可能会导致功耗较高:
esp_sleep_enable_ext0_wakeup()
需要打开 RTC 外设,产生额外的 100mA 左右的电流消耗,调用 esp_sleep_enable_ext1_wakeup()
则不需要打开 RTC 外设,即不会产生额外的电流。adc_power_off()
关闭 ADC 以解决这个问题。rtc_gpio_isolate()
函数。例如:ESP32-WROVER 模组上,外部上拉 GPIO12。GPIO12 在 ESP32 芯片中也有内部下拉。这意味着在 Deep-sleep 中,一些电流将流过这些外部和内部电阻,从而增加电流消耗。使能 自动 Light-sleep 后,功耗较高
自动 Light-sleep 调试方法
esp_pm_*
函数将跟踪每个电源管理锁的保留时间,esp_pm_dump_locks
函数将打印此信息。 此功能可用于分析哪些锁阻止芯片进入低功耗状态,并查看芯片在每种省电模式下花费的时间,并应在应用程序中保持禁用状态。pm_trace.c
文件。 此功能旨在用于分析/调试电源管理实现的行为时使用,并应在应用程序中保持禁用状态。