跳转至

功耗控制

概要

本节内容为大家讲解ESP32的功耗控制方法。

ESP32工作频率

使用machine模块,你可以轻而易举的设置CPU的运行频率:

import machine

machine.freq()          # 查看当前的CPU运行频率
machine.freq(160000000) # 设置CPU运行频率至 160 MHz

ESP32默认工作在240MHz的主频下,它拥有三个可调的频率挡位:

  • 80MHz

  • 160MHz

  • 240MHz

越低的运行主频意味着性能的下降,但同时也意味着更低的功耗,适用于计算力需求低,长续航的任务。

反之,越高的主频,提升了性能的同时意味着更高的功耗。实际应用中,大家按照需求,自行取舍。

休眠模式

有时候我们需要使用休眠模式来为设备节省功耗,保证续航。

进入休眠模式

ESP32进入休眠模式后,除了RTC的时钟电路不掉电继续工作外,其余各电路模块都将掉电.

注意

进入休眠模式也意味着你将失去网络连接,无法连接到WebREPL和EMP-IDE甚至是通过串口连接的REPL,直到休眠模式被唤醒或者重启复位

import machine
machine.deepsleep() # 休眠,直到被人为唤醒
machine.deepsleep(5000) # 休眠5S

因此,当ESP32从休眠模式醒来时,相当于重启,不同之处在于:

  • 普通的重启RTC会恢复至初始点

  • 休眠时RTC不会被关闭,因此芯片醒来之后,RTC是正确的时间。

从休眠唤醒

当ESP32进入休眠模式后,很多时候我们希望能够人为的唤醒它。

我们有四种方式来唤醒ESP32:

  • RTC Timer 唤醒模式

  • EXT0 唤醒模式

  • EXT1 唤醒模式

  • TouchPad 唤醒模式

接下来,我们分别对前三种模式进行详细的阐述。

RTC Timer 唤醒模式

这是最简单的唤醒模式,这种方式其实我们上文的代码中就已经涉及:

import machine
machine.deepsleep() # 休眠,直到被人为唤醒
machine.deepsleep(5000) # 休眠5S

这里的deepsleep()函数本身可以接收一个休眠时间的参数,单位是毫秒,当该时间被计时器计时完成,便会触发唤醒。

RTC GPIO

在进行接下来的三种唤醒模式之前,你需要先了解什么是RTC GPIO

你可能并不清楚什么是RTC GPIO, 简而言之,RTC GPIO也属于GPIO,只不过他们特殊的地方在于,RTC GPIORTC时钟模块相连,因此在低功耗睡眠模式下,这些RTC GPIO才能够保证输入和输出信号能够被检测到,因此,只有RTC GPIO才能够唤醒ESP32

NodeMCU-32S上,GPIO 总共有32个。

ESP32上,RTC GPIO有18个,拥有自己的一套独立的RTC GPIO编号,0-17

NodeMCU-32S上,引出的RTC GPIO共有16个:

EXT0唤醒模式

EXT0唤醒模式,允许用户配置 一个 RTC GPIO, 来唤醒ESP32。

只有具备RTC GPIO功能的GPIO才能够被配置用以唤醒。

我们可以在GPIO上使用中断的方式来触发唤醒, 其中:

trigger有两种触发方式:

  • Pin.WAKE_HIGH 高电平触发

  • Pin.WAKE_LOW 低电平触发

我们以高电平触发唤醒为例(大部分引脚默认都是低电平,所以WAKE_LOW触发会立即执行),在GPIO27上接入一个按钮。

>>> from machine import Pin
>>> import machine
>>> wake_pin = Pin(27,Pin.IN) # GPIO27 支持 RTC GPIO
>>> wake_pin.irq(trigger=Pin.WAKE_LOW, wake=machine.DEEPSLEEP) # 配置唤醒中断
>>> machine.deepsleep() # 开始睡眠

当我按下这个按钮的时候,ESP32就苏醒(重启)了。

EXT1 唤醒模式

在这种模式下,允许用户配置一个或者多个RTC GPIO 作为唤醒源, 在一个或者多个 RTC GPIO 存在任意一个高电平或者同时为低电平时唤醒。

