提示:本博客作为学习笔记,有错误的地方希望指正
一、ESP32 Sleep Modes概述
参考资料:ESP IDF编程手册V4.4 ??ESP32 具有 Light-sleep 和 Deep-sleep 两种睡眠节能模式。此外还有一种Modem-sleep模式,这种模式在ESP32 WI-FI可以与路由器保持连接。
1、Sleep模式
1.1.1、 Light-sleep 模式
??在 Light-sleep 模式下,数字外设、CPU、以及大部分 RAM 都使用时钟门控,同时电源电压降低。退出该模式后,数字外设、CPU 和 RAM 恢复运行,内部状态保持不变。 ??函数 esp_light_sleep_start() 可用于在配置唤醒源后进入 Light-sleep 模式,也可用于在未配置唤醒源的情况下进入 Light-sleep 模式。在后一种情况中,芯片将一直处于睡眠模式,直到从外部被复位。
1.1.2、 Deep-sleep 模式
??在 Deep-sleep 模式下,CPU、大部分 RAM、以及所有由时钟 APB_CLK 驱动的数字外设都会被断电。芯片上继续处于供电状态的部分仅包括:
- RTC 控制器
- RTC 外设
- ULP 协处理器
- RTC 高速内存
- RTC 低速内存
??函数 esp_deep_sleep_start() 可用于在配置唤醒源后进入 Deep-sleep 模式,也可用于在未配置唤醒源的情况下进入 Deep-sleep 模式 模式。在后一种情况中,芯片将一直处于睡眠模式,直到从外部被复位。
系统资源 | Light-sleep | Deep-sleep |
---|
Wi-Fi 和 Bluetooth 功能 | 关闭 | 关闭 | GPIO状态保 | 保持 | 保持 | 系统时钟 | 关闭 | 关闭 | RTC 时钟 | 开启 | 开启 | CPU 状态 | 暂停 | 关闭 |
??Light-sleep 和 Deep-sleep 模式有多种唤醒源。这些唤醒源也可以组合在一起,此时任何一个唤醒源都可以触发唤醒。通过 API esp_sleep_enable_X_wakeup 可启用唤醒源,通过 API esp_sleep_disable_wakeup_source() 可禁用唤醒源,在系统进入 Light-sleep 或 Deep-sleep 模式前,可以在任意时刻配置唤醒源。 ??此外,应用程序可以使用 API esp_sleep_pd_config() 强制 RTC 外设和 RTC 内存进入特定断电模式。 ??配置唤醒源后,应用程序就可以使用 API esp_light_sleep_start() 或 esp_deep_sleep_start() 进入睡眠模式。此时,系统将按照被请求的唤醒源配置硬件,同时 RTC 控制器会给 CPU 和数字外设断电。 ??如需保持 Wi-Fi 连接,请启用 Wi-Fi Modem-sleep 模式和自动 Light-sleep 模式(请参阅 电源管理 API)。在这两种模式下,Wi-Fi 驱动程序发出请求时,系统将自动从睡眠中被唤醒,从而保持与 AP 的连接。
1.2、睡眠模式下的 Wi-Fi 和 Bluetooth 功能
??在 Light-sleep 和 Deep-sleep 模式下,无线外设会被断电。因此,在进入这两种睡眠模式前,应用程序必须调用恰当的函数(esp_bluedroid_disable()、esp_bt_controller_disable() 或 esp_wifi_stop())来禁用 Wi-Fi 和 Bluetooth。在 Light-sleep 或 Deep-sleep 模式下,即使不调用这些函数也无法连接 Wi-Fi 和 Bluetooth。
1.3、唤醒源
??其中唤醒源有以下几种情况
ESP_SLEEP_WAKEUP_UNDEFINED,
ESP_SLEEP_WAKEUP_ALL ,
ESP_SLEEP_WAKEUP_EXT0 ,
ESP_SLEEP_WAKEUP_EXT1 ,
ESP_SLEEP_WAKEUP_TIMER ,
ESP_SLEEP_WAKEUP_TOUCHPAD,
ESP_SLEEP_WAKEUP_ULP ,
ESP_SLEEP_WAKEUP_GPIO ,
ESP_SLEEP_WAKEUP_UART ,
ESP_SLEEP_WAKEUP_WIFI ,
ESP_SLEEP_WAKEUP_COCPU ,
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG,
ESP_SLEEP_WAKEUP_BT ,
1.3.1、定时器唤醒
??RTC 控制器中内嵌定时器,可用于在预定义的时间到达后唤醒芯片。时间精度为微秒,但其实际分辨率依赖于为 RTC SLOW_CLK 所选择的时钟源。
关于 RTC 时钟选项的更多细节,请参考 ESP32 技术参考手册 > ULP 协处理器 [PDF]。
??在这种唤醒模式下,无需为睡眠模式中的 RTC 外设或内存供电。 ??调用 esp_sleep_enable_timer_wakeup() 函数可启用使用定时器唤醒睡眠模式。
1.3.2、触摸传感器唤醒
??RTC IO 模块中包含这样一个逻辑——当发生触摸传感器中断时,触发唤醒。要启用此唤醒源,用户需要在芯片进入睡眠模式前配置触摸传感器中断功能。 ??ESP32 修订版 0 和 1 仅在 RTC 外设没有被强制供电时支持该唤醒源(即 ESP_PD_DOMAIN_RTC_PERIPH 应被设置为 ESP_PD_OPTION_AUTO)。 ??可调用 esp_sleep_enable_touchpad_wakeup() 函数来启用该唤醒源。
1.3.3、外部唤醒 (ext0)唤醒
??RTC IO 模块中包含这样一个逻辑——当某个 RTC GPIO 被设置为预定义的逻辑值时,触发唤醒。RTC IO 是 RTC 外设电源域的一部分,因此如果该唤醒源被请求,RTC 外设将在 Deep-sleep 模式期间保持供电。 ??在此模式下,RTC IO 模块被使能,因此也可以使用内部上拉或下拉电阻。配置时,应用程序需要在调用函数 esp_deep_sleep_start() 前先调用函数 rtc_gpio_pullup_en() 和 rtc_gpio_pulldown_en()。 ??在 ESP32 修订版 0 和 1 中,此唤醒源与 ULP 和触摸传感器唤醒源都不兼容。 ??可调用 esp_sleep_enable_ext0_wakeup() 函数来启用此唤醒源。
从睡眠模式中唤醒后,用于唤醒的 IO pad 将被配置为 RTC IO。因此,在将该 pad 用作数字 GPIO 之前,请调用 rtc_gpio_deinit() 函数对其进行重新配置。
1.3.4、外部唤醒 (ext1)
??RTC 控制器中包含使用多个 RTC GPIO 触发唤醒的逻辑。您可以从以下两个逻辑函数中选择其一,用于触发唤醒:
- 当所有所选管脚为低电平时唤醒 (ESP_EXT1_WAKEUP_ALL_LOW
- 当任意一个所选管脚为高电平时唤醒(ESP_EXT1_WAKEUP_ANY_HIGH)
??此唤醒源由 RTC 控制器实现。这种模式下的 RTC 外设和 RTC 内存可以被断电。但如果 RTC 外设被断电,内部上拉和下拉电阻将被禁用。想要使用内部上拉和下拉电阻,需要 RTC 外设电源域在睡眠期间保持开启,并在进入睡眠前使用函数 rtc_gpio_ 配置上拉或下拉电阻。
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
gpio_pullup_dis(gpio_num);
gpio_pulldown_en(gpio_num);
从睡眠模式中唤醒后,用于唤醒的 IO pad 将被配置为 RTC IO。因此在将该 pad 用作数字 GPIO 前,请调用 rtc_gpio_deinit() 函数对其进行重新配置。
??可调用 esp_sleep_enable_ext1_wakeup() 函数来启用此唤醒源。
1.3.5、ULP 协处理器唤醒
??当芯片处于睡眠模式时,ULP 协处理器仍然运行,可用于轮询传感器、监视 ADC 或触摸传感器的值,并在检测到特殊事件时唤醒芯片。ULP 协处理器是 RTC 外设电源域的一部分,运行存储在 RTC 低速内存中的程序。如果这一唤醒源被请求,RTC 低速内存将会在睡眠期间保持供电状态。RTC 外设会在 ULP 协处理器开始运行程序前自动上电;一旦程序停止运行,RTC 外设会再次自动断电。 ??ESP32 修订版 0 和 1 仅在 RTC 外设没有被强制供电时支持该唤醒(即 ESP_PD_DOMAIN_RTC_PERIPH 应被设置为 ESP_PD_OPTION_AUTO)。 ??可调用 esp_sleep_enable_ulp_wakeup() 函数来启用此唤醒源。
1.3.6、GPIO 唤醒(仅适用于 Light-sleep 模式)
??除了上述 EXT0 和 EXT1 唤醒源之外,还有一种从外部唤醒 Light-sleep 模式的方法——使用函数 gpio_wakeup_enable()。启用该唤醒源后,可将每个管脚单独配置为在高电平或低电平时唤醒。EXT0 和 EXT1 唤醒源只能用于 RTC IO,但此唤醒源既可以用于 RTC IO,可也用于数字 IO。 ??可调用 esp_sleep_enable_gpio_wakeup() 函数来启用此唤醒源。
??在进入 Light-sleep 模式前,请查看您将要驱动的 GPIO 管脚的电源域。如果有管脚属于 VDD_SDIO 电源域,必须将此电源域配置为在睡眠期间保持供电。
??例如,在 ESP32-WROOM-32 开发板上,GPIO16 和 GPIO17 连接到 VDD_SDIO 电源域。如果这两个管脚被配置为在睡眠期间保持高电平,则您需将对应电源域配置为保持供电。您可以使用函数 esp_sleep_pd_config():
esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSDIO, ESP_PD_OPTION_ON);
1.3.7、UART 唤醒(仅适用于 Light-sleep 模式)
??当 ESP32 从外部设备接收 UART 输入时,通常需要在输入数据可用时唤醒芯片。UART 外设支持在 RX 管脚上观测到一定数量的上升沿时,将芯片从 Light-sleep 模式中唤醒。调用 uart_set_wakeup_threshold() 函数可设置被观测上升沿的数量。请注意,触发唤醒的字符(及该字符前的所有字符)在唤醒后不会被 UART 接收,因此在发送数据之前,外部设备通常需要首先向 ESP32 额外发送一个字符以触发唤醒。 ??可调用 esp_sleep_enable_uart_wakeup() 函数来启用此唤醒源。
1.4、RTC 外设和内存断电
??默认情况下,调用函数 esp_deep_sleep_start() 和 esp_light_sleep_start() 后,所有唤醒源不需要的 RTC 电源域都会被断电。可调用函数 esp_sleep_pd_config() 来修改这一设置。 ??注意:在 ESP32 修订版 1 中,RTC 高速内存在 Deep-sleep 期间将总是保持使能,以保证复位后可运行 Deep-sleep stub。如果应用程序在 Deep-sleep 模式后无需复位,您也可以对其进行修改。 ??如果程序中的某些值被放入 RTC 低速内存中(例如使用 RTC_DATA_ATTR 属性),RTC 低速内存将默认保持供电。如果有需要,也可以使用函数 esp_sleep_pd_config() 对其进行修改。
1.5、配置 IO
??一些 ESP32 IO 在默认情况下启用内部上拉或下拉电阻。如果这些管脚在 Deep-sleep 模式下中受外部电路驱动,电流流经这些上下拉电阻时,可能会增加电流消耗。 ??想要隔离这些管脚以避免额外的电流消耗,请调用 rtc_gpio_isolate() 函数。 ??例如,在 ESP32-WROVER 模组上,GPIO12 在外部上拉,但其在 ESP32 芯片中也有内部下拉。这意味着在 Deep-sleep 模式中,电流会流经这些外部和内部电阻,使电流消耗超出可能的最小值。 ??在函数 esp_deep_sleep_start() 前增加以下代码即可避免额外电流消耗:
rtc_gpio_isolate(GPIO_NUM_12);
1.6、UART 输出处理
??在进入睡眠模式之前,调用函数 esp_deep_sleep_start() 会冲刷掉 UART FIFO 缓存。 ??当使用函数 esp_light_sleep_start() 进入 Light-sleep 模式时,UART FIFO 将不会被冲刷。与之相反,UART 输出将被暂停,FIFO 中的剩余字符将在 Light-sleep 唤醒后被发送。
1.7、检查睡眠唤醒原因
??esp_sleep_get_wakeup_cause() 函数可用于检测是何种唤醒源在睡眠期间被触发。 ??对于触摸传感器唤醒源,可以调用函数 esp_sleep_get_touchpad_wakeup_status() 来确认触发唤醒的触摸管脚。 ??对于 ext1 唤醒源,可以调用函数 esp_sleep_get_ext1_wakeup_status() 来确认触发唤醒的触摸管脚。
1.8、禁用睡眠模式唤醒源
??调用 API esp_sleep_disable_wakeup_source() 可以禁用给定唤醒源的触发器,从而禁用该唤醒源。此外,如果将参数设置为 ESP_SLEEP_WAKEUP_ALL,该函数可用于禁用所有触发器。
二、硬件设计
??经过我测试下ESP32和ESP32S3的串口唤醒有些不一样,ESP32串口唤醒不需要配置接收引脚GPIO可以实现唤醒,但是ESP32S3不配置不能唤醒。具体的功耗手里目前没有工具没法测试。
三、Light-sleep 实现代码
??值得注意的是我在测试的时候发现了几个问题,官方给的Demo中GPIO唤醒的时候没有配置输入上下拉使能,当我手指在GPIO引脚上空的时候就会出现感应到GPIO唤醒,并且一段时间GPIO就会复位,其次是在于ESP32和ESP32的串口唤醒有些不一样,对于ESP32的串口唤醒的话我测试的时候不需要配追GPIO的参数,其次就是串口唤醒不支持GPIO矩阵作为串口的引脚,必须原配的,下图就是ESP32的串口唤醒截图。 ??对于ESP32S3的话我测试过可以使用GPIO矩阵作为串口换新的,但是需要配置前面的GPIO参数,不然原配的引脚或者IO矩阵的引脚都不行,不能唤醒,弄了许久的,我以为前面配置了GPIO的参数就是GPIO唤醒掩盖了串口唤醒,但是我到唤醒源函数中看逻辑,逻辑是GPIO的唤醒等级逻辑高于串口,这样看来这样配置就是没有问题可以触发串口唤醒的。如果有小伙伴发现更好的方法欢迎留言一起讨论。 ??在这个实验历程中有定时器唤醒和串口唤醒以及GPIO唤醒三种状态。整个测试文件的初始化流程为。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#if CONFIG_IDF_TARGET_ESP32C3
#define BUTTON_GPIO_NUM_DEFAULT 9
#else
#define BUTTON_GPIO_NUM_DEFAULT 19
#endif
#define BUTTON_WAKEUP_LEVEL_DEFAULT 0
#define Power_Control_Pin 10
#define RX_BUF_SIZE 1024
#define Wakeup_Uart_NumberPort UART_NUM_0
#define Wakeup_Uart_DataNumber 3
#define UART_RX_GPIO_NUM 44
const char * TAG = "Light Sleep";
void app_main(void)
{
esp_err_t err = ESP_OK;
const int button_gpio_num = BUTTON_GPIO_NUM_DEFAULT;
const int wakeup_level = BUTTON_WAKEUP_LEVEL_DEFAULT;
gpio_config_t config = {
.pin_bit_mask = BIT64(button_gpio_num),
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
};
ESP_ERROR_CHECK(gpio_config(&config));
gpio_wakeup_enable(button_gpio_num,wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
const int uart_rx_gpio_num = UART_RX_GPIO_NUM;
gpio_wakeup_enable(uart_rx_gpio_num,wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
gpio_config_t gpio_uart_config = {
.pin_bit_mask = BIT64(uart_rx_gpio_num),
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
};
ESP_ERROR_CHECK(gpio_config(&gpio_uart_config));
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
uart_driver_install(Wakeup_Uart_NumberPort, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(Wakeup_Uart_NumberPort, &uart_config);
uart_set_pin(Wakeup_Uart_NumberPort, 43, 44, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
char data[] = "168";
uart_write_bytes(Wakeup_Uart_NumberPort, (const char *) data, 4);
err = uart_set_wakeup_threshold(Wakeup_Uart_NumberPort,Wakeup_Uart_DataNumber);
if(err != ESP_OK)
ESP_LOGE(TAG,"Init Err");
err = esp_sleep_enable_uart_wakeup(Wakeup_Uart_NumberPort);
if(err != ESP_OK)
ESP_LOGE(TAG,"Init Err");
esp_sleep_enable_gpio_wakeup();
while (true) {
esp_sleep_enable_timer_wakeup(5000000);
esp_sleep_enable_gpio_wakeup();
printf("Entering light sleep\n");
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
int64_t t_before_us = esp_timer_get_time();
esp_light_sleep_start();
int64_t t_after_us = esp_timer_get_time();
const char* wakeup_reason;
switch (esp_sleep_get_wakeup_cause()) {
case ESP_SLEEP_WAKEUP_TIMER:
wakeup_reason = "timer";
break;
case ESP_SLEEP_WAKEUP_GPIO:
wakeup_reason = "pin";
break;
case ESP_SLEEP_WAKEUP_UART:
wakeup_reason = "uart";
break;
default:
wakeup_reason = "other";
break;
}
printf("Returned from light sleep, reason: %s, t=%lld ms, slept for %lld ms\n",wakeup_reason, t_after_us / 1000, (t_after_us - t_before_us) / 1000);
}
}
四、Light-sleep 实验演示结果
五、Deep-sleep 实现代码
??深度睡眠模式下cpu唤醒就会复位,但是功耗低,支持触摸唤醒、定时器唤醒,ulp唤醒,但是ulp是汇编写的。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "soc/rtc.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/ulp.h"
#endif
#if SOC_TOUCH_SENSOR_NUM > 0
#include "soc/sens_periph.h"
#include "driver/touch_pad.h"
#endif
#ifdef CONFIG_IDF_TARGET_ESP32C3
#define DEFAULT_WAKEUP_PIN CONFIG_EXAMPLE_GPIO_WAKEUP_PIN
#ifdef CONFIG_EXAMPLE_GPIO_WAKEUP_HIGH_LEVEL
#define DEFAULT_WAKEUP_LEVEL ESP_GPIO_WAKEUP_GPIO_HIGH
#else
#define DEFAULT_WAKEUP_LEVEL ESP_GPIO_WAKEUP_GPIO_LOW
#endif
#endif
static RTC_DATA_ATTR struct timeval sleep_enter_time;
#define CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
#define ULP_DATA_OFFSET 36
_Static_assert(ULP_DATA_OFFSET < CONFIG_ESP32_ULP_COPROC_RESERVE_MEM/4 - 6,
"ULP_DATA_OFFSET is set too high, or CONFIG_ESP32_ULP_COPROC_RESERVE_MEM is not sufficient");
static void start_ulp_temperature_monitoring(void);
static inline uint16_t ulp_data_read(size_t offset)
{
return RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] & 0xffff;
}
static inline void ulp_data_write(size_t offset, uint16_t value)
{
RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] = value;
}
#endif
#endif
#ifdef CONFIG_EXAMPLE_TOUCH_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
#define TOUCH_THRESH_NO_USE 0
static void calibrate_touch_pad(touch_pad_t pad);
#endif
#endif
void app_main(void)
{
struct timeval now;
gettimeofday(&now, NULL);
int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
switch (esp_sleep_get_wakeup_cause()) {
#ifdef CONFIG_EXAMPLE_EXT1_WAKEUP
case ESP_SLEEP_WAKEUP_EXT1: {
uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
if (wakeup_pin_mask != 0) {
int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
printf("Wake up from GPIO %d\n", pin);
} else {
printf("Wake up from GPIO\n");
}
break;
}
#endif
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
case ESP_SLEEP_WAKEUP_GPIO: {
uint64_t wakeup_pin_mask = esp_sleep_get_gpio_wakeup_status();
if (wakeup_pin_mask != 0) {
int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
printf("Wake up from GPIO %d\n", pin);
} else {
printf("Wake up from GPIO\n");
}
break;
}
#endif
case ESP_SLEEP_WAKEUP_TIMER: {
printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms);
break;
}
#ifdef CONFIG_EXAMPLE_TOUCH_WAKEUP
case ESP_SLEEP_WAKEUP_TOUCHPAD: {
printf("Wake up from touch on pad %d\n", esp_sleep_get_touchpad_wakeup_status());
break;
}
#endif
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
case ESP_SLEEP_WAKEUP_ULP: {
printf("Wake up from ULP\n");
int16_t diff_high = (int16_t)ulp_data_read(3);
int16_t diff_low = (int16_t) ulp_data_read(4);
if (diff_high < 0) {
printf("High temperature alarm was triggered\n");
} else if (diff_low < 0) {
printf("Low temperature alarm was triggered\n");
} else {
assert(false && "temperature has stayed within limits, but got ULP wakeup\n");
}
break;
}
#endif
#endif
case ESP_SLEEP_WAKEUP_UNDEFINED:
default:
printf("Not a deep sleep reset\n");
}
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED) {
printf("ULP did %d temperature measurements in %d ms\n", ulp_data_read(1), sleep_time_ms);
printf("Initial T=%d, latest T=%d\n", ulp_data_read(0), ulp_data_read(2));
}
#endif
#endif
vTaskDelay(1000 / portTICK_PERIOD_MS);
const int wakeup_time_sec = 20;
printf("Enabling timer wakeup, %ds\n", wakeup_time_sec);
esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
#ifdef CONFIG_EXAMPLE_EXT1_WAKEUP
const int ext_wakeup_pin_1 = 2;
const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1;
const int ext_wakeup_pin_2 = 4;
const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2;
printf("Enabling EXT1 wakeup on pins GPIO%d, GPIO%d\n", ext_wakeup_pin_1, ext_wakeup_pin_2);
esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask | ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
#endif
#ifdef CONFIG_EXAMPLE_GPIO_WAKEUP
const gpio_config_t config = {
.pin_bit_mask = BIT(DEFAULT_WAKEUP_PIN),
.mode = GPIO_MODE_INPUT,
};
ESP_ERROR_CHECK(gpio_config(&config));
ESP_ERROR_CHECK(esp_deep_sleep_enable_gpio_wakeup(BIT(DEFAULT_WAKEUP_PIN), DEFAULT_WAKEUP_LEVEL));
printf("Enabling GPIO wakeup on pins GPIO%d\n", DEFAULT_WAKEUP_PIN);
#endif
#ifdef CONFIG_EXAMPLE_TOUCH_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
ESP_ERROR_CHECK(touch_pad_init());
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_set_voltage(TOUCH_HVOLT_2V4, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
touch_pad_config(TOUCH_PAD_NUM8, TOUCH_THRESH_NO_USE);
touch_pad_config(TOUCH_PAD_NUM9, TOUCH_THRESH_NO_USE);
calibrate_touch_pad(TOUCH_PAD_NUM8);
calibrate_touch_pad(TOUCH_PAD_NUM9);
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
touch_pad_init();
touch_pad_config(TOUCH_PAD_NUM9);
touch_pad_denoise_t denoise = {
.grade = TOUCH_PAD_DENOISE_BIT4,
.cap_level = TOUCH_PAD_DENOISE_CAP_L4,
};
touch_pad_denoise_set_config(&denoise);
touch_pad_denoise_enable();
printf("Denoise function init\n");
touch_filter_config_t filter_info = {
.mode = TOUCH_PAD_FILTER_IIR_16,
.debounce_cnt = 1,
.noise_thr = 0,
.jitter_step = 4,
.smh_lvl = TOUCH_PAD_SMOOTH_IIR_2,
};
touch_pad_filter_set_config(&filter_info);
touch_pad_filter_enable();
printf("touch pad filter init %d\n", TOUCH_PAD_FILTER_IIR_8);
touch_pad_sleep_channel_enable(TOUCH_PAD_NUM9, true);
touch_pad_sleep_channel_enable_proximity(TOUCH_PAD_NUM9, false);
touch_pad_sleep_channel_set_work_time(1000, TOUCH_PAD_MEASURE_CYCLE_DEFAULT);
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_fsm_start();
vTaskDelay(100 / portTICK_RATE_MS);
uint32_t touch_value, wake_threshold;
touch_pad_sleep_channel_read_smooth(TOUCH_PAD_NUM9, &touch_value);
wake_threshold = touch_value * 0.1;
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM9, wake_threshold);
printf("Touch pad #%d average: %d, wakeup threshold set to %d\n",
TOUCH_PAD_NUM9, touch_value, (uint32_t)(touch_value * 0.1));
#endif
printf("Enabling touch pad wakeup\n");
esp_sleep_enable_touchpad_wakeup();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
printf("Enabling ULP wakeup\n");
esp_sleep_enable_ulp_wakeup();
#endif
#endif
#if CONFIG_IDF_TARGET_ESP32
rtc_gpio_isolate(GPIO_NUM_12);
#endif
printf("Entering deep sleep\n");
gettimeofday(&sleep_enter_time, NULL);
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
start_ulp_temperature_monitoring();
#endif
#endif
esp_deep_sleep_start();
}
#ifdef CONFIG_EXAMPLE_TOUCH_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
static void calibrate_touch_pad(touch_pad_t pad)
{
int avg = 0;
const size_t calibration_count = 128;
for (int i = 0; i < calibration_count; ++i) {
uint16_t val;
touch_pad_read(pad, &val);
avg += val;
}
avg /= calibration_count;
const int min_reading = 300;
if (avg < min_reading) {
printf("Touch pad #%d average reading is too low: %d (expecting at least %d). "
"Not using for deep sleep wakeup.\n", pad, avg, min_reading);
touch_pad_config(pad, 0);
} else {
int threshold = avg - 100;
printf("Touch pad #%d average: %d, wakeup threshold set to %d.\n", pad, avg, threshold);
touch_pad_config(pad, threshold);
}
}
#endif
#endif
#ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP
#if CONFIG_IDF_TARGET_ESP32
static void start_ulp_temperature_monitoring(void)
{
const int16_t max_temp_diff = 3;
const uint32_t measurements_per_sec = 5;
SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 2, SENS_TSENS_CLK_DIV_S);
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S);
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE);
memset(RTC_SLOW_MEM, 0, CONFIG_ESP32_ULP_COPROC_RESERVE_MEM);
ulp_data_write(0, 0);
ulp_data_write(1, 0);
const ulp_insn_t program[] = {
I_MOVI(R2, ULP_DATA_OFFSET),
I_LD(R1, R2, 1),
I_ADDI(R1, R1, 1),
I_ST(R1, R2, 1),
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3),
I_TSENS(R3, 8000),
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0),
I_ST(R3, R2, 2),
I_LD(R0, R2, 0),
M_BGE(1, 1),
I_MOVR(R0, R3),
I_ST(R0, R2, 0),
M_LABEL(1),
I_ADDI(R1, R0, max_temp_diff - 1),
I_SUBR(R1, R1, R3),
I_ST(R1, R2, 3),
M_BXF(2),
I_SUBI(R1, R0, max_temp_diff - 1),
I_SUBR(R1, R3, R1),
I_ST(R1, R2, 4),
M_BXF(2),
I_HALT(),
M_LABEL(2),
I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0),
I_WAKE(),
I_HALT()
};
size_t size = sizeof(program)/sizeof(ulp_insn_t);
ESP_ERROR_CHECK( ulp_process_macros_and_load(0, program, &size) );
assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
const uint32_t sleep_cycles = rtc_clk_slow_freq_get_hz() / measurements_per_sec;
REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles);
ESP_ERROR_CHECK( ulp_run(0) );
}
#endif
#endif
六、Deep-sleep 实验演示结果
??分别为定时器唤醒和触摸唤醒。
七、ESP32 Sleep.h文件中的API
typedef enum {
ESP_EXT1_WAKEUP_ALL_LOW = 0,
ESP_EXT1_WAKEUP_ANY_HIGH = 1
} esp_sleep_ext1_wakeup_mode_t;
# if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
typedef enum {
ESP_GPIO_WAKEUP_GPIO_LOW = 0,
ESP_GPIO_WAKEUP_GPIO_HIGH = 1
} esp_deepsleep_gpio_wake_up_mode_t;
# endif
typedef enum {
ESP_PD_DOMAIN_RTC_PERIPH ,
ESP_PD_DOMAIN_RTC_SLOW_MEM,
ESP_PD_DOMAIN_RTC_FAST_MEM,
ESP_PD_DOMAIN_XTAL ,
#if SOC_PM_SUPPORT_CPU_PD
ESP_PD_DOMAIN_CPU ,
# endif
ESP_PD_DOMAIN_RTC8M ,
ESP_PD_DOMAIN_VDDSDIO ,
ESP_PD_DOMAIN_MAX
} esp_sleep_pd_domain_t;
typedef enum {
ESP_PD_OPTION_OFF ,
ESP_PD_OPTION_ON ,
ESP_PD_OPTION_AUTO
} esp_sleep_pd_option_t;
typedef enum {
ESP_SLEEP_WAKEUP_UNDEFINED,
ESP_SLEEP_WAKEUP_ALL ,
ESP_SLEEP_WAKEUP_EXT0 ,
ESP_SLEEP_WAKEUP_EXT1 ,
ESP_SLEEP_WAKEUP_TIMER ,
ESP_SLEEP_WAKEUP_TOUCHPAD,
ESP_SLEEP_WAKEUP_ULP ,
ESP_SLEEP_WAKEUP_GPIO ,
ESP_SLEEP_WAKEUP_UART ,
ESP_SLEEP_WAKEUP_WIFI ,
ESP_SLEEP_WAKEUP_COCPU ,
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG,
ESP_SLEEP_WAKEUP_BT ,
} esp_sleep_source_t;
typedef esp_sleep_source_t esp_sleep_wakeup_cause_t;
esp_err_t esp_sleep_disable_wakeup_source (esp_sleep_source_t source);
#if SOC_ULP_SUPPORTED
esp_err_t esp_sleep_enable_ulp_wakeup(void);
# endif
esp_err_t esp_sleep_enable_timer_wakeup (uint64_t time_in_us);
#if SOC_TOUCH_SENSOR_NUM > 0
esp_err_t esp_sleep_enable_touchpad_wakeup(void);
touch_pad_t esp_sleep_get_touchpad_wakeup_status(void);
#endif
bool esp_sleep_is_valid_wakeup_gpio (gpio_num_t gpio_num);
#if SOC_PM_SUPPORT_EXT_WAKEUP
Esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
Esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask,esp_sleep_ext1_wakeup_mode_t mode);
# endif
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
Esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode);
# endif
esp_err_t esp_sleep_enable_gpio_wakeup(void);
esp_err_t esp_sleep_enable_uart_wakeup (int uart_num);
esp_err_t esp_sleep_enable_wifi_wakeup(void);
esp_err_t esp_sleep_disable_wifi_wakeup(void);
uint64_t esp_sleep_get_ext1_wakeup_status(void);
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
uint64_t esp_sleep_get_gpio_wakeup_status(void);
# endif
esp_err_t esp_sleep_pd_config (esp_sleep_pd_domain_t domain,esp_sleep_pd_option_t option);
void esp_deep_sleep_start(void) __attribute__((noreturn));
esp_err_t esp_light_sleep_start(void);
void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn));
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void);
void esp_wake_deep_sleep(void);
typedefvoid(* esp_deep_sleep_wake_stub_fn_t)(void);
voidesp_set_deep_sleep_wake_stub (esp_deep_sleep_wake_stub_fn_t new_stub);
esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void);
voidesp_default_wake_deep_sleep(void);
voidesp_deep_sleep_disable_rom_logging(void);
# ifdef SOC_PM_SUPPORT_CPU_PD
esp_err_t esp_sleep_cpu_pd_low_init (bool enable);
# endif
#if SOC_GPIO_SUPPORT_SLP_SWITCH
voidesp_sleep_config_gpio_isolate(void);
voidesp_sleep_enable_gpio_switch (bool enable);
# endif
#if CONFIG_MAC_BB_PD
typedef void (* mac_bb_power_down_cb_t)(void);
typedef void (* mac_bb_power_up_cb_t)(void);
esp_err_t esp_register_mac_bb_pd_callback (mac_bb_power_down_cb_t cb);
esp_err_t esp_unregister_mac_bb_pd_callback (mac_bb_power_down_cb_t cb);
esp_err_t esp_register_mac_bb_pu_callback (mac_bb_power_up_cb_t cb);
esp_err_t esp_unregister_mac_bb_pu_callback (mac_bb_power_up_cb_t cb);
|