ESP32S3基于espidf 深度睡眠模式和唤醒使用
ESP32S3基于espidf 深度睡眠模式和唤醒使用
- 📍睡眠模式官方在线文档介绍:
https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/system/sleep_modes.html?highlight=ulp
- 有关RTC GPIO官方在线文档介绍:
https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/peripherals/gpio.html
📝超低功耗协处理器 (ULP-FSM, ULP-RISC-V)(ESP32S3参考手册)
超低功耗协处理器 (ULP, Ultra-Low-Power coprocessor) 是一种功耗极低的处理器设备,可在芯片进入Deep-sleep 时保持上电,允许开发者通过存储在 RTC 存储器中的专用程序,访问 RTC 外设、内部传感器及 RTC 寄存器。在对功耗敏感的场景下,主 CPU 处于睡眠状态以降低功耗,协处理器可以由协处理器定时器唤醒,通过控制 RTC GPIO、RTC I2C、SAR ADC、温度传感器 (TSENS) 等外设监测外部环境或与外部电路进行交互,并在达到唤醒条件时主动唤醒主 CPU。
esp32s3在 Deep-sleep 模式下,CPU、大部分 RAM、以及所有由时钟 APB_CLK 驱动的数字外设都会被断电。芯片上继续处于供电状态的部分仅包括:
- RTC 控制器
ULP 协处理器
RTC 高速内存
RTC 低速内存
ESP32-S3 搭载了基于有限状态机 (FSM) 实现的 ULP 协处理器(以下简称 ULP-FSM)和基于 RISC-V 指令集的ULP 协处理器(以下简称 ULP-RISC-V)
- 特性
- • 可访问最多 8 KB SRAM RTC 慢速内存,用于储存指令和数据
• 时钟采用 17.5 MHz RTC_FAST_CLK
• 支持正常模式和 Monitor 模式
• 可唤醒 CPU 或向 CPU 发送中断
• 可访问外设、内部传感器及 RTC 寄存器
ULP-FSM 和 ULP-RISC-V 不能同时工作,用户只能选择其中一个作为 ESP32-S3 的超低功耗协处理器。
ULP-FSM 与 ULP-RISC-V 特性比较如下:
📘唤醒源
- 📍相关参考文档:
https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/system/sleep_modes.html?highlight=ulp
1. 定时器唤醒(Timer Wakeup)
描述: 通过 RTC 定时器设置一个时间间隔,设备在深度睡眠后经过指定时间自动唤醒。
相关函数:
esp_sleep_enable_timer_wakeup(uint64_t time_in_us);
2. 外部唤醒(External Wakeup)
描述: 通过外部引脚的电平变化唤醒设备。ESP32-S3 支持两种外部唤醒方式:
-
- EXT0: 单个 GPIO 引脚的电平触发。
-
- EXT1: 多个 GPIO 引脚的组合触发。
相关函数:
- EXT0:
esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
gpio_num: 触发唤醒的 GPIO 引脚。
level: 触发电平(0 或 1)。
- EXT1:
esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode);
mask: GPIO 引脚的位掩码(例如 GPIO_SEL_12 表示 GPIO12)。
mode: 触发模式:
ESP_EXT1_WAKEUP_ALL_LOW
: 所有引脚为低电平时触发。
ESP_EXT1_WAKEUP_ANY_HIGH
: 任意引脚为高电平时触发。
特点: 适用于按键、传感器信号等外部事件触发唤醒。
- 外部唤醒(EXT0/EXT1)可配置gpio引脚:(ESP32S3)
const int rtc_io_num_map[SOC_GPIO_PIN_COUNT] = {
RTCIO_GPIO0_CHANNEL, //GPIO0
-1,//GPIO1
RTCIO_GPIO2_CHANNEL, //GPIO2
-1,//GPIO3
RTCIO_GPIO4_CHANNEL, //GPIO4
-1,//GPIO5
-1,//GPIO6
-1,//GPIO7
-1,//GPIO8
-1,//GPIO9
-1,//GPIO10
-1,//GPIO11
RTCIO_GPIO12_CHANNEL, //GPIO12
RTCIO_GPIO13_CHANNEL, //GPIO13
RTCIO_GPIO14_CHANNEL, //GPIO14
RTCIO_GPIO15_CHANNEL, //GPIO15
-1,//GPIO16
-1,//GPIO17
-1,//GPIO18
-1,//GPIO19
-1,//GPIO20
-1,//GPIO21
-1,//GPIO22
-1,//GPIO23
-1,//GPIO24
RTCIO_GPIO25_CHANNEL, //GPIO25
RTCIO_GPIO26_CHANNEL, //GPIO26
RTCIO_GPIO27_CHANNEL, //GPIO27
-1,//GPIO28
-1,//GPIO29
-1,//GPIO30
-1,//GPIO31
RTCIO_GPIO32_CHANNEL, //GPIO32
RTCIO_GPIO33_CHANNEL, //GPIO33
RTCIO_GPIO34_CHANNEL, //GPIO34
RTCIO_GPIO35_CHANNEL, //GPIO35
RTCIO_GPIO36_CHANNEL, //GPIO36
RTCIO_GPIO37_CHANNEL, //GPIO37
RTCIO_GPIO38_CHANNEL, //GPIO38
RTCIO_GPIO39_CHANNEL, //GPIO39
};
- gpio初始化配置:
#include "driver/gpio.h"
// 配置 GPIO 为输入模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_12), // 选择 GPIO12
.mode = GPIO_MODE_INPUT, // 输入模式
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
.intr_type = GPIO_INTR_DISABLE, // 禁用中断(唤醒功能不需要中断)
};
gpio_config(&io_conf);
// 配置 EXT0 唤醒
esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 1); // GPIO12 高电平触发唤醒
// 配置 EXT1 唤醒
uint64_t mask = (1ULL << GPIO_NUM_12) | (1ULL << GPIO_NUM_13); // GPIO12 和 GPIO13
esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH); // 任意引脚高电平触发唤醒
- 配置 GPIO 引脚并使用 EXT0 和 EXT1 唤醒模式:
#include "driver/gpio.h"
#include "esp_sleep.h"
void app_main() {
// 配置 GPIO12 为输入模式,启用上拉电阻
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_12), // GPIO12
.mode = GPIO_MODE_INPUT, // 输入模式
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
.intr_type = GPIO_INTR_DISABLE, // 禁用中断
};
gpio_config(&io_conf);
// 配置 EXT0 唤醒(GPIO12 高电平触发)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 1);
// 或者配置 EXT1 唤醒(GPIO12 或 GPIO13 高电平触发)
// uint64_t mask = (1ULL << GPIO_NUM_12) | (1ULL << GPIO_NUM_13);
// esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH);
// 进入深度睡眠
printf("Entering deep sleep\n");
esp_deep_sleep_start();
}
3. 触摸传感器唤醒(Touchpad Wakeup)
描述: 通过触摸传感器的信号变化唤醒设备。
相关函数:
esp_sleep_enable_touchpad_wakeup();
特点: 适用于需要触摸交互的场景。
- 可配置通道和对应引脚关系:
-
相关参考例程:
v5.4\esp-idf\examples\system\deep_sleep
-
📝 代码配置:
#include "driver/touch_pad.h"
#include "esp_sleep.h"
#include "esp_log.h"
void app_main() {
// 初始化触摸传感器
touch_pad_init();
// 配置触摸传感器引脚(例如 TOUCH_PAD_NUM8)
touch_pad_config(TOUCH_PAD_NUM8, 0); // 0 表示默认阈值
// 设置触摸传感器阈值
touch_pad_set_thresh(TOUCH_PAD_NUM8, 1000); // 设置阈值为 1000
// 启用触摸传感器滤波(可选)
touch_pad_filter_start(10); // 滤波周期为 10ms
// 启用触摸传感器唤醒
esp_sleep_enable_touchpad_wakeup();
// 进入深度睡眠
ESP_LOGI("TOUCH", "Entering deep sleep");
esp_deep_sleep_start();
}
4. ULP 协处理器唤醒(ULP Coprocessor Wakeup)
描述: ULP(Ultra Low Power)协处理器可以在深度睡眠模式下运行,并在满足条件时唤醒主处理器。
相关函数:
esp_sleep_enable_ulp_wakeup();
特点: 适用于需要低功耗运行并周期性检查某些条件的场景。
- 需要使用到超低功耗协处理器 (ULP-FSM, ULP-RISC-V),配置比较麻烦,可以参见:
v5.4\esp-idf\examples\system\ulp\lp_core\gpio_wakeup
- ESP32S3使用
ULP-RISC-V
,adc唤醒测试失败,输入模拟电压达到设定的阈值,无法唤醒。使用ULP-FSM
,adc唤醒测试没有问题,输入的模拟电压信号达到设定范围,即可唤醒,在ULP-FSM
低功耗模式下,可以正常运行ADC功能,也可以正常响应唤醒。
5. GPIO 唤醒(GPIO Wakeup)
描述: 通过 GPIO 引脚的电平变化唤醒设备。与外部唤醒类似,但 GPIO 唤醒支持更多的引脚和灵活配置。
GPIO 唤醒允许在深度睡眠模式下通过 GPIO 引脚的电平变化唤醒设备。与外部唤醒(EXT0/EXT1)相比,GPIO 唤醒支持更多的 GPIO 引脚,并且可以配置为高电平或低电平触发。
相关函数:
esp_sleep_enable_gpio_wakeup();
- 所有GPIO引脚:
并非所有 GPIO 引脚都支持 GPIO 唤醒功能。请参考 ESP32-S3 的技术手册,确认所选引脚是否支持唤醒功能。
- 配置 GPIO 引脚并使用 GPIO 唤醒功能:
#include "driver/gpio.h"
#include "esp_sleep.h"
void app_main() {
// 配置 GPIO12 为输入模式,启用上拉电阻
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_12), // GPIO12
.mode = GPIO_MODE_INPUT, // 输入模式
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉电阻
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉电阻
.intr_type = GPIO_INTR_DISABLE, // 禁用中断
};
gpio_config(&io_conf);
// 配置 GPIO12 为高电平触发唤醒
gpio_wakeup_enable(GPIO_NUM_12, GPIO_INTR_HIGH_LEVEL);
// 启用 GPIO 唤醒
esp_sleep_enable_gpio_wakeup();
// 进入深度睡眠
printf("Entering deep sleep\n");
esp_deep_sleep_start();
}
- UART 唤醒(UART Wakeup)
描述: 通过 UART 接收到的数据唤醒设备。(仅适用于 Light-sleep 模式)
相关函数:
esp_sleep_enable_uart_wakeup(int uart_num);
uart_num: UART 端口号(例如 UART_NUM_0)。
- 特点: 适用于需要通过串口通信唤醒的场景。
- 串口唤醒配置代码:
#include "driver/uart.h"
#include "esp_sleep.h"
#include "esp_log.h"
#define UART_NUM UART_NUM_1
#define TX_PIN GPIO_NUM_17
#define RX_PIN GPIO_NUM_18
void uart_init() {
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_DEFAULT,
};
uart_param_config(UART_NUM, &uart_config);
uart_set_pin(UART_NUM, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(UART_NUM, 1024, 0, 0, NULL, 0);
}
void enable_uart_wakeup() {
esp_sleep_enable_uart_wakeup(UART_NUM);
}
void enter_light_sleep() {
ESP_LOGI("UART", "Entering light sleep");
esp_light_sleep_start();
ESP_LOGI("UART", "Woken up from light sleep");
}
void app_main() {
// 初始化 UART
uart_init();
// 启用 UART 唤醒
enable_uart_wakeup();
// 进入 Light-sleep 模式
enter_light_sleep();
}
- 调用 API
esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
可以禁用给定唤醒源的触发器,从而禁用该唤醒源。
typedef enum {
ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep
ESP_SLEEP_WAKEUP_ALL, //!< Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO
ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL
ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer
ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only on ESP32, S2 and S3)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
ESP_SLEEP_WAKEUP_COCPU, //!< Wakeup caused by COCPU int
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG, //!< Wakeup caused by COCPU crash
ESP_SLEEP_WAKEUP_BT, //!< Wakeup caused by BT (light sleep only)
ESP_SLEEP_WAKEUP_VAD, //!< Wakeup caused by VAD
} esp_sleep_source_t;
- 如果将参数设置为
ESP_SLEEP_WAKEUP_ALL
,该函数可用于禁用所有触发器。
- WiFi 唤醒(WiFi Wakeup)
描述: 通过 WiFi 信号唤醒设备。ESP32-S3 支持在深度睡眠模式下通过 WiFi 唤醒。
相关函数:
esp_sleep_enable_wifi_wakeup();
特点: 适用于需要 WiFi 远程唤醒的场景。
- RTC 看门狗定时器唤醒(RTC Watchdog Timer Wakeup)
描述: 通过 RTC 看门狗定时器唤醒设备。
相关函数:
esp_sleep_enable_rtc_watchdog_wakeup();
- SDIO 唤醒(SDIO Wakeup)
描述: 通过 SDIO 接口的信号变化唤醒设备。
相关函数:
esp_sleep_enable_sdio_wakeup();
特点: 适用于需要 SDIO 接口唤醒的场景。
- 组合唤醒源
描述: 可以同时启用多个唤醒源,设备在满足任意一个唤醒源条件时唤醒。
示例:
esp_sleep_enable_timer_wakeup(5000000); // 5秒后唤醒
esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 1); // GPIO12高电平唤醒
esp_deep_sleep_start(); // 进入深度睡眠
📗RTC 快速内存(RTC Fast Memory)用法
RTC快速内存是ESP32芯片中一块特殊的内存区域,即使在深度睡眠模式下也能保持供电,因此可以在唤醒时快速执行代码。esp32s3可访问最多 8 KB SRAM RTC 慢速内存,用于储存指令和数据.
- 使用方法一: 将函数放置在文件名以
rtc_wake_stub
开头的源文件中。这种命名约定会让编译器自动将这些文件中的内容链接到RTC内存中。
- 例程见:
v5.4\esp-idf\examples\system\deep_sleep_wake_stub
- 使用方法二: 在ESP-IDF中,
RTC_IRAM_ATTR
是一个特殊的属性宏,用于将函数或变量放置在 RTC 快速内存(RTC Fast Memory) 中。RTC 快速内存是一块特殊的内存区域,即使在深度睡眠模式下也能保持供电,因此适合存放需要在唤醒时立即执行的代码或数据。
#include "esp_sleep.h"
#include "esp_attr.h" // 包含 RTC_IRAM_ATTR 的定义
// 使用 RTC_IRAM_ATTR 将函数放置在 RTC 内存中
void RTC_IRAM_ATTR my_wake_stub_function() {
// 这里是唤醒后立即执行的代码
// 例如:读取 RTC 寄存器的值或执行一些低级别操作
printf("Wake stub function executed!\n");
}
void app_main() {
// 设置唤醒存根函数
esp_set_deep_sleep_wake_stub(&my_wake_stub_function);
// 进入深度睡眠
esp_deep_sleep_start();
}