定时器怎样做到“到点办事”?|单片机动画课 10

课程插图

第 04 课里,主循环用 millis() 看时间;第 09 课里,事件可以通过中断通知 CPU。现在把它们连接起来:如果有一块专门数节拍的硬件,到点后发通知,会发生什么?

这就是理解硬件定时器的入口。它数的是时钟带来的节拍,不是 CPU 执行了多少次循环。

本文先用通用计数示意,再用 STM32 常见的向上计数模式讲计算。GIF 非仿真、非实测;输入频率和寄存器数值为教学设定,不是某块 Blue Pill 的实测时钟配置。

时钟:稳定重复的节拍

可以把时钟想成不断重复的节拍源。定时器选择一个时钟来源,按规定的边沿更新计数。只要相关时钟和计数器还在运行,它通常不需要 CPU 每次亲自执行“加一”。

输入节拍经过四分频,每四拍产生一个计数节拍

有时输入节拍太快,直接计数很快就满了,于是先经过预分频器。图中每四个输入节拍,才让后面的计数增加一次,相当于把计数频率降到输入的四分之一。

四分频是这张示意图的设定,不是在说所有定时器只能除以四。芯片对分频范围、时钟来源、低功耗模式下是否继续运行,都有自己的规则。

数到上限,发生一次事件

再看第二张:计数器从 0、1、2,一直数到 7;下一个计数节拍到来时回到 0,并产生一次更新事件。

从零数到七,下一拍回到零并产生更新事件

为什么不是七拍?因为 0 到 7 一共有八个计数状态。这个“起点也占一个位置”的细节,是定时器计算里常见的差一错误。

更新事件可以用于触发中断或其他硬件动作,但具体要看是否启用了相应功能。硬件产生事件,不等于 CPU 已经完成了你想做的事情。 中断入口仍有响应延迟,主循环领取标志又可能更晚。

第 05 课的 PWM 也能和计数器联系起来:比较计数值与某个门限,就可以安排输出何时翻转。不过 PWM 有不同计数模式,本课的简单溢出公式不能不加条件地套到所有波形上。

一秒钟是怎样算出来的?

以 STM32 常见的向上计数、自动重装模式为例,暂不考虑重复计数器等额外机制。设定时器输入时钟为 f,预分频寄存器为 PSC,自动重装寄存器为 ARR。

计数节拍频率为 f/(PSC+1);每经过 ARR+1 个计数节拍,完成一轮更新。因此更新周期为:

T = (PSC + 1) × (ARR + 1) / f

假设一兆赫输入,先除一千,再数一千拍

在动画假设中,输入是 1MHz,PSC=999,得到 1kHz 的计数节拍,也就是每拍 1ms。再设 ARR=999,一轮是 1000 拍,得到 1 秒更新周期。

这不是让你把某块板的默认 CPU 主频直接代入。CPU 时钟、总线时钟和定时器输入时钟可能存在分频、倍频等关系;应先从芯片时钟树确认实际输入。

Arduino 的 STM32 HardwareTimer 库还会用参数格式帮助换算。比如库函数要求的分频“倍数”,不一定等于你直接写入 PSC 寄存器的数值。使用高层 API 时要先看它的参数定义。

一个常见误区

“硬件定时器到了点,回调函数就会毫无误差地立即执行。”硬件计数时刻、发出中断请求、CPU 开始执行 ISR、应用任务完成,是不同的时刻。

时钟自身也有误差。用一个并不精确的节拍源数得再认真,长期时间仍可能偏移。硬件定时改善了调度方式,并没有消除所有时间误差。

多个功能还可能共享一个定时器资源。改它的频率,可能同时影响 PWM 或其他库,因此不能看见一个可用 API 就认为资源互不相关。

看完再猜

在输入时钟和 ARR 不变时,把分频倍数从 1000 改成 2000,更新周期会怎样?

变为原来的两倍,因为计数节拍慢了一半。下一课我们观察另一种“停下来”:不是暂停程序,而是直接断电。程序和刚才保存的变量,会一起消失吗?

资料:STM32duino HardwareTimer、Espressif Timer API、Arduino AVR 计时实现。

发现更多好玩的开源项目

专注 Arduino、开源硬件、端侧 AI 和值得收藏的开发者工具。

课程插图

长按二维码关注「GitStar」

所有动画为教学原理示意,非芯片仿真、非硬件实测。网页发布:2026-09-21。