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 = 7199、ARR = 9999:
text
计数频率 = 72MHz ÷ 7200 = 10kHz
计数周期 = 1 ÷ 10kHz = 0.1ms
更新时间 = 10000 × 0.1ms = 1s
计算过程可以分成两步:首先用 PSC 把 72MHz 降为 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 系列常见编号 | 所属总线 | 主要功能 |
|---|---|---|---|
| 高级定时器 | TIM1、TIM8 |
APB2 | 包含通用定时器的大部分功能,并增加重复计数、互补输出、死区生成和刹车保护 |
| 通用定时器 | TIM2、TIM3、TIM4、TIM5 |
APB1 | 支持内外部时钟、输入捕获、输出比较、PWM、编码器接口和主从触发 |
| 基本定时器 | TIM6、TIM7 |
APB1 | 主要用于定时中断和触发 DAC,不具备连接 GPIO 的输入输出通道 |
STM32F103C8T6 实际拥有 4 个定时器:
TIM1:高级定时器;TIM2、TIM3、TIM4:通用定时器;- 该型号没有
TIM5、TIM6、TIM7和TIM8。
因此,在 STM32F103C8T6 上进行普通定时、输入捕获或 PWM 输出时,通常使用 TIM2~TIM4;需要互补 PWM、死区和硬件刹车保护时,则使用 TIM1。
2、三类定时器的功能对比
三类定时器不是三套毫无关系的结构,而是在同一条计数主线上逐级增加功能:基本定时器保留时基和触发功能;通用定时器增加输入、输出、编码器和同步控制;高级定时器又增加面向电机驱动的输出与保护功能。
| 功能 | 基本定时器 | 通用定时器 | 高级定时器 |
|---|---|---|---|
PSC + CNT + ARR 时基单元 |
支持 | 支持 | 支持 |
| 定时更新中断 | 支持 | 支持 | 支持 |
TRGO 触发其他外设 |
支持 | 支持 | 支持 |
| 外部时钟 | 不支持 | 支持 | 支持 |
| 输入捕获 | 不支持 | 支持 | 支持 |
| 输出比较与 PWM | 不支持 | 支持 | 支持 |
| 编码器接口 | 不支持 | 支持 | 支持 |
| 完整的从模式控制 | 不支持 | 支持 | 支持 |
| 重复计数器 | 不支持 | 不支持 | 支持 |
| 互补输出与死区 | 不支持 | 不支持 | 支持 |
| 刹车保护 | 不支持 | 不支持 | 支持 |
选择定时器时,应先判断任务需要什么功能:
- 只需要周期中断或 DAC 触发,可以使用基本定时器;
- 需要测量输入信号、输出 PWM、读取编码器或使用外部时钟,应使用通用定时器;
- 需要驱动电机、输出互补 PWM、插入死区或使用硬件刹车保护,应使用高级定时器。
基本定时器虽然能够输出 TRGO 并作为触发源,但没有通用定时器中的完整从模式控制器。因此,将它描述为"主从触发功能较少"不够准确,更合适的说法是:它可以向外提供触发信号,但不支持通用定时器那样完整的从模式。
3、阅读定时器框图的方法
定时器框图中的线路很多,第一次看到很容易无从下手。阅读时可以采用"先主干,后支路"的方法:
- 找到时钟从哪里进入定时器;
- 沿着
PSC → CNT → ARR找到时基单元; - 查看计数器在什么条件下产生更新事件;
- 再分析
CH1~CH4的输入捕获和输出比较通道; - 最后查看编码器、主从触发、互补输出和刹车等附加功能。
这就像阅读一张复杂的地铁图:先认清贯穿全图的主线路,再研究两侧的换乘支线,原本交错的线路就会逐渐清晰。
框图中的常见缩写如下:
| 缩写 | 含义 |
|---|---|
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 向上计数。当 CNT 从 ARR 回到 0 时,定时器产生更新事件,并开始下一轮计数。
基本定时器的信号流可以概括为:
text
RCC 提供 TIMxCLK
↓
内部时钟 CK_INT
↓
PSC 预分频
↓
CNT 向上计数
↓
达到 ARR
↓
产生更新事件 U
├── 申请更新中断 UI
└── 通过 TRGO 触发 DAC
这里的 U 表示更新事件,UI 表示由更新事件产生的中断请求。更新事件不仅可以通知 CPU,也可以通过 TRGO 直接触发 DAC 等外设。后者是一条硬件通路,不需要 CPU 每次都执行一段软件指令。
2、更新事件与缓冲结构
框图中 PSC、ARR 附近的阴影部分表示缓冲或影子寄存器结构。程序写入的新值不一定立刻影响当前正在进行的计数,而是可以等到更新事件发生时再统一装入。
可以把缓冲结构理解成列车的新时刻表:工作人员先把新表放在候车区,等本趟列车完成运行后再正式启用。这样就不会因为一轮计数进行到一半时突然改变参数,导致当前周期忽长忽短。
3、基本定时器的用途与限制
基本定时器没有 CH1~CH4,因此不能直接完成输入捕获、输出比较、PWM 输出或编码器计数。它更像一个只负责"按时敲铃"的计时器,主要用于:
- 产生固定周期的更新中断;
- 为 DAC 提供稳定的转换触发信号;
- 为程序或其他外设提供基础时间节拍。
四、通用定时器框图
通用定时器保留了基本定时器的 PSC → CNT → ARR 主干,同时在计数器前后增加时钟选择、输入捕获、输出比较、PWM、编码器接口和主从控制等功能。STM32F103C8T6 中的 TIM2、TIM3 和 TIM4 都属于通用定时器。
1、多种时钟来源
通用定时器可以从多条路线获得计数时钟:
CK_INT:来自 RCC 的内部时钟,最常用于普通定时;ETR:来自外部触发引脚的信号;ITR0~ITR3:来自其他定时器的内部触发信号;TI1、TI2:来自定时器通道引脚的输入信号;- 编码器接口:同时使用
TI1FP1和TI2FP2两相信号驱动计数器。
外部信号通常需要先经过极性选择、边沿检测和数字滤波,再送入触发选择器和从模式控制器。滤波器用于抑制持续时间很短的毛刺,边沿检测电路则决定在上升沿、下降沿还是两个边沿到来时动作。
多个时钟入口可以看作定时器门口的交通枢纽:内部时钟、外部引脚和其他定时器都能送来脉冲,但内部选择电路最终只会把符合当前模式的信号送入 PSC 和 CNT。
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、编码器接口
编码器接口同时接收 TI1FP1 和 TI2FP2 两路正交信号。两路信号的先后顺序表示旋转方向,信号边沿则驱动 CNT 自动加一或减一。
如果使用普通 GPIO 和 EXTI 读取编码器,CPU 需要不断判断 A、B 两相的电平关系;启用定时器编码器模式后,方向判断和加减计数都由硬件完成,程序只需要读取 CNT。这相当于把"看方向、数脉冲"的工作交给定时器这名专职计数员。
6、主从触发系统
触发控制器和从模式控制器可以让多个定时器协同工作:
ITR0~ITR3接收其他定时器的内部触发输出;- 从模式控制器决定当前定时器在触发到来后复位、启动或进行门控计数;
- 当前定时器可以通过
TRGO把更新事件或其他内部信号发送给其他定时器、ADC 或 DAC。
主从结构就像一支乐队:主定时器通过 TRGO 发出节拍,从定时器通过 ITR 接收节拍,再按照预设方式开始、复位或暂停计数。这些动作主要由硬件完成,几乎不需要 CPU 逐个控制。
五、高级定时器框图
高级定时器包含通用定时器的大部分结构。在阅读其框图时,可以先把它看成一张通用定时器框图,再重点寻找额外增加的 RCR、互补输出、死区发生器和刹车输入。STM32F103C8T6 中的 TIM1 就是高级定时器。
1、与通用定时器相同的部分
高级定时器同样具有:
- 内部时钟、外部时钟和内部触发输入;
PSC、CNT、ARR组成的时基单元;- 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 | 使用通道引脚上的信号驱动计数器 |
TI1 与 TI2 |
编码器模式 | 根据正交信号判断方向并加减计数 |
这些来源不会同时驱动 CNT。定时器内部的选择与控制电路会根据当前工作模式,选择一条路线送往 PSC。
普通的定时中断一般使用 RCC 提供的内部时钟,不需要配置 ETR 或输入捕获引脚。只有使用外部时钟或编码器模式时,才需要配置对应的 GPIO 和输入通道。
3、PSC、CNT 与 ARR 的分工
PSC负责把较快的输入时钟降低到合适的计数频率;CNT负责累计分频后的时钟脉冲;ARR负责规定一轮计数的边界。
三者之间的信号关系如下:
text
输入时钟 CK_PSC
↓
PSC 分频
↓
计数时钟 CK_CNT
↓
CNT 从 0 向上计数
↓
达到 ARR 后溢出并重新从 0 开始
在向上计数模式下,真正产生溢出的是 CNT 从 ARR 跳回 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 到 PSC 的 PSC + 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 + 1 和 ARR + 1 都不能漏掉。因为预分频计数和主计数都是从 0 开始,寄存器保存的是"最大计数值",并不是直接保存"总次数"。
3、UEV 与 UIF 的时序区别
更新事件 UEV 通常表现为一个短暂的内部脉冲,用来完成寄存器装载、触发其他外设或申请中断。更新中断标志 UIF 则像一盏会保持点亮的指示灯:更新事件发生后它被置 1,直到程序主动清除。
这也是中断服务函数中必须清除更新标志的原因。若只执行用户任务而不清除 UIF,NVIC 可能持续收到同一个中断请求,程序便会反复进入定时器中断。
4、定时中断周期的理解
PSC 和 ARR 都能改变更新周期,但分工不同:
- 调大
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 倍频,得到 72MHz 的 PLLCLK,再被选为系统时钟 SYSCLK。
2、SYSCLK、HCLK 与 PCLK
系统时钟经过 AHB 预分频器后得到 HCLK,用于 Cortex-M3 内核、AHB 总线、存储器和 DMA。HCLK 再分别经过 APB1、APB2 预分频器,得到:
PCLK1:APB1 外设时钟,最高36MHz;PCLK2:APB2 外设时钟,最高72MHz。
STM32F103C8T6 的定时器分布如下:
| 定时器 | 所属总线 | 对应外设时钟 |
|---|---|---|
TIM1 |
APB2 | PCLK2 |
TIM2、TIM3、TIM4 |
APB1 | PCLK1 |
这里的 PCLK1 和 PCLK2 是总线外设时钟,但定时器真正使用的 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、定时时间计算的完整步骤
计算定时器参数时,可以按照以下顺序进行:
- 确认定时器位于 APB1 还是 APB2;
- 查看对应 APB 总线的预分频系数;
- 根据二倍频规则求出实际
TIMxCLK; - 使用
PSC + 1求出CK_CNT; - 使用
ARR + 1求出更新频率或定时周期。
完整公式为:
text
CK_CNT = TIMxCLK ÷ (PSC + 1)
更新频率 = TIMxCLK ÷ [(PSC + 1) × (ARR + 1)]
定时周期 = (PSC + 1) × (ARR + 1) ÷ TIMxCLK
例如,TIM2 的实际时钟为 72MHz,设置 PSC = 7199、ARR = 9999:
text
CK_CNT = 72MHz ÷ 7200 = 10kHz
更新频率 = 10kHz ÷ 10000 = 1Hz
定时周期 = 1s
只要先从时钟树找到真正的 TIMxCLK,再依次经过 PSC 和 ARR 两级计算,就能准确得到定时器的更新频率。遇到计算结果与实际现象正好相差 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,再代入 PSC 和 ARR。如果使用高级定时器的重复计数器,还要结合工作模式和 RCR 继续计算有效更新周期。
2、常见缩写
| 缩写 | 含义 |
|---|---|
TIMxCLK |
进入某个定时器的实际时钟 |
CK_INT |
定时器内部时钟 |
CK_PSC |
进入预分频器的时钟 |
CK_CNT |
驱动主计数器的时钟 |
PSC |
预分频器 |
CNT |
计数器 |
ARR |
自动重装寄存器 |
RCR |
重复计数器 |
CCRx |
捕获/比较寄存器 |
ETR |
外部触发输入 |
ITRx |
内部触发输入 |
TRGO |
触发输出 |
UEV |
更新事件 |
UIF |
更新中断标志 |
ARPE |
自动重装预装载使能位 |
3、三类定时器选型速查
| 需求 | 推荐类型 | STM32F103C8T6 可选资源 |
|---|---|---|
| 普通周期中断 | 通用定时器 | TIM2、TIM3、TIM4 |
| 输入捕获或测量频率 | 通用定时器 | TIM2、TIM3、TIM4 |
| 普通 PWM 输出 | 通用定时器或高级定时器 | TIM1~TIM4,还要检查通道和引脚映射 |
| 读取旋转编码器 | 通用定时器或高级定时器 | TIM1~TIM4,还要检查编码器接口通道 |
| 互补 PWM、死区和刹车保护 | 高级定时器 | TIM1 |
| DAC 周期触发 | 基本定时器更常见 | STM32F103C8T6 没有基本定时器 |
4、易错点速查
PSC = 7199表示 7200 分频,不是 7199 分频;ARR = 9999表示一轮包含 10000 个计数状态;- 更新事件
UEV、更新标志UIF和更新中断请求不是同一个概念; - 中断服务函数处理完成后,要及时清除相应的更新中断标志;
PSC的新值需要通过更新事件装入内部缓冲器后才正式参与分频;ARPE = 0时修改ARR会直接影响当前周期,ARPE = 1时新值通常在更新事件后生效;- 定时器时钟不一定等于
PCLK1或PCLK2,要检查 APB 预分频与二倍频规则; - STM32F103C8T6 只有
TIM1~TIM4,不能直接照搬其他 STM32F1 型号的定时器资源表。