定时器核心原理:时基单元与计数模式

大家好,我是你们的老朋友。今天咱们来啃定时器最核心的部分——时基单元。说实话,我刚开始学STM32时,被PSC、ARR、CNT这三个寄存器绕得晕头转向。后来在项目里调一个电机速度控制,才真正把它们搞明白。说白了,定时器就是个会自己数数的计数器,而这三个寄存器就是控制它怎么数、数到多少的核心。

时基单元三兄弟:PSC、ARR、CNT

先看一张整体结构图,帮你建立直观印象:

定时器时基单元结构图 时钟源 CK_PSC 预分频器 PSC 16位可编程 计数器 CNT 16位向上/向下 自动重装载 ARR 16位影子寄存器 更新事件 UE 溢出/重装载触发 计数过程 CNT从0向上计数 到达ARR后归零 产生更新事件

这张图把三个核心寄存器串起来了。我来逐个拆解:

1. 预分频器 PSC(Prescaler)

PSC的作用,就是把输入时钟频率降下来。比如你给定时器喂了72MHz的时钟,但你想让计数器1秒加一次,怎么办?用PSC分频。

公式很简单:计数器时钟频率 = 输入时钟 / (PSC + 1)

举个例子:72MHz时钟,PSC设为7199,那么计数器时钟就是 72M / 7200 = 10kHz。也就是每0.1ms计数器加1。

我的习惯:配置PSC时,我一般先确定目标分辨率。比如做PWM控制电机,需要1μs的精度,那就让计数器时钟为1MHz,PSC = 72M/1M - 1 = 71。这样每个计数步就是1μs,算起来特别方便。

2. 自动重装载寄存器 ARR

ARR决定了计数周期。计数器从0开始,一路加到ARR,然后归零,同时触发更新事件。

定时周期公式:定时时间 = (ARR + 1) × (PSC + 1) / 输入时钟频率

还是刚才的例子:PSC=7199,计数器时钟10kHz,ARR设为9999,那么定时周期 = (9999+1) × 0.1ms = 1秒。完美实现1秒定时。

注意影子寄存器:ARR和PSC都有影子寄存器。你写入的值不会立即生效,要等到更新事件发生后才会加载到工作寄存器。我曾经在这里栽过跟头——写了个PSC值,以为立刻生效,结果计数器还在用旧值跑了半天。解决办法是手动产生更新事件,或者等溢出自动更新。

3. 计数器 CNT

CNT就是那个一直在数数的家伙。它从0开始,每个时钟脉冲加1,加到ARR后归零。你可以随时读取CNT的值,了解当前计数位置。

寄存器 位数 作用 我常用的值
PSC 16位 分频输入时钟 71(1MHz分辨率)
ARR 16位 设定计数上限 9999(1ms周期)
CNT 16位 当前计数值 只读,调试用

时钟源选择

定时器的时钟从哪来?STM32给了你几个选择:

  • 内部时钟(CK_INT):最常用,来自APB总线时钟。一般就是72MHz或36MHz。
  • 外部时钟模式1(TIx):从定时器的输入引脚捕获外部信号。我在做频率计项目时用过这个模式。
  • 外部时钟模式2(ETR):通过专门的ETR引脚输入。适合外部脉冲计数。
  • 内部触发(ITRx):一个定时器的输出作为另一个定时器的输入。级联定时器时很有用。

我个人90%的场景都用内部时钟。只有在需要外部信号同步或测量外部频率时,才会切换到外部模式。

三种计数模式

STM32定时器支持三种计数模式,每种都有它的用武之地:

向上计数模式

CNT从0开始,每个时钟脉冲加1,加到ARR后归零,产生更新事件。这是最常用的模式,适合做周期性定时、PWM生成等。

代码配置示例:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数
TIM_TimeBaseStructure.TIM_Period = 9999;                     // ARR值
TIM_TimeBaseStructure.TIM_Prescaler = 7199;                  // PSC值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     // 时钟分频
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

向下计数模式

CNT从ARR开始,每个时钟脉冲减1,减到0后重新加载ARR,产生更新事件。这个模式在某些特殊场景下有用,比如需要从高往低计数的场合。

中央对齐模式

这个模式有点意思。CNT先向上计数到ARR,然后向下计数到0,如此反复。一个完整周期包含两次溢出。这种模式在生成对称PWM波形时特别有用,比如电机控制中的互补PWM。

避坑指南:我曾经在中央对齐模式下配置中断,结果发现中断频率是预期的两倍。为什么?因为中央对齐模式在向上溢出和向下溢出时都会产生更新事件。如果你只需要一半的中断频率,记得在中断服务函数里判断计数方向。
计数模式 计数方向 更新事件触发点 典型应用
向上计数 0 → ARR 到达ARR时 普通定时、PWM
向下计数 ARR → 0 到达0时 特殊计数需求
中央对齐 0 → ARR → 0 到达ARR和0时 对称PWM、电机控制

实战经验:如何快速计算定时参数

每次配置定时器,我脑子里都会快速过一遍这个流程:

  1. 确定目标定时周期:比如我要1ms的定时中断。
  2. 选择合适的分辨率:1ms周期,分辨率1μs就够用了。所以计数器时钟选1MHz。
  3. 计算PSC:PSC = 72M / 1M - 1 = 71。
  4. 计算ARR:ARR = 1ms / 1μs - 1 = 999。

搞定。就这么简单。你想想看,只要掌握了这个公式,任何定时需求都能快速算出来。

我的小技巧:调试时我喜欢在中断服务函数里翻转一个GPIO,用示波器看波形。如果波形周期不对,先检查PSC和ARR的值,再检查时钟源配置。90%的问题都出在这两个地方。