STM32学习笔记(七)TIM定时中断(上)

STM32学习笔记(七)TIM定时中断(上)

本文以 STM32F103C8T6、STM32F10x 标准外设库和常见最小系统板为例,介绍 STM32 定时器的基本结构、工作时序和时钟来源,为后续使用定时器产生周期中断打下基础。

在单片机项目中,延时函数虽然使用简单,但执行延时时 CPU 往往只能原地等待。定时器则像一只独立运行的闹钟:CPU 设定好计时时间后可以继续处理其他任务,时间到达时再由定时器通过中断通知 CPU。因此,定时中断是实现按键扫描、传感器采样、界面刷新和任务调度等周期性工作的常用方法。

一、定时器概述

1、什么是定时器

TIM 是定时器(Timer)的缩写。定时器会按照一定频率接收时钟脉冲,并累计脉冲数量。当计数值达到预先设定的条件时,定时器可以产生更新事件、中断请求或硬件触发信号。

可以把定时器理解成一名按照节拍数数的计数员:时钟每跳动一次,它就改变一次计数值;数到规定的终点后,它会发出通知,并根据配置开始下一轮计数。

定时器的用途并不局限于延时和周期中断。功能完整的定时器还可以:

  • 选择内部时钟或外部信号作为计数来源;
  • 测量输入信号的周期、频率和脉冲宽度;
  • 通过输出比较产生指定时刻的电平变化;
  • 输出频率和占空比可调的 PWM 波形;
  • 使用编码器接口读取旋转编码器;
  • 通过主从触发系统让多个定时器或外设同步工作。

2、时基单元

定时器最核心的部分称为时基单元,主要由以下三个器件组成:

  • PSC:预分频器,用于降低输入时钟频率;
  • CNT:计数器,用于累计分频后的时钟脉冲;
  • ARR:自动重装寄存器,用于规定一轮计数的终点。

三者的基本关系如下:

text 复制代码
定时器时钟
    ↓
PSC 预分频器
    ↓
CNT 计数器
    ↓
与 ARR 比较并产生更新事件
    ↓
触发中断、输出触发信号或重新开始计数

如果把定时器比作一块机械手表,输入时钟就像不断摆动的摆轮,PSC 是负责减速的齿轮组,CNT 是不断向前走动的指针,ARR 则是提前标出的终点刻度。

需要注意,向上计数时并不是 CNT 一达到 ARR 就立刻清零。CNT 会先计到 ARR,在下一个有效计数脉冲到来时从 ARR 跳回 0,并在这个过程中产生溢出和更新事件。因此,从 0 数到 ARR 一共包含 ARR + 1 个计数状态。

3、定时时间的计算

预分频器同样从 0 开始计数,所以实际分频系数不是 PSC,而是 PSC + 1。计数器得到的时钟频率为:

text 复制代码
f_CNT = f_TIM ÷ (PSC + 1)

向上计数时,计数器从 0 数到 ARR,一共经历 ARR + 1 个计数脉冲,因此更新事件的周期为:

text 复制代码
T = (PSC + 1) × (ARR + 1) ÷ f_TIM

例如,定时器时钟为 72MHz,设置 PSC = 7199ARR = 9999

text 复制代码
计数频率 = 72MHz ÷ 7200 = 10kHz
计数周期 = 1 ÷ 10kHz = 0.1ms
更新时间 = 10000 × 0.1ms = 1s

计算过程可以分成两步:首先用 PSC72MHz 降为 10kHz,此时 CNT 每隔 0.1ms 变化一次;随后让 CNT 完成 10000 次计数,最终得到 1s 的更新周期。

16 位预分频器和 16 位自动重装寄存器的最大值都是 65535。二者均取最大值,并且定时器时钟为 72MHz 时,最长更新周期约为:

text 复制代码
Tmax = 65536 × 65536 ÷ 72MHz
     ≈ 59.65s

这里的 72MHz 必须是实际进入定时器的时钟频率。定时器时钟与 APB 总线的分频设置有关,计算前应先从时钟树中确定 TIMxCLK,不能看到系统主频是 72MHz 就直接代入。


二、定时器分类与框图阅读方法

1、定时器的分类

STM32 的定时器按照功能复杂度,可以分为高级定时器、通用定时器和基本定时器。不同芯片型号拥有的定时器数量不同,下表中的编号表示 STM32F1 系列中常见的资源,并不代表每一款芯片都全部具备。

定时器类型 STM32F1 系列常见编号 所属总线 主要功能
高级定时器 TIM1TIM8 APB2 包含通用定时器的大部分功能,并增加重复计数、互补输出、死区生成和刹车保护
通用定时器 TIM2TIM3TIM4TIM5 APB1 支持内外部时钟、输入捕获、输出比较、PWM、编码器接口和主从触发
基本定时器 TIM6TIM7 APB1 主要用于定时中断和触发 DAC,不具备连接 GPIO 的输入输出通道

