定时器本质上就是个计数器。输入的是 CPU 的时钟信号,预分频器 PSC 会执行第一重计数降低频率。预分频器的输出信号会被送入计数器 CNT,计数器溢出会触发更新事件,完成一次定时器触发。
输入时钟 → 预分频器(PSC) → 计数器(CNT) → 自动加载寄存器(ARR) → 溢出触发更新事件
注:更新事件的产生是硬件行为,软件想感知它还需要使能中断。
ARR 保存的是计数上限,也就是计数到 ARR+1 就会溢出,触发事件。也就是说:
定时器触发周期 = (ARR + 1) × (PSC + 1) / 时钟频率
公式拆解
为什么是 +1 值而不是原值? 比如 ARR + 1 而不是 ARR。
这是因为硬件的工作方式是"输入 N+1 个信号,输出 1 个信号"。比如 PSC 是 0,那每次输入都会触发一次输出,因为每一次计数都是溢出;PSC 是 1,那每两次输入才会溢出一次:0 → 1 → 溢出。
时钟1: CNT=0 (等于ARR,还没溢出)
时钟2: CNT 要变成1 → 但 ARR 限制了 → 溢出!CNT 重置为 0,触发更新事件
→ 溢出发生在试图超过 ARR 的时候
注:要小心把 ARR 设为 0。向上计数模式下,这可能导致不确定行为,一般建议 ARR ≥ 1。
而且,计数是从 0 开始的,所以数到 999 的时候,实际上也就计数了 1000 次。
为什么是 (ARR + 1) × (PSC + 1)?
其实可以把 PSC 理解成另一重计数器,或者说是前置计数器。两个计数器叠加,可以有效降低系统收到的触发频率。
举个例子,如果时钟是 72MHz,我要 1ms 的触发,可以这样设置:PSC 设为 71,这样信号到 ARR 就是 1MHz 了;但是目标频率是 1KHz,所以 ARR 要设置为 (1MHz / 1KHz) - 1 = 1000 - 1 = 999。
代入公式就是:
1/1000 s = (999 + 1) × (71 + 1) / 72MHz
值的更新
PSC 值默认情况下是自动装载:更新值写入影子寄存器,等待下一个更新事件才生效。通过设置 UG 位,可以立即触发更新事件,让 PSC 值马上生效。此外,从模式控制器(如复位模式、门控模式)也能触发更新事件。
PSC 和 ARR 都有影子寄存器,只是 PSC 的影子寄存器始终启用(无法关闭,写入后等待更新事件生效),ARR 的影子寄存器可选:
ARPE=1时启用:写 ARR 需要等更新事件生效ARPE=0时禁用:写 ARR 立即生效
计数中途改变溢出值可能会导致定时不准,为了安全起见,推荐尽量使用影子寄存器。连起来看就是:
计数器当前溢出值是 a → 写入新值 b 到影子寄存器 → 计数器计数递增到 a+1 → 溢出,触发计数器溢出值更新到 b、计数值重置为 0
溢出之后,在同一个时钟周期内就会自动完成置 0:
时钟周期: [N] [N+1] [N+2]
CNT: ARR → 0 → 1 → ...
↑
溢出检测
重载为 0
计数方向
除了常用的向上计数,还支持:
- 向下计数:从 ARR 倒数到 0
- 中央对齐:先向上再向下,适合电机 PWM
注:PSC 是没有计数方向的,它也不需要,因为其唯一作用就是分频。