ESP32中的定时器

本人是初学者,非常愿意与各位多交流。

最近在看到定时器时,觉得还是先弄个简单版的描述,看起来比较有概念。

想象一下你在做饭:

  1. 你的大脑(ESP32的主CPU):正在忙着切菜、看菜谱。

  2. 厨房计时器(硬件定时器):你设定好“10分钟后提醒我关火”,然后就不用管它了,专心切菜。

  3. 时间到了(定时器中断):计时器“叮!”的一声响,提醒你该去关火了。你暂停切菜(当前任务),去关火(执行定时任务),关完火再回来继续切菜。

ESP32 的定时器工作原理几乎一模一样!

ESP32 定时器的核心概念

  1. 数量: ESP32 内部有 4 个独立的硬件定时器。想象成你有 4 个独立的厨房计时器(Timer0, Timer1, Timer2, Timer3)。你可以同时使用它们做不同的事情。

  2. 功能: 这些定时器主要有两大本领:

    • 计时/定时中断: 就像厨房计时器。你设定一个时间间隔(比如 1 秒),时间一到,它就“叮!”(产生一个中断),告诉 CPU:“嘿!时间到了!该干点啥了!” CPU 会暂时停下手头的工作,去执行你预先设定好的任务(比如改变一个LED灯的状态),执行完再回来继续原来的工作。

    • 计数: 它可以数外部发生的事情的次数(比如数一个按钮按了多少下),或者自己数内部的“嘀嗒”声。这更像是计数器功能,我们这里主要讲定时。

  3. 为啥不用delay() 初学者常用delay(1000)让程序停 1 秒。这就像你关火时,必须站在炉子前死死盯着 1 分钟,啥也不能干(CPU 被完全占用,不能做其他任务)。而定时器是后台计时,CPU 在等待期间可以做其他事情(比如切菜、读取传感器),效率高得多!

  4. 核心部件:

    • 时钟源: 就像计时器的电池。ESP32 定时器通常使用系统主时钟(一般是 80MHz,也就是每秒震动 8 千万次!)。

    • 预分频器: 这个很重要!80MHz 太快了。预分频器就像给这个超快速度“减速”。比如设置分频系数为 80,那么定时器实际“嘀嗒”一次的速度就变成了 80,000,000 Hz / 80 = 1,000,000 Hz(1MHz,每秒 100 万次)。

    • 计数器: 这是一个可以向上或向下数的数字。它每“嘀嗒”一次(经过分频后的速度)就加 1 或减 1。

    • 比较寄存器/自动重装载寄存器: 这是你设定的“目标值”。当计数器数到这个值(向上计数时)或者数到 0(向下计数时),定时器就“到时间了”,触发中断,并且计数器通常会自动重置回初始值,开始下一轮计时。

一个例子:用定时器让 LED 闪烁(不卡顿!)

目标: 让一个 LED 灯每秒闪烁一次(亮 0.5 秒,灭 0.5 秒),同时让 ESP32 的串口每秒打印一次“我在干活呢!”,证明主程序没有被闪烁 LED 的等待卡住。

如何用定时器实现?
  1. 选一个计时器: 比如我们用 Timer0

  2. 设定时间间隔: 我们想要 LED 每 0.5 秒改变一次状态(亮->灭 或 灭->亮)。所以定时器间隔 = 0.5 秒 (500 毫秒)

  3. 配置定时器:

    • 时钟源: 默认 80MHz (80,000,000 Hz)。

    • 预分频器: 设为 80。这样定时器计数频率 = 80,000,000 / 80 = 1,000,000 Hz (每微秒计数 1 次)。

    • 目标值: 我们需要定时器每 0.5 秒中断一次。0.5 秒 = 500,000 微秒。因为计数频率是 1MHz (1次/微秒),所以计数器数到 500,000 次就是 0.5 秒。这个 500000 就是我们要设置的目标值(自动重装载值)。

  4. 中断处理函数: 写一个小函数,专门处理定时器“叮!”的事件。这个函数要尽量短快!它只做一件事:设置一个标志位 timerFlag = true,告诉主程序“时间到了,该改变 LED 了!”(实际改变 LED 的操作放在主循环里,避免在中断里做耗时操作)。

  5. 启动定时器: 告诉 Timer0:“开始按我设定的参数计时吧!”

  6. 主程序循环:

    • 检查 timerFlag 是否为 true

    • 如果是 true,说明定时器中断发生了:

      • 把 timerFlag 设回 false (准备下次)。

      • 改变 LED 的状态 (如果灯是亮的就关掉,如果是灭的就打开)。

    • 不管 timerFlag 是什么,每秒打印一次消息(用 millis() 或另一个定时器实现,这里简化说明)。

    • 主循环可以做其他事情(比如读取传感器数据、处理网络连接等),它不会被 delay() 卡住