STM32F103C8T6 实际拥有 4 个定时器:

  • TIM1:高级定时器;
  • TIM2TIM3TIM4:通用定时器;
  • 该型号没有 TIM5TIM6TIM7TIM8

因此,在 STM32F103C8T6 上进行普通定时、输入捕获或 PWM 输出时,通常使用 TIM2~TIM4;需要互补 PWM、死区和硬件刹车保护时,则使用 TIM1

2、三类定时器的功能对比

三类定时器不是三套毫无关系的结构,而是在同一条计数主线上逐级增加功能:基本定时器保留时基和触发功能;通用定时器增加输入、输出、编码器和同步控制;高级定时器又增加面向电机驱动的输出与保护功能。

功能 基本定时器 通用定时器 高级定时器
PSC + CNT + ARR 时基单元 支持 支持 支持
定时更新中断 支持 支持 支持
TRGO 触发其他外设 支持 支持 支持
外部时钟 不支持 支持 支持
输入捕获 不支持 支持 支持
输出比较与 PWM 不支持 支持 支持
编码器接口 不支持 支持 支持
完整的从模式控制 不支持 支持 支持
重复计数器 不支持 不支持 支持
互补输出与死区 不支持 不支持 支持
刹车保护 不支持 不支持 支持

选择定时器时,应先判断任务需要什么功能:

  • 只需要周期中断或 DAC 触发,可以使用基本定时器;
  • 需要测量输入信号、输出 PWM、读取编码器或使用外部时钟,应使用通用定时器;
  • 需要驱动电机、输出互补 PWM、插入死区或使用硬件刹车保护,应使用高级定时器。

基本定时器虽然能够输出 TRGO 并作为触发源,但没有通用定时器中的完整从模式控制器。因此,将它描述为"主从触发功能较少"不够准确,更合适的说法是:它可以向外提供触发信号,但不支持通用定时器那样完整的从模式。

3、阅读定时器框图的方法

定时器框图中的线路很多,第一次看到很容易无从下手。阅读时可以采用"先主干,后支路"的方法:

  1. 找到时钟从哪里进入定时器;
  2. 沿着 PSC → CNT → ARR 找到时基单元;
  3. 查看计数器在什么条件下产生更新事件;
  4. 再分析 CH1~CH4 的输入捕获和输出比较通道;
  5. 最后查看编码器、主从触发、互补输出和刹车等附加功能。

这就像阅读一张复杂的地铁图:先认清贯穿全图的主线路,再研究两侧的换乘支线,原本交错的线路就会逐渐清晰。

框图中的常见缩写如下:

缩写 含义
CK_INT 来自 RCC 的内部定时器时钟
CK_PSC 进入预分频器之前的时钟
CK_CNT 经过预分频后驱动计数器的时钟
PSC 预分频器
CNT 计数器
ARR 自动重装寄存器
RCR 重复计数器,高级定时器特有
TIx 第 x 路定时器输入信号
ICx 第 x 路输入捕获通道
OCx 第 x 路输出比较信号
CCRx 第 x 路捕获/比较寄存器
ETR 外部触发输入
ITR 来自其他定时器的内部触发输入
TRGO 向其他定时器或外设输出的触发信号
UEV 更新事件
UIF 更新中断标志

4、三类定时器的共同主线

三类定时器都可以归结为"选择时钟---分频---计数---比较---产生事件"这条主线:

text 复制代码
选择内部或外部时钟
        ↓
PSC 将时钟分频
        ↓
CNT 按照指定方向计数
        ↓
ARR 决定计数周期
        ↓
CCRx 完成捕获或比较
        ↓
产生更新、捕获、比较或触发事件
        ↓
申请中断、请求 DMA、输出 PWM 或触发其他外设

其中,CCRx 和输入输出通道只存在于通用定时器与高级定时器中。基本定时器只保留时基和触发部分;通用定时器在主线两侧增加输入、输出和同步功能;高级定时器再为输出端增加重复控制、互补驱动、死区和故障保护。掌握这条共同主线后,复杂的定时器框图也能被拆成几个容易理解的功能模块。


三、基本定时器框图

基本定时器的结构最简单,只保留定时器最核心的计数与触发功能,因此适合用来认识所有定时器共有的主线。STM32F103C8T6 本身没有基本定时器,本节介绍它是为了理解 STM32 定时器家族的结构差异。

1、时钟与计数主线

第一步,RCC 向定时器提供 TIMxCLK,在定时器内部形成 CK_INT。基本定时器通常使用内部时钟,不提供连接 GPIO 的外部计数通道。

第二步,内部时钟经过控制电路后进入 PSC。预分频器把频率较高的时钟降为适合计数的 CK_CNT。例如,把 72MHz 经过 7200 分频后,可以得到 10kHz 的计数时钟。

