1概述
ESP32-C3微控制器有多个定时器,这些定时器可用于各种用途,包括计时、生成PWM信号、测量输入信号的频率等。以下是ESP32-C3上可用的定时器资源:
-
两个硬件定时器:
- 定时器0:这是一个通用定时器,通常用于操作系统的任务调度。
- 定时器1:同样是一个通用定时器,也可以用于其他目的。
-
八个LED控制定时器:
- 这些定时器专门用于LED控制,例如,它们可以用来生成PWM信号来控制LED的亮度。
-
两个I2S DMA定时器:
- 这些定时器用于I2S(Inter-IC Sound)接口的DMA(Direct Memory Access)操作。
-
一个RMT(Remote Control)定时器:
- RMT定时器用于生成和接收红外遥控信号。
这些定时器资源在ESP32-C3上的分配和功能可能会根据具体的应用和ESP-IDF(Espressif IoT Development Framework)的版本有所不同。硬件定时器(定时器0和定时器1)通常用于最关键的计时任务,而其他定时器则可用于特定外设的控制。
2实现定时器的启停(1秒)
#include <Arduino.h>
hw_timer_t *timer = NULL;
volatile int seconds = 0;
volatile bool printFlag = false; // 添加一个标志来指示是否需要打印
// 定时器中断服务例程
void IRAM_ATTR tim1Interrupt() {
seconds++; // 每次中断增加秒数
printFlag = true; // 设置打印标志
}
void setup() {
Serial.begin(115200);
timer = timerBegin(0, 80, true); // 初始化定时器
timerAttachInterrupt(timer, &tim1Interrupt, true); // 绑定中断函数
timerAlarmWrite(timer, 1000000, true); // 设置报警值为 1 秒
timerAlarmEnable(timer); // 启用定时器中断
}
void loop() {
if (printFlag) {
Serial.print("Seconds passed: ");
Serial.println(seconds);
printFlag = false; // 重置打印标志
}
}
3注意串口不能放在定时器中断内部
,放进去会产生内存溢出
-
串行缓冲区溢出:如果在
Serial.print
或Serial.println
调用之间有太多的数据要发送,而串行通信速度跟不上,串行缓冲区可能会溢出,导致数据丢失或混乱。 -
中断处理和串行通信冲突:由于
Serial.print
和Serial.println
不是原子操作,如果在它们执行过程中发生中断,可能会导致输出不完整或混乱。 -
其他代码或库的干扰:如果在您的程序中还有其他代码或库使用串行端口进行通信,它们可能会与您的打印语句冲突。
4重点解释
-
timerBegin(0, 80, true);
-
timerBegin
函数用于初始化定时器,并返回一个定时器句柄。 - 第一个参数
0
指定要使用的定时器组。ESP32-C3 有两个定时器组,每个组有两个定时器。这里选择定时器组0。 - 第二个参数
80
是预分频器的值。定时器的时钟源通常是 APB(Advanced Peripheral Bus)时钟,预分频器用于降低时钟频率。预分频器值设置为80,意味着定时器的时钟频率是 APB 时钟的 1/80。 - 第三个参数
true
表示定时器是向上计数的,即从0开始,每次计数增加,直到达到设定的报警值。
-
-
timerAttachInterrupt(timer, &tim1Interrupt, true);
-
timerAttachInterrupt
函数用于将中断服务例程(ISR)与定时器中断关联起来。 - 第一个参数
timer
是由timerBegin
返回的定时器句柄。 - 第二个参数
&tim1Interrupt
是指向中断服务例程的指针。当定时器达到报警值时,将调用此函数。 - 第三个参数
true
表示在中断服务例程执行期间,定时器中断应该被禁用。这是为了避免在中断服务例程执行期间再次进入中断。
-
-
timerAlarmWrite(timer, 1000000, true);
-
timerAlarmWrite
函数用于设置定时器的报警值,即定时器达到该值时触发中断。 - 第一个参数
timer
是定时器句柄。 - 第二个参数
1000000
是定时器的报警值,单位是定时器时钟周期的个数。由于定时器时钟频率是 APB 时钟的 1/80,因此 1000000 个周期大约等于 1 秒(假设 APB 时钟频率为 80MHz)。 - 第三个参数
true
表示报警值设置后,定时器会自动重载并重新开始计数,从而实现周期性中断。
-
-
timerAlarmEnable(timer);
-
timerAlarmEnable
函数用于启用定时器中断。 - 参数
timer
是定时器句柄。 - 一旦启用,定时器开始计数,当达到设定的报警值时,将调用之前关联的中断服务例程。
-
时间计算
以下是计算步骤:
-
确定 APB 时钟频率: ESP32-C3 的 APB 时钟频率默认为 80 MHz(即每秒 80,000,000 周)。
-
应用预分频器: 您设置的预分频器值为 80,这意味着定时器时钟频率是 APB 时钟频率的 1/80。
定时器时钟频率 = APB 时钟频率 / 预分频器值 定时器时钟频率 = 80 MHz / 80 = 1 MHz (即每秒 1,000,000 周)
-
计算定时器计数周期: 您设置的报警值为 1,000,000,这是定时器达到后触发中断的计数值。
计时时间 = 报警值 / 定时器时钟频率 计时时间 = 1,000,000 / 1,000,000 = 1 秒
1分钟
#include <Arduino.h>
hw_timer_t *timer = NULL;
volatile int minutes = 0;
volatile bool printFlag = false; // 添加一个标志来指示是否需要打印
// 定时器中断服务例程
void IRAM_ATTR tim1Interrupt() {
minutes++; // 每次中断增加分钟数
printFlag = true; // 设置打印标志
}
void setup() {
Serial.begin(115200);
timer = timerBegin(0, 80, true); // 初始化定时器
timerAttachInterrupt(timer, &tim1Interrupt, true); // 绑定中断函数
timerAlarmWrite(timer, 60000000, true); // 设置报警值为 60 秒(即 1 分钟)
timerAlarmEnable(timer); // 启用定时器中断
}
void loop() {
if (printFlag) {
Serial.print("Minutes passed: ");
Serial.println(minutes);
printFlag = false; // 重置打印标志
}
}
30分钟
#include <Arduino.h>
hw_timer_t *timer = NULL;
volatile int minutes = 0;
volatile bool printFlag = false; // 添加一个标志来指示是否需要打印
// 定时器中断服务例程
void IRAM_ATTR tim1Interrupt() {
minutes += 30; // 每次中断增加30分钟数
printFlag = true; // 设置打印标志
}
void setup() {
Serial.begin(115200);
timer = timerBegin(0, 80, true); // 初始化定时器
timerAttachInterrupt(timer, &tim1Interrupt, true); // 绑定中断函数
timerAlarmWrite(timer, 1800000000, true); // 设置报警值为 1800 秒(即 30 分钟)
timerAlarmEnable(timer); // 启用定时器中断
}
void loop() {
if (printFlag) {
Serial.print("Minutes passed: ");
Serial.println(minutes);
printFlag = false; // 重置打印标志
}
}