伪代码:
#include // 包含必要的库

// 定义LED引脚和状态变量
const int ledPin = 2; // 假设LED在GPIO2
bool ledState = false; // LED初始状态(灭)
volatile bool timerFlag = false; // 定时器中断标志

// 中断处理函数 (要简短!)
void IRAM_ATTR onTimer() {
  timerFlag = true; // 只是设置一个标志
}

void setup() {
  pinMode(ledPin, OUTPUT); // 设置LED为输出
  digitalWrite(ledPin, ledState); // 初始关闭LED

  // 1. 选择定时器 (Timer0)
  hw_timer_t *timer = timerBegin(0, 80, true); // 分频系数80, 向上计数

  // 2. 绑定中断处理函数
  timerAttachInterrupt(timer, &onTimer, true); // 边沿触发中断

  // 3. 设置目标值 (500ms)
  timerAlarmWrite(timer, 500000, true); // 目标值500000 (0.5秒), 自动重装载

  // 4. 启动定时器
  timerAlarmEnable(timer);

  Serial.begin(115200);
}

void loop() {
  // 检查定时器标志
  if (timerFlag) {
    timerFlag = false; // 清除标志
    ledState = !ledState; // 翻转LED状态
    digitalWrite(ledPin, ledState); // 更新LED
  }

  // 主循环可以自由地做其他事情,不会被闪烁LED卡住
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint >= 1000) {
    lastPrint = millis();
    Serial.println("我在干活呢!主循环没卡住!");
    // 这里还可以添加读取传感器、处理网络等代码...
  }

  // ... 其他任务代码 ...
}

关键优势总结

  1. 不卡顿: CPU 在等待定时器到期时可以继续执行 loop() 中的其他任务(打印消息、读传感器、连WiFi等),程序响应更灵敏。

  2. 精准: 硬件定时比软件循环 (millis()) 或 delay() 更精确稳定(尤其间隔很短时)。

  3. 多任务: 4 个定时器可以独立工作,实现多个不同周期的定时任务(比如一个 LED 每秒闪,另一个每 2 秒闪,一个传感器每 5 秒读一次)。

  4. 效率高: 让硬件去做计时的脏活累活,解放 CPU。

其他用途

除了让 LED 闪,定时器还能干很多事:

  • 产生精确的 PWM 信号控制电机速度或 LED 亮度。

  • 测量外部脉冲的宽度(比如超声波测距)。

  • 作为看门狗定时器,防止程序跑飞。

  • 实现软件串口。

  • 周期性采集传感器数据。

初学者小结:

  1. 从简单例子开始: 先理解并实现上面的 LED 闪烁例子。

  2. 理解分频和目标值: 计算时间间隔 (间隔(秒) = (分频系数 * 目标值) / 时钟源频率(Hz)) 是核心。

  3. 中断处理要短快! 在中断函数里只做最简单的事情(设置标志位),复杂的操作(开关设备、打印日志)放到 loop() 里根据标志位去做。

  4. 善用 volatile 在中断里修改、在主循环里读取的变量(如 timerFlag),一定要用 volatile 声明,告诉编译器这个变量可能随时被意外修改(由中断)。

  5. 查官方文档和库示例: Arduino core for ESP32 提供了 hw_timer.h 等库,有很多示例可以参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FightingFreedom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值