第三步,CK_CNT 驱动 16 位 CNT 向上计数。当 CNTARR 回到 0 时,定时器产生更新事件,并开始下一轮计数。

基本定时器的信号流可以概括为:

text 复制代码
RCC 提供 TIMxCLK
        ↓
内部时钟 CK_INT
        ↓
PSC 预分频
        ↓
CNT 向上计数
        ↓
达到 ARR
        ↓
产生更新事件 U
        ├── 申请更新中断 UI
        └── 通过 TRGO 触发 DAC

这里的 U 表示更新事件,UI 表示由更新事件产生的中断请求。更新事件不仅可以通知 CPU,也可以通过 TRGO 直接触发 DAC 等外设。后者是一条硬件通路,不需要 CPU 每次都执行一段软件指令。

2、更新事件与缓冲结构

框图中 PSCARR 附近的阴影部分表示缓冲或影子寄存器结构。程序写入的新值不一定立刻影响当前正在进行的计数,而是可以等到更新事件发生时再统一装入。

可以把缓冲结构理解成列车的新时刻表:工作人员先把新表放在候车区,等本趟列车完成运行后再正式启用。这样就不会因为一轮计数进行到一半时突然改变参数,导致当前周期忽长忽短。

3、基本定时器的用途与限制

基本定时器没有 CH1~CH4,因此不能直接完成输入捕获、输出比较、PWM 输出或编码器计数。它更像一个只负责"按时敲铃"的计时器,主要用于:

  • 产生固定周期的更新中断;
  • 为 DAC 提供稳定的转换触发信号;
  • 为程序或其他外设提供基础时间节拍。

四、通用定时器框图

通用定时器保留了基本定时器的 PSC → CNT → ARR 主干,同时在计数器前后增加时钟选择、输入捕获、输出比较、PWM、编码器接口和主从控制等功能。STM32F103C8T6 中的 TIM2TIM3TIM4 都属于通用定时器。

1、多种时钟来源

通用定时器可以从多条路线获得计数时钟:

  • CK_INT:来自 RCC 的内部时钟,最常用于普通定时;
  • ETR:来自外部触发引脚的信号;
  • ITR0~ITR3:来自其他定时器的内部触发信号;
  • TI1TI2:来自定时器通道引脚的输入信号;
  • 编码器接口:同时使用 TI1FP1TI2FP2 两相信号驱动计数器。

外部信号通常需要先经过极性选择、边沿检测和数字滤波,再送入触发选择器和从模式控制器。滤波器用于抑制持续时间很短的毛刺,边沿检测电路则决定在上升沿、下降沿还是两个边沿到来时动作。

多个时钟入口可以看作定时器门口的交通枢纽:内部时钟、外部引脚和其他定时器都能送来脉冲,但内部选择电路最终只会把符合当前模式的信号送入 PSCCNT

2、计数模式

通用定时器通常支持三种计数方式:

  • 向上计数 :从 0 递增到 ARR,随后回到 0;
  • 向下计数 :从 ARR 递减到 0,随后重新装载 ARR
  • 中央对齐计数 :在 0 与 ARR 之间往返计数,常用于生成中心对齐 PWM。

普通定时中断一般使用向上计数。无论采用哪种模式,PSC 仍然决定计数速度,ARR 仍然决定计数边界。

3、输入捕获

通用定时器拥有 4 个捕获/比较通道 CH1~CH4。当某个通道配置为输入捕获时,外部信号会依次经过输入滤波器、边沿检测器、输入选择器和捕获预分频器,最后进入捕获电路。

text 复制代码
外部输入 TIMx_CHx
        ↓
滤波与边沿检测
        ↓
输入选择、捕获预分频
        ↓
有效边沿到来
        ↓
把 CNT 当前值锁存到 CCRx

输入捕获可以理解成给一块高速运行的秒表拍照:CNT 一直计数,外部有效边沿到来时,硬件立即把当时的数值保存到 CCRx。程序比较连续两次捕获值,就能计算输入信号的周期、频率或脉冲宽度。

锁存动作由硬件完成。即使 CPU 稍后才进入中断服务函数,边沿到来时的准确计数值也已经保存在 CCRx 中,因此不会因为软件响应稍慢而直接丢失这一次测量时刻。

4、输出比较与 PWM

当通道配置为输出时,硬件会持续比较 CNT 和相应的 CCRx

  • 当二者满足设定的比较条件时,产生输出比较参考信号 OCxREF
  • 输出控制电路根据模式将引脚置高、置低或翻转;
  • 最终信号通过 TIMx_CHx 引脚输出。

PWM 本质上也是自动重复进行的比较过程。ARR 决定一轮计数的长度,CCRx 决定有效电平持续多久,因此二者分别控制 PWM 的频率和占空比。