我们可以使用GPIO26 , GPIO27同时按下时将其唤醒。

按照上面的GPIO中断进行配置的话,正如在ESP32-MicroPython的Github仓库ISSUE里开发者们所讨论的那样

pin1.irq(trigger=Pin.IRQ_LOW_LEVEL, wake=machine.DEEPSLEEP) # configures ext0
pin2.irq(trigger=Pin.IRQ_HIGH_LEVEL, wake=machine.DEEPSLEEP) # configures ext1 with "any high"
pin3.irq(trigger=Pin.IRQ_HIGH_LEVEL, wake=machine.DEEPSLEEP) # reconfigures ext1 with "any high" with pin2 and pin3
pin4.irq(trigger=Pin.IRQ_LOW_LEVEL, wake=machine.DEEPSLEEP) # raises an exception, no resources left

我看完之后直接就懵圈了。

所以还好我们有另外一种方式进行配置——esp32模块:

import esp32
esp32.wake_on_ext1(pins=(...), level=...) # 这样就简单很多了

pins是一个元组,传入配置的多个RTC GPIO引脚

level 有以下两个选项:

  • WAKEUP_ANY_HIGH 任意一个RTC GPIO高电平即可唤醒

  • WAKEUP_ALL_LOW 所有RTC GPIO低电平时触发唤醒

笔者测试时使用 GPIO26(RTC GPIO7) GPIO27(RTC GPIO17)

使用WAKEUP_ANY_HIGN的选项进行测试(WAKEUP_ALL_LOW模式需要外部电路的支持,不然大部分引脚默认都是低电平,所以会立即触发唤醒),在这种模式下,我按下下图中的黑色或原谅色按钮,都可以唤醒

>>> import esp32
>>> import machine
>>> esp32.wake_on_ext1(pins=(Pin(26),Pin(27)),level=esp32.WAKEUP_ALL_LOW)
>>> machine.deepsleep()

判断重启和唤醒的原因

既然存在休眠被唤醒的情况,那么很多时候用户就需要知道当前重启是由于何种原因,以便决策启动之后的工作。

在machine模块中,包含了以下两个函数,可以让我们对重启和唤醒的原因进行侦查:

  • reset_cause 查看重启原因

  • wake_reason 查看唤醒原因

这些数值,与machine模块中的宏定义有着一一对应的关系:

重启原因的宏定义 数值 含义
PWRON_RESET 1 上电重启
HARD_RESET 2 硬重启
WDT_RESET 3 看门狗计时器重启
DEEPSLEEP_RESET 4 从休眠重启
SOFT_RESET 5 软重启
唤醒原因的宏定义 数值 含义
PIN_WAKE EXT0_WAKE 1 单个RTC_GPIO唤醒
EXT1_WAKE 2 多RTC_GPIO唤醒
TIMER_WAKE 3 定时器唤醒
TOUCHPAD_WAKE 4 触摸唤醒
ULP_WAKE 5 协处理器唤醒

目前为止,笔者在测试的过程中,发现我们无论是使用machine.reset()函数进行软重启,还是按开发板上的reset按键进行重启,machine.reset_cause()的数值都是5即对应machine.SOFT_RESET

当开发板从休眠模式唤醒后,重启原因是4machine.DEEPSLEEP_RESET

ESP32最常见的重启原因,无非也就以上两种了。

所以要想判断ESP32是从休眠中醒来还是正常的按键重启或是machine.reset()函数的重启,我们只需在开机后通过machine.reset_cause()函数来进行判断,如果是从睡眠中唤醒,那么进而可以继续判断唤醒的原因:

if machine.reset_cause() == machine.SOFT_RESET:
    print('normal reboot')
    # do something
elif machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('reboot from deepsleep')
    # 进而可以继续判断 是由于什么原因导致的唤醒
    if machine.wake_reason() == machine.PIN_WAKE:
        # do something
        pass
    elif machine.wake_reason() == machine.EXT1_WAKE:
        # do something else...
        pass
    # ......