上一节我们介绍了 ESP32 的外部中断的使用,本节课介绍 ESP32 的定时器功能。
实验原理
定时器,顾名思义就是用来计时的,我们常常会设置计时或闹钟,然后时间到了就告诉我们要做什么。ESP32 也是这样,通过定时器可以完成各种预设好的任务。ESP32 定时器到达指定时间后也会产生中断,然后在回调函数内执行所需功能,这个和外部中断类似。
ESP32 内置 4 个 64-bit 通用定时器。每个定时器包含一个 16-bit 预分频器和一个 64-bit 可自动重新加载向上/向下计数器。
使用计时器的好处,是实现类似并行处理的功能,也就是一个应用里只能有一个 while True,两个以上都是不可以的,但是如果使用定时器,就可以同时运行多个 while True。
硬件电路设计
物料清单(BOM 表):
| 材料名称 | 数量 |
|---|---|
| 直插式 LED | 2 |
| 1kΩ 电阻 | 2 |
| 杜邦线(跳线) | 若干 |
| 面包板 | 1 |
LED 的正极接开发板的 D2、D4 引脚,并串联一个电阻,负极接 GND,如下图:

注意
一定要接电阻,不然会由于电流过大,烧坏 LED。
软件程序设计
1. 硬件定时器
在 ESP32 Arduino 开发环境中,可以使用以下几个库函数来配置和操作硬件定时器(Timer):
-
void timerBegin(timer_num_t timer_num, uint32_t divider, bool count_up):初始化硬件定时器,参数说明: -
timer_num:定时器编号,可选值为 0-3 等。 divider:定时器的分频系数,用于设置定时器的时钟频率。较大的分频系数将降低定时器的时钟频率。可以根据需要选择合适的值,一般设置为 80 即可;-
count_up:指定定时器是否为向上计数模式。设置为 true 表示向上计数,设置为 false 表示向下计数。 -
timerAttachInterrupt(hw_timer_t *timer, void (*isr)(void *), void *arg, int intr_type):用于将中断处理函数与特定的定时器关联起来,参数含义如下: -
timer;定时器指针; isr: 中断处理函数。arg: 传递给中断处理函数的参数。-
intr_type: 中断类型,可选值为 ture(边沿触发)或 false(电平触发)。 -
timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload):用于设置定时器的计数值,即定时器触发的时间间隔,参数含义如下: -
timer:定时器指针; alarm_value: 定时器的计数值,即触发时间间隔;-
autoreload: 是否自动重载计数值,可选值为 true(自动重载)或 false(单次触发)。 -
timerAlarmEnable(hw_timer_t *timer):用于启动定时器,使其开始计数; timerAlarmDisable(hw_timer_t *timer):用于禁用定时器,停止计数;timerGetAutoReload(hw_timer_t *timer):获取定时器是否自动重新加载;timerAlarmRead(hw_timer_t *timer):获取定时器计数器报警值;timerStart(hw_timer_t *timer):计数器开始计数;timerStop(hw_timer_t *timer):计数器停止计数;timerRestart(hw_timer_t *timer):计数器重新开始计数,从 0 开始;timerStarted(hw_timer_t *timer):计数器是否开始计数。
以上是一些常用的硬件定时器相关的库函数,你可以根据自己的需求和定时器的特性,调用适当的函数来配置和操作硬件定时器。请参考 ESP32 的官方文档和相关库的文档,以获取更详细的信息。
使用硬件定时器的基本步骤如下:
- 初始化定时器:使用
timerBegin()函数初始化所需的硬件定时器; - 注册中断处理函数:使用
timerAttachInterrupt()函数将中断处理函数与定时器关联起来; - 设置定时器模式:使用
timerAlarmWrite(),设置触发一次,还是周期性触发; - 启动定时器:使用
timerAlarmEnable()函数启动定时器,使其开始计数。
因此,我们的代码可以这么写:
#define LED 2
#define LED_ONCE 4
hw_timer_t *timer = NULL;
hw_timer_t *timer_once=NULL;
// 定时器中断处理函数
void timer_interrupt(){
digitalWrite(LED, !digitalRead(LED));
}
void timer_once_interrupt() {
digitalWrite(LED_ONCE, !digitalRead(LED_ONCE));
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(LED_ONCE, OUTPUT);
// 初始化定时器
timer = timerBegin(0,80,true);
timer_once = timerBegin(1, 80, true);
// 配置定时器
timerAttachInterrupt(timer,timer_interrupt,true);
timerAttachInterrupt(timer_once, timer_once_interrupt, true);
// 定时模式,单位us,只触发一次
timerAlarmWrite(timer,1000000,true);
timerAlarmWrite(timer_once, 3000000, false);
// 启动定时器
timerAlarmEnable(timer);
timerAlarmEnable(timer_once);
}
void loop() {
}
2. 软件定时器
使用软件计时器的时候,我们需要用到 ESP32 内置的库 Ticker,Ticker 是 ESP32 Arduino 内置的一个定时器库,这个库用于规定时间后调用函数。
接着我们来看看 Ticker 库的一些方法
detach():停止 Ticker;active():Ticker 是否激活状态,True 表示启用;once(n, callback,arg):n 秒后只执行一次 callback 函数,arg 表示回调函数的参数(不写表示没有);once_ms(n, callback,arg):n 毫秒后只执行一次 callback 函数,arg 表示回调函数的参数(不写表示没有);attach(n, callback, arg):每隔 n 秒周期性执行;attach_ms(n, callback, arg):每隔 n 毫秒周期性执行;
::: warning
不建议使用 Ticker 回调函数来阻塞 IO 操作(网络、串口、文件);可以在 Ticker 回调函数中设置一个标记,在 loop 函数中检测这个标记;
:::
#include <Ticker.h>
#define LED 4
#define LED_ONCE 2
// 定义定时器对象
Ticker timer;
Ticker timer_once;
// 定义定时器中断回调函数
void toggle(int pin) {
digitalWrite(pin, !digitalRead(pin));
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(LED_ONCE, OUTPUT);
// 配置周期性定时器
timer.attach(0.5, toggle, LED);
// 配置一次性定时器
timer_once.once(3, toggle, LED_ONCE);
}
void loop() {
}