在常见的边沿对齐、向上计数 PWM 模式中:

text 复制代码
PWM频率 = f_TIM ÷ [(PSC + 1) × (ARR + 1)]
占空比约为 CCRx ÷ (ARR + 1)

这里写"约为",是因为实际有效电平还会受到 PWM 模式、输出极性和比较边界的影响。计算基础参数时可以先使用上述关系,分析精确波形时则应结合所选择的 PWM 模式。

5、编码器接口

编码器接口同时接收 TI1FP1TI2FP2 两路正交信号。两路信号的先后顺序表示旋转方向,信号边沿则驱动 CNT 自动加一或减一。

如果使用普通 GPIO 和 EXTI 读取编码器,CPU 需要不断判断 A、B 两相的电平关系;启用定时器编码器模式后,方向判断和加减计数都由硬件完成,程序只需要读取 CNT。这相当于把"看方向、数脉冲"的工作交给定时器这名专职计数员。

6、主从触发系统

触发控制器和从模式控制器可以让多个定时器协同工作:

  • ITR0~ITR3 接收其他定时器的内部触发输出;
  • 从模式控制器决定当前定时器在触发到来后复位、启动或进行门控计数;
  • 当前定时器可以通过 TRGO 把更新事件或其他内部信号发送给其他定时器、ADC 或 DAC。

主从结构就像一支乐队:主定时器通过 TRGO 发出节拍,从定时器通过 ITR 接收节拍,再按照预设方式开始、复位或暂停计数。这些动作主要由硬件完成,几乎不需要 CPU 逐个控制。


五、高级定时器框图

高级定时器包含通用定时器的大部分结构。在阅读其框图时,可以先把它看成一张通用定时器框图,再重点寻找额外增加的 RCR、互补输出、死区发生器和刹车输入。STM32F103C8T6 中的 TIM1 就是高级定时器。

1、与通用定时器相同的部分

高级定时器同样具有:

  • 内部时钟、外部时钟和内部触发输入;
  • PSCCNTARR 组成的时基单元;
  • 4 路输入捕获与输出比较通道;
  • PWM 输出与编码器接口;
  • 主从触发系统和 TRGO 输出。

也就是说,通用定时器能够完成的大部分工作,高级定时器同样可以完成。它的主要优势集中在电机驱动和功率控制方面。

2、重复计数器 RCR

高级定时器在时基单元中增加了 RCR 重复计数器。普通定时器通常每完成一个计数周期就产生更新事件,而重复计数器可以让若干个计数周期完成后,再产生一次有效更新事件。

在常见的边沿对齐、向上计数模式下,重复计数值设置为 RCR 时,通常经过 RCR + 1 个计数周期才产生一次有效更新:

text 复制代码
T更新 = (PSC + 1) × (ARR + 1) × (RCR + 1) ÷ f_TIM

中央对齐模式可能在计数上溢和下溢边界产生更新请求,具体周期还要结合更新事件的配置判断,不能不加区分地套用这一公式。

重复计数器常用于电机控制,使 PWM 连续输出若干个周期后,再更新参数、申请中断或触发其他外设。

3、互补输出与死区生成

高级定时器的 CH1~CH3 除了普通输出 CHx,还可以提供互补输出 CHxN。两路波形逻辑上互为补充,常用于控制半桥或全桥电路中的上、下功率管。

实际功率器件从导通切换到完全关闭需要一定时间。如果上、下两个功率管瞬间交替导通,它们可能在极短时间内同时导通,造成电源直通和大电流。

高级定时器在互补输出之间加入 DTG 死区发生器:一侧关闭后,先等待一小段时间,再允许另一侧打开。死区就像双向单车道桥梁的清场时间------一个方向的车辆离开后,必须确认桥面已经清空,另一个方向才能放行。

CH4 通常没有与之对应的互补输出,主要作为普通捕获/比较通道使用。

4、刹车输入与输出保护

高级定时器还提供 TIMx_BKIN 刹车输入。外部过流、过压或驱动故障信号可以通过该引脚进入定时器,内部时钟故障等信号也可以参与保护逻辑。

刹车事件发生后,输出控制电路可以迅速关闭 PWM 主输出,或把输出切换到预设的安全状态,而不必等待 CPU 进入中断服务函数。硬件保护的响应速度高于软件轮询,因此特别适合电机和功率变换电路。

高级定时器还具有主输出使能控制。即使计数器和比较单元已经正常工作,如果没有打开主输出使能,PWM 仍可能不会真正送到引脚。这相当于所有高级输出通道前的一道"总闸门"。


六、定时中断的基本结构

1、定时中断的核心通路

定时中断的本质是:先向计数器提供时钟脉冲,再让计数器按照设定的速度进行计数;当计数器完成一轮计数时,定时器产生更新事件,并在允许更新中断的情况下向 NVIC 申请中断。

