ESP32 (Sleep Modes)-睡眠模式学习(8)

本文详细介绍了ESP32的Light-sleep和Deep-sleep两种睡眠模式,包括模式特点、唤醒源、电源管理以及相关API的使用。在Light-sleep模式下,CPU和大部分RAM仍保持供电,而Deep-sleep模式下则会切断大部分电源以实现低功耗。唤醒源包括定时器、GPIO、触摸传感器、ULP协处理器等,不同唤醒源有不同的配置方法和注意事项。代码示例展示了如何设置和使用这些唤醒源。最后,文章提到了在硬件设计、实验演示结果以及ESP32Sleep.h文件中的API使用情况。

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

提示:本博客作为学习笔记,有错误的地方希望指正

一、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,         //!< 在深度睡眠的情况下,复位不是由退出深度睡眠引起的 In case of deep sleep, reset was not caused by exit from deep sleep                                      
ESP_SLEEP_WAKEUP_ALL ,              //!< 没有一个唤醒源,esp_sleep_disable_wakeup_source 禁用睡眠模式唤醒源 Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source                                                      
ESP_SLEEP_WAKEUP_EXT0 ,             //!< 由外部信号RTC_IO引起的唤醒 Wakeup caused by external signal using RTC_IO              
ESP_SLEEP_WAKEUP_EXT1 ,             //!< 使用RTC_CNTL由外部信号引起的唤醒  Wakeup caused by external signal using RTC_CNTL                      
ESP_SLEEP_WAKEUP_TIMER ,            //!< 定时器引起的唤醒 Wakeup caused by timer       
ESP_SLEEP_WAKEUP_TOUCHPAD,          //!< touchpad引起的唤醒 Wakeup caused by touchpad       
ESP_SLEEP_WAKEUP_ULP ,              //!< ULP程序导致的唤醒 Wakeup caused by ULP program       
ESP_SLEEP_WAKEUP_GPIO ,             //!< GPIO导致的唤醒(仅为轻度睡眠) Wakeup caused by GPIO (light sleep only)                  
ESP_SLEEP_WAKEUP_UART ,             //!< UART引起的觉醒(仅轻度睡眠) Wakeup caused by UART (light sleep only)              
ESP_SLEEP_WAKEUP_WIFI ,             //!< WIFI引起的唤醒(仅限轻度睡眠) Wakeup caused by WIFI (light sleep only)                  
ESP_SLEEP_WAKEUP_COCPU ,            //!< 由COCPU引起的唤醒int Wakeup caused by COCPU int           
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG,   //!< 由COCPU崩溃引起的唤醒 Wakeup caused by COCPU crash           
ESP_SLEEP_WAKEUP_BT ,               //!< BT引起的醒来(仅轻度睡眠) Wakeup caused by BT (light sleep only)      

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);    //设置RTC电源域休眠模式的下电模式
gpio_pullup_dis(gpio_num);      //禁用GPIO上拉菜单。
gpio_pulldown_en(gpio_num);     //使能GPIO下拉功能。
⚠️警告
从睡眠模式中唤醒后,用于唤醒的 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);   //设置RTC电源域休眠模式的下电模式

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

/* "Boot" button is active low */
#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;    //配置默认唤醒GPIO    
    const int wakeup_level = BUTTON_WAKEUP_LEVEL_DEFAULT;   //设置唤醒电平

    gpio_config_t config = {
   
   
            .pin_bit_mask = BIT64(button_gpio_num),         //M5Stack 不能配置GPIO1 作为串口引脚,影响串口输出
            .mode = GPIO_MODE_INPUT,                        //输入模式
            .pull_up_en = 1,                                //使能上拉
    };
    ESP_ERROR_CHECK(gpio_config(&config));                  //配置GPIO
    gpio_wakeup_enable(button_gpio_num,wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);//设置gpio唤醒
	//esp32s3 需要有串口接收引脚的配置
    const int uart_rx_gpio_num = UART_RX_GPIO_NUM;          //配置默认唤醒GPIO    
    gpio_wakeup_enable(uart_rx_gpio_num,wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);//设置gpio唤醒 不能放置在初始化gpio之后,不然就配置成gpio唤醒了
    gpio_config_t gpio_uart_config = {
   
   
            .pin_bit_mask = BIT64(uart_rx_gpio_num),        //M5Stack 不能配置GPIO1 作为串口引脚,影响串口输出
            .mode = GPIO_MODE_INPUT,                        //输入模式
            .pull_up_en = 1,                                //使能上拉
    };
    ESP_ERROR_CHECK(gpio_config(&gpio_uart_config));        //配置GPIO
    //配置串口唤醒的一些参数 esp32 只需要以下串口配置就行了
    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,                        //设置时钟源 sleep模式的时候启用-->REF_TICK时钟源
    };
    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_wri

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值