定时器基础:STM32定时器家族

说到定时器,我刚开始学STM32时也觉得挺简单的——不就是计时嘛。后来做项目才发现,这玩意儿的水比想象中深得多。你想想看,一个电机控制项目,PWM频率不对,电机就嗡嗡响;一个脉冲采集项目,输入捕获没配好,数据全乱套。嗯,今天我们就把它彻底搞明白。

STM32定时器家族

STM32的定时器分三类:基本定时器、通用定时器、高级定时器。它们的关系有点像汽车——基本型是代步车,通用型是家用SUV,高级型就是带越野套装的顶配版。

类型 典型型号 主要功能 适用场景
基本定时器 TIM6、TIM7 基础计时、触发DAC 定时中断、软件延时
通用定时器 TIM2~TIM5 PWM、输入捕获、输出比较 电机控制、信号测量
高级定时器 TIM1、TIM8 互补PWM、刹车功能、死区插入 三相电机、电源变换

核心区别一句话:基本定时器只能向上计数;通用定时器可以向上/向下/中央对齐计数;高级定时器在通用基础上多了互补输出和刹车控制。

我个人习惯,做普通LED呼吸灯用通用定时器就够了。但如果你要驱动无刷电机,必须上高级定时器——它的死区插入功能是硬件级的,软件模拟根本做不到那么精准。

定时器时钟源与时基单元

定时器要工作,首先得有时钟。STM32的定时器时钟源有几种选择:

  • 内部时钟(CK_INT)——来自APB总线,最常用
  • 外部时钟模式1(TIx)——从GPIO引脚输入
  • 外部时钟模式2(ETR)——专用外部触发引脚
  • 内部触发(ITRx)——其他定时器级联

我在项目中遇到过一个问题:用外部脉冲计数时,信号频率太高导致漏计数。后来查手册才发现,外部时钟模式1的最大频率不能超过APB时钟的1/4。这个坑,我踩过一次就记住了。

时基单元是定时器的核心,它由三个寄存器组成:

  • 预分频器(PSC)——对时钟分频,16位,范围0~65535
  • 自动重装载寄存器(ARR)——决定计数周期
  • 计数器(CNT)——当前计数值

计算公式很简单:

定时频率 = 时钟频率 / (PSC + 1) / (ARR + 1)
定时周期 = 1 / 定时频率

举个例子,APB时钟72MHz,要产生1ms中断:

PSC = 71  // 72MHz / (71+1) = 1MHz
ARR = 999 // 1MHz / (999+1) = 1kHz → 1ms

小技巧:PSC和ARR都是16位寄存器,最大值65535。如果你需要很长的定时时间,可以考虑用定时器级联——把两个定时器串起来用。我做过一个项目需要定时1小时,就是用TIM2和TIM5级联实现的。

PWM与输入捕获概念

这两个功能是定时器最常用的外设接口。说白了,PWM是定时器往外发信号,输入捕获是定时器往里收信号。

PWM(脉冲宽度调制)

PWM的核心思想:通过调节高电平占整个周期的比例(占空比),来模拟不同的电压值。频率固定,占空比可变。

STM32的PWM生成原理:

  • 计数器CNT从0开始向上计数
  • 当CNT < CCR(捕获比较寄存器)时,输出高电平
  • 当CNT >= CCR时,输出低电平
  • CNT达到ARR后归零,重新开始

占空比 = CCR / (ARR + 1) × 100%

// LL库配置PWM示例
LL_TIM_EnableCounter(TIM2);
LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_SetCompareCH1(TIM2, 500);  // 50%占空比(假设ARR=999)

注意:修改占空比时,一定要在更新事件之后修改,否则会出现输出异常。我曾经在中断里直接改CCR,结果波形乱跳了半个周期。正确的做法是等定时器溢出中断标志位置位后再修改。

输入捕获

输入捕获是用来测量外部信号的。它可以捕获到外部信号的上升沿或下降沿,并记录下当时的CNT值。通过两次捕获的差值,就能算出信号的周期或脉宽。

典型应用场景:

  • 测量PWM信号的频率和占空比
  • 编码器转速测量
  • 超声波测距(测量回波脉宽)

输入捕获的配置步骤:

  1. 设置GPIO为复用功能输入
  2. 配置定时器的捕获通道(选择上升沿/下降沿)
  3. 使能捕获中断
  4. 在中断中读取CCR值并计算
// LL库配置输入捕获示例
LL_TIM_IC_Config(TIM3, LL_TIM_CHANNEL_CH1, 
                 LL_TIM_ACTIVEINPUT_TI1FP1,
                 LL_TIM_IC_POLARITY_RISING,
                 LL_TIM_IC_PRESCALER_DIV1,
                 LL_TIM_IC_FILTER_FDIV1);
LL_TIM_EnableIT_CC1(TIM3);
LL_TIM_EnableCounter(TIM3);

经验之谈:输入捕获的滤波器参数很关键。如果信号有毛刺,建议开启数字滤波(ICF字段)。我做过一个项目,编码器信号线长了之后有抖动,开了4级滤波就稳了。但滤波级别越高,信号延迟越大,这个要权衡。

定时器知识体系总览

下面这张图把本章的核心知识点串起来了,你可以对照着理解:

STM32定时器知识体系 基本定时器 通用定时器 高级定时器 时基单元 预分频器 PSC 自动重装载 ARR 计数器 CNT PWM输出 频率固定 · 占空比可调 输入捕获 测量频率 · 测量脉宽 应用场景 LED调光 · 电机控制 · 信号测量 · 编码器 · 超声波测距

这张图把定时器从分类到时基单元,再到PWM和输入捕获,最后到应用场景的完整链路展示出来了。你写代码的时候,脑子里要有这张图——知道自己现在配置的是哪个模块,它跟其他模块是什么关系。

我的建议:刚开始学的时候,别急着把所有功能都试一遍。先拿通用定时器做一个PWM呼吸灯,再做一个输入捕获测频率。这两个实验做通了,定时器这块你就入门了。剩下的高级功能,遇到具体项目再查手册也来得及。