整个过程可以概括为:

text 复制代码
选择计数时钟
      ↓
PSC 对时钟进行预分频
      ↓
CNT 对分频后的脉冲进行计数
      ↓
CNT 达到 ARR 设定的终点
      ↓
产生更新事件和更新中断请求
      ↓
NVIC 接收请求并通知 CPU
      ↓
CPU 执行定时器中断服务函数

可以把这套结构理解成一个带有报警功能的计步器:时钟源不断提供"脚步",PSC 决定每隔多少步才让主计数器走一次,CNT 负责累计次数,ARR 规定一轮的目标。计数完成后,定时器按下"报警按钮",再由 NVIC 通知 CPU。

2、计数时钟的来源

通用定时器可以从多条路线获得计数时钟:

时钟来源 进入方式 典型用途
RCC 内部时钟 内部时钟模式 普通定时和周期中断
ETR 引脚 外部时钟模式 2 对外部脉冲直接计数
ITRx 外部时钟模式 1 接收其他定时器的内部触发信号
TIx 捕获通道 外部时钟模式 1 使用通道引脚上的信号驱动计数器
TI1TI2 编码器模式 根据正交信号判断方向并加减计数

这些来源不会同时驱动 CNT。定时器内部的选择与控制电路会根据当前工作模式,选择一条路线送往 PSC

普通的定时中断一般使用 RCC 提供的内部时钟,不需要配置 ETR 或输入捕获引脚。只有使用外部时钟或编码器模式时,才需要配置对应的 GPIO 和输入通道。

3、PSC、CNT 与 ARR 的分工

  • PSC 负责把较快的输入时钟降低到合适的计数频率;
  • CNT 负责累计分频后的时钟脉冲;
  • ARR 负责规定一轮计数的边界。

三者之间的信号关系如下:

text 复制代码
输入时钟 CK_PSC
        ↓
PSC 分频
        ↓
计数时钟 CK_CNT
        ↓
CNT 从 0 向上计数
        ↓
达到 ARR 后溢出并重新从 0 开始

在向上计数模式下,真正产生溢出的是 CNTARR 跳回 0 的过程。平时常说"计数到 ARR 产生更新",是对整个过程的简化描述。

4、更新事件、标志和中断请求

计数器溢出后会产生更新事件 UEV。如果已经使能更新中断,定时器会将更新中断标志 UIF 置 1,并向 NVIC 发送中断请求。

NVIC 会根据中断通道是否使能以及优先级关系,决定何时让 CPU 响应该请求。CPU 进入中断服务函数后,需要检查 UIF、执行相应任务,并及时清除中断标志。否则标志会继续保持为 1,CPU 退出中断后可能再次响应同一请求。

三个概念的区别如下:

概念 作用 特点
更新事件 UEV 在定时器内部触发装载、同步和中断申请等动作 通常是短暂的内部事件
更新中断标志 UIF 记录更新事件已经发生 置 1 后会保持,直到程序清除
更新中断请求 向 NVIC 请求 CPU 响应 只有相关中断使能后才会送出

清除 UIF 只是告诉硬件"这次更新已经处理完毕",不会让计数器倒退,也不会撤销已经发生的更新事件。


七、预分频器的工作时序

1、为什么实际分频系数是 PSC + 1

预分频器内部也有一个计数器,它从 0 开始计数。当内部计数值达到 PSC 后,才向主计数器输出一个有效计数脉冲。因此,它一共经历从 0 到 PSCPSC + 1 个状态。

text 复制代码
预分频系数 = PSC + 1

计数器时钟频率为:

text 复制代码
CK_CNT = CK_PSC ÷ (PSC + 1)

常见参数如下:

PSC 的值 实际分频系数 结果
0 1 分频 CK_CNT = CK_PSC
1 2 分频 CK_CNT = CK_PSC ÷ 2
71 72 分频 CK_CNT = CK_PSC ÷ 72
7199 7200 分频 CK_CNT = CK_PSC ÷ 7200

例如,PSC = 1 时,预分频器内部会按照 0、1、0、1...... 循环,每接收两个 CK_PSC 脉冲,才向 CNT 提供一个 CK_CNT 脉冲。

2、PSC 的缓冲结构

程序写入 TIMx_PSC 的新数值后,新分频系数通常不会立即影响正在进行的计数。写入值会先保存在预分频寄存器中,等更新事件发生后,再装入真正参与分频的内部缓冲器。

例如,原来 PSC = 0,即 1 分频;随后程序把 PSC 写成 1,希望改为 2 分频:

text 复制代码
向 TIMx_PSC 写入 1
        ↓
新值暂时保存在预分频控制寄存器中
        ↓
更新事件 UEV 到来
        ↓
新值装入预分频缓冲器
        ↓
CK_CNT 从 1 分频切换为 2 分频

