定时器中断
上一节我们介绍了 ESP32 的外部中断的使用,本节课介绍 ESP32 的定时器功能。
实验原理
定时器,顾名思义就是用来计时的,我们常常会设置计时或闹钟,然后时间到了就告诉我们要做什么。ESP32 也是这样,通过定时器可以完成各种预设好的任务。ESP32 定时器到达指定时间后也会产生中断,然后在回调函数内执行所需功能,这个和外部中断类似。
在 Arduino 中操控 ESP32 时,有 硬件定时器
和 软件定时器
两种类型的定时器可供选择。它们具有不同的工作原理和用途。
硬件定时器
是 ESP32 芯片上的内置计时器,它们是专门设计用于定时和计时任务的硬件模块。硬件定时器可以通过设置特定的寄存器来配置和控制,通常具有更高的精确度和稳定性。它们不受软件的影响,可以在后台独立运行,不会受到其他代码的干扰。硬件定时器适用于需要高精度和实时性的定时任务,例如 PWM 输出、捕获输入脉冲等。
软件定时器
是通过编写代码在 Arduino 中模拟实现的定时器。它们不依赖于硬件模块,而是使用计数器变量来实现定时功能。软件定时器是基于延时循环的原理,在特定的时间间隔内执行特定的任务。但是,使用软件定时器时需要注意,它们可能会受到其他代码的影响而产生误差,特别是当涉及到需要精确时间控制的应用时,如通信协议处理、高速数据采集等。
硬件定时器和软件定时器各有优劣,具体选择取决于你的应用需求。如果需要高精度、实时性和稳定性,建议使用硬件定时器。如果时间精度要求不高,或者只需要基本的定时功能,可以使用软件定时器来简化代码编写。
需要注意的是,ESP32 具有 4 个硬件定时器,具体使用哪个定时器取决于你的需求和硬件资源的可用性。请参考 ESP32 的官方文档和相关库的文档以获取更详细的信息和使用示例。
硬件电路设计
物料清单(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 毫秒周期性执行;
注意
不建议使用 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() {
}