更新事件到来之前,内部仍使用原来的分频参数,因此 CK_CNT 暂时保持不变;更新事件发生后,新值被装入缓冲器,新的分频比例才正式生效。

3、缓冲器的作用

如果修改 PSC 后立刻改变分频比例,当前正在进行的分频周期可能只完成了一半,切换后的第一个周期就可能过长或过短。缓冲结构让参数在更新事件的统一边界处切换,使输出时序更加整齐。

可以把它理解成变速箱换挡:驾驶员先选中新的挡位,等齿轮到达合适的位置后再真正接合,避免动力传递过程中突然"打齿"。

如果希望初始化时写入的 PSC 立即装入内部缓冲器,可以产生一次更新事件。STM32F10x 标准外设库的时基初始化过程会通过更新事件装载预分频值,因此完成正常初始化后,定时器通常能直接按照设定的分频系数运行。


八、计数器溢出与更新事件

1、计数器如何产生溢出

以向上计数为例,CNT 在每个 CK_CNT 有效脉冲到来时加 1。当 CNT 已经等于 ARR 时,下一个有效计数脉冲会让它回到 0,同时产生溢出和更新事件。

假设计数器从 0x0034 开始计数,自动重装值为 0x0036

text 复制代码
CNT:0034 → 0035 → 0036 → 0000 → 0001 → 0002......
                          ↑
                    产生计数器溢出
                    产生更新事件 UEV
                    更新标志 UIF 置 1

当内部时钟分频系数为 2 时,每两个 CK_INT 脉冲才产生一个 CK_CNT 脉冲,因此 CNT 的变化速度只有内部时钟频率的一半。

2、溢出频率的计算

计数器时钟频率为:

text 复制代码
CK_CNT = CK_PSC ÷ (PSC + 1)

向上计数器从 0 数到 ARR,一轮共有 ARR + 1 个计数状态,因此溢出频率为:

text 复制代码
CK_CNT_OV = CK_CNT ÷ (ARR + 1)
          = CK_PSC ÷ (PSC + 1) ÷ (ARR + 1)

对应的溢出周期,也就是常用的定时中断周期为:

text 复制代码
T = (PSC + 1) × (ARR + 1) ÷ CK_PSC

使用公式时,PSC + 1ARR + 1 都不能漏掉。因为预分频计数和主计数都是从 0 开始,寄存器保存的是"最大计数值",并不是直接保存"总次数"。

3、UEV 与 UIF 的时序区别

更新事件 UEV 通常表现为一个短暂的内部脉冲,用来完成寄存器装载、触发其他外设或申请中断。更新中断标志 UIF 则像一盏会保持点亮的指示灯:更新事件发生后它被置 1,直到程序主动清除。

这也是中断服务函数中必须清除更新标志的原因。若只执行用户任务而不清除 UIF,NVIC 可能持续收到同一个中断请求,程序便会反复进入定时器中断。

4、定时中断周期的理解

PSCARR 都能改变更新周期,但分工不同:

  • 调大 PSC 会降低整个计数器的运行速度;
  • 调大 ARR 会增加每一轮需要经历的计数次数;
  • 二者共同决定最终更新周期。

可以把 PSC 看作钟表走得有多快,把 ARR 看作闹钟需要走过多少格。一个控制"步速",另一个控制"路程",两者相乘才得到总时间。


九、自动重装寄存器的预装载机制

ARR 决定计数器的溢出位置,修改它就相当于改变本轮定时的终点。为了控制新值何时生效,定时器提供了自动重装预装载使能位 ARPE

1、关闭预装载:ARPE = 0

ARPE = 0 时,程序写入 TIMx_ARR 的新值会直接影响当前参与比较的自动重装值,因此当前计数周期可能立刻发生变化。

例如,原来的 ARR = 0x00FF,当 CNT 计数到 0x0032 左右时,把 ARR 改为 0x0036

text 复制代码
原计数终点:00FF
当前计数值:0032
写入新 ARR:0036
        ↓
新值立即生效
        ↓
CNT 继续数到 0036 后溢出并回到 0000

新值大于当前 CNT,所以计数器可以在本轮到达 0x0036 后正常溢出,但本轮周期会突然缩短。

如果写入的新 ARR 已经小于当前 CNT,计数器已经错过新的比较边界,本轮计数可能无法在预期位置结束,周期也可能异常延长。因此,关闭预装载后动态修改 ARR 时,需要同时关注 CNT 的当前值。

2、开启预装载:ARPE = 1

ARPE = 1 时,自动重装部分可以理解为两个层次:

  • 预装载寄存器 :接收程序新写入的 ARR
  • 影子寄存器:保存当前真正参与计数比较的数值。

程序写入新值时,只会先修改预装载寄存器,当前周期仍然使用影子寄存器中的旧值。更新事件发生后,新值才会从预装载寄存器转移到影子寄存器,并在下一轮计数中生效。

例如,当前实际使用的 ARR = 0x00F5,计数过程中写入 0x0036

text 复制代码
本轮计数仍以 00F5 为终点
        ↓
CNT 到达 00F5,产生更新事件
        ↓
预装载寄存器中的 0036 装入影子寄存器
        ↓
下一轮计数以 0036 为新终点

这一次写入不会破坏当前周期:本轮仍完整运行到 0x00F5,下一轮才改用 0x0036

3、有无预装载的区别

对比项目 ARPE = 0,关闭预装载 ARPE = 1,开启预装载
新 ARR 的生效时间 写入后直接生效 更新事件发生后生效
是否影响当前周期 可能直接改变当前周期 当前周期保持完整
动态修改周期的稳定性 较低,需要关注 CNT 当前值 较高,切换边界整齐
常见用途 需要立即改变计数上限 平稳调整定时周期或 PWM 频率

预装载机制很像运动会修改圈数规则:关闭预装载时,裁判在运动员跑到一半时直接改变本圈终点;开启预装载时,裁判先记录新规则,等运动员跑完当前一圈后,再从下一圈开始执行。

在需要动态调整定时周期或 PWM 频率时,通常开启预装载更容易获得连续、稳定的时序。如果确实希望新值立即生效,也可以在写入后主动产生更新事件,但要注意更新事件还可能装载其他缓冲寄存器并设置相关标志。


十、RCC 时钟树与定时器时钟

1、系统时钟的主要来源

STM32F1 的时钟树可以理解成一套供水管网:不同振荡源像不同水源,PLL 像增压站,各级预分频器像分流阀,最终把不同频率的时钟输送到内核、总线和外设。

常见时钟源包括:

  • HSI:高速内部时钟,频率为 8MHz
  • HSE:高速外部时钟,可使用外部晶振或外部时钟信号;
  • LSI:低速内部时钟,典型频率约为 40kHz
  • LSE:低速外部时钟,通常使用 32.768kHz 晶振。

普通定时器主要使用高速时钟系统。以常见的 STM32F103C8T6 最小系统板为例,板载 8MHz 外部晶振可以经过 PLL 进行 9 倍频,得到 72MHzPLLCLK,再被选为系统时钟 SYSCLK

2、SYSCLK、HCLK 与 PCLK

系统时钟经过 AHB 预分频器后得到 HCLK,用于 Cortex-M3 内核、AHB 总线、存储器和 DMA。HCLK 再分别经过 APB1、APB2 预分频器,得到:

  • PCLK1:APB1 外设时钟,最高 36MHz
  • PCLK2:APB2 外设时钟,最高 72MHz

STM32F103C8T6 的定时器分布如下:

定时器 所属总线 对应外设时钟
TIM1 APB2 PCLK2
TIM2TIM3TIM4 APB1 PCLK1

这里的 PCLK1PCLK2 是总线外设时钟,但定时器真正使用的 TIMxCLK 还要继续根据 APB 预分频系数判断。

3、定时器时钟的二倍频规则

STM32F1 为 APB 上的定时器设置了特殊时钟规则:

text 复制代码
如果 APB 预分频系数 = 1:
    TIMxCLK = PCLKx

如果 APB 预分频系数不等于 1:
    TIMxCLK = 2 × PCLKx

这条规则分别适用于 APB1 和 APB2 上的定时器。它的作用是:即使为了满足 APB 总线频率上限而对总线进行分频,定时器仍然可以获得较高的计数时钟。

常见的 72MHz 系统时钟配置如下:

text 复制代码
SYSCLK = 72MHz
AHB 1分频  → HCLK  = 72MHz
APB1 2分频 → PCLK1 = 36MHz
             TIM2~TIM4 时钟 = 2 × 36MHz = 72MHz
APB2 1分频 → PCLK2 = 72MHz
             TIM1 时钟 = 72MHz

因此,虽然 APB1 外设时钟只有 36MHz,APB1 上的 TIM2~TIM4 仍然可以获得 72MHz 的定时器时钟。这是计算定时周期时最容易忽略的地方之一。

不能把"APB 定时器一定会二倍频"当成固定结论。只有对应 APB 预分频系数不等于 1 时才会二倍频;如果 APB 不分频,TIMxCLK 就等于 PCLKx

4、定时时间计算的完整步骤

计算定时器参数时,可以按照以下顺序进行:

  1. 确认定时器位于 APB1 还是 APB2;
  2. 查看对应 APB 总线的预分频系数;
  3. 根据二倍频规则求出实际 TIMxCLK
  4. 使用 PSC + 1 求出 CK_CNT
  5. 使用 ARR + 1 求出更新频率或定时周期。

完整公式为:

text 复制代码
CK_CNT = TIMxCLK ÷ (PSC + 1)

更新频率 = TIMxCLK ÷ [(PSC + 1) × (ARR + 1)]

定时周期 = (PSC + 1) × (ARR + 1) ÷ TIMxCLK

例如,TIM2 的实际时钟为 72MHz,设置 PSC = 7199ARR = 9999

text 复制代码
CK_CNT = 72MHz ÷ 7200 = 10kHz
更新频率 = 10kHz ÷ 10000 = 1Hz
定时周期 = 1s

只要先从时钟树找到真正的 TIMxCLK,再依次经过 PSCARR 两级计算,就能准确得到定时器的更新频率。遇到计算结果与实际现象正好相差 2 倍时,应优先检查 APB 预分频和定时器二倍频规则。


十一、附录

本文以定时器原理、框图和时序为主,没有使用 C 语言示例程序。下面集中整理核心公式、常见缩写和易错点,便于后续配置定时中断时快速查阅。

1、核心公式

计算目标 公式
预分频系数 PSC + 1
计数器时钟 CK_CNT = TIMxCLK ÷ (PSC + 1)
向上计数的更新频率 f_UPDATE = TIMxCLK ÷ [(PSC + 1) × (ARR + 1)]
向上计数的更新周期 T = (PSC + 1) × (ARR + 1) ÷ TIMxCLK
边沿对齐 PWM 频率 f_PWM = TIMxCLK ÷ [(PSC + 1) × (ARR + 1)]

计算时应先确定真正的 TIMxCLK,再代入 PSCARR。如果使用高级定时器的重复计数器,还要结合工作模式和 RCR 继续计算有效更新周期。

2、常见缩写

缩写 含义
TIMxCLK 进入某个定时器的实际时钟
CK_INT 定时器内部时钟
CK_PSC 进入预分频器的时钟
CK_CNT 驱动主计数器的时钟
PSC 预分频器
CNT 计数器
ARR 自动重装寄存器
RCR 重复计数器
CCRx 捕获/比较寄存器
ETR 外部触发输入
ITRx 内部触发输入
TRGO 触发输出
UEV 更新事件
UIF 更新中断标志
ARPE 自动重装预装载使能位

3、三类定时器选型速查

需求 推荐类型 STM32F103C8T6 可选资源
普通周期中断 通用定时器 TIM2TIM3TIM4
输入捕获或测量频率 通用定时器 TIM2TIM3TIM4
普通 PWM 输出 通用定时器或高级定时器 TIM1~TIM4,还要检查通道和引脚映射
读取旋转编码器 通用定时器或高级定时器 TIM1~TIM4,还要检查编码器接口通道
互补 PWM、死区和刹车保护 高级定时器 TIM1
DAC 周期触发 基本定时器更常见 STM32F103C8T6 没有基本定时器

4、易错点速查

  1. PSC = 7199 表示 7200 分频,不是 7199 分频;
  2. ARR = 9999 表示一轮包含 10000 个计数状态;
  3. 更新事件 UEV、更新标志 UIF 和更新中断请求不是同一个概念;
  4. 中断服务函数处理完成后,要及时清除相应的更新中断标志;
  5. PSC 的新值需要通过更新事件装入内部缓冲器后才正式参与分频;
  6. ARPE = 0 时修改 ARR 会直接影响当前周期,ARPE = 1 时新值通常在更新事件后生效;
  7. 定时器时钟不一定等于 PCLK1PCLK2,要检查 APB 预分频与二倍频规则;
  8. STM32F103C8T6 只有 TIM1~TIM4,不能直接照搬其他 STM32F1 型号的定时器资源表。
相关推荐
LCG元2 小时前
STM32 HAL 库串口 DMA + 空闲中断接收不定长数据:从原理到量产级稳定方案
stm32·单片机·嵌入式硬件
fasdgfgsdha56942 小时前
西乡塘代理记账哪家好
笔记
海兰3 小时前
【Kafka学习2】Apache Kafka 典型应用场景
学习·kafka·apache
Wang's Blog3 小时前
PostgreSQL笔记19:进程与内存架构精解
笔记·postgresql·架构
嵌入式阿蔡3 小时前
OTA 实战四:安全启动与固件签名(Secure Boot)—— 拒绝非法固件
网络·stm32·单片机·嵌入式硬件·嵌入式实时数据库
边境悍匪3 小时前
蜗牛学苑 Java 学习 Day23|SpringBoot 思维导图复盘
学习
学计算机的计算基3 小时前
TCP 传输层硬核整理:三次握手、四次挥手、拥塞控制一次讲透
java·网络·笔记·网络协议·算法
xx~t3 小时前
嵌入式学习22
数据结构·学习·算法·排序算法
Wang's Blog4 小时前
PostgreSQL笔记20: 实例、集群、模板数据库与表空间 —— 逻辑体系架构深度解析
数据库·笔记·postgresql