STM32 通用定时器 TIM 详解:从概念到 PWM 实战
1. 通用定时器概念
1.1 通用定时器 vs 基础定时器
对比项 基础定时器 TIM6/7 通用定时器 TIM2~5 计数方向 只向上(0,1,2…) 向上 / 向下 / 中央对齐 三种 时钟源 只内部 CK_INT 内部 + 外部引脚 + 编码器 + 内部触发 四种 通道 无独立通道 4 个独立通道 CH1~CH4(可同时干 4 件事) 输出能力 只能 TRGO 触发 DAC 每个通道可输出到 GPIO(PWM、方波、脉冲) 输入能力 无 输入捕获(测脉宽、测频率、测外部信号) DMA 有 有(且每通道独立 DMA 请求) 中断 更新中断 更新 + 4 个捕获/比较中断 一句话:
通用定时器 = 基础定时器(时基单元)+ 捕获/比较单元 + 更多时钟源 + 4 个对外引脚通道。
1.2 通用定时器内部框图(必读)
时钟源选择(4选1) ┌──────────────────────────┐ │ CK_INT 内部时钟 │ │ TI1/TI2 外部引脚 │ │ ETR 外部触发 │ │ ITR 其他定时器 │ └───────────┬──────────────┘ ▼ ┌─── PSC 预分频器 ───┐ │ CK_CNT = CK_PSC/(PSC+1) │ └───────────┬──────────┘ ▼ ┌─── CNT 计数器(16位)───┐ │ 向上 / 向下 / 中央对齐 │◄──► ARR 自动重装(影子寄存器) └───────────┬──────────┘ ▼ ┌──────────┬──────────┬──────────┬──────────┐ ▼ ▼ ▼ ▼ ▼ CH1 比较器 CH2 比较器 CH3 比较器 CH4 比较器 溢出 UEV │ │ │ │ │ CCR1 CCR2 CCR3 CCR4 ├─ 更新中断 (16位) (16位) (16位) (16位) ├─ DMA 请求 │ │ │ │ └─ TRGO ▼ ▼ ▼ ▼ OC1REF OC2REF OC3REF OC4REF │ │ │ │ CCMR 配置 CCMR 配置 CCMR 配置 CCMR 配置(PWM模式/输出比较模式) │ │ │ │ CC1 引脚 CC2 引脚 CC3 引脚 CC4 引脚 (PA0/PA1 (PA1/PA7 (PB10/PB11 (PB11/PA3 PA1/PA5...) PA5...) PA9...) PB...)
1.3 计数模式(CR1 寄存器 DIR + CMS 位)
① 向上计数模式(最常用,和基础定时器一样):CNT 从 0 数到 ARR,溢出后回 0 重新数。
② 向下计数模式:CNT 从 ARR 数到 0,溢出后重装 ARR。
③ 中央对齐模式(PWM 电机专用):CNT 从 0 数到 ARR-1 → 再向下数到 1 → 一个周期两次比较(上升沿+下降沿),输出更对称的 PWM,减少电机噪音。
1.4 通用定时器用到的关键寄存器(在基础定时器之上新增的)
寄存器 偏移 作用 TIMx_CCMR1 0x18 通道 1、2 的模式配置(输出比较模式 / 输入捕获模式、预分频、滤波器) TIMx_CCMR2 0x1C 通道 3、4 的模式配置 TIMx_CCER 0x20 通道输出使能(CCxE)、极性(CCxP) TIMx_CCR1~CCR4 0x34 / 0x38 / 0x3C / 0x40 捕获/比较寄存器(16 位,设定"比较阈值") TIMx_CR1 0x00 比基础定时器多了 DIR(位4)、CMS[1:0](位6:5) TIMx_SMCR 0x08 从模式控制(外部时钟、编码器模式) 2. 输出比较速通
2.1 什么是"输出比较"
把 CNT 的值和 CCR(捕获/比较寄存器) 的值做比较:
- 当
CNT == CCR时,硬件自动对 OCxREF(输出比较参考信号) 执行一个动作;- 这个动作是什么,由 CCMR 寄存器的 OCxM 位决定;
- OCxREF 再经过极性选择(CCER),最终输出到 GPIO 引脚。
类比:
CNT 是秒表在走,CCR 是你设的"到这个数字就响",OCxM 是"响了之后干嘛"(亮灯?翻转?输出高电平?)。
2.2 OCxM 输出比较模式(CCMR 的位 6:4,CH1 为例)
OCxM[2:0] 模式 行为(CNT==CCR 时) 000 冻结 什么都不做(只中断/DMA,不改输出) 001 匹配时置有效 OCxREF 强制变为有效电平 010 匹配时置无效 OCxREF 强制变为无效电平 011 匹配时翻转 OCxREF 翻转(原来高变低,原来低变高) 100 强制无效 OCxREF 始终为无效(软件强制) 101 强制有效 OCxREF 始终为有效(软件强制) 110 PWM 模式 1 向上计数时:CNT < CCR → 有效;CNT ≥ CCR → 无效 111 PWM 模式 2 与 PWM1 相反(CNT < CCR → 无效) 2.3 用"翻转模式"做一个 1Hz 方波
配置:ARR=999、PSC=71(CK_CNT=1MHz,1us/次)、OC1M=011(翻转)。
CNT 计数:0 → 1 → 2 → ... → 999 → 溢出回 0 → 再到 CCR=500 → 翻转 每隔 (ARR+1)/2 = 500us 翻转一次 → 周期 1ms → 1kHz 方波翻转模式是输出比较最简单的用法:不需要算占空比,硬件自动对半翻转。
2.4 输出比较的硬件时序
CNT ───0────1────...────500────...────999────0────1────... ↑ CNT==CCR=500 ↓ OC1REF ────────────────┐ ┌──────────── └───────────────────────┘ ← 500us →← 500us →3. 软件可调占空比
3.1 占空比是什么
PWM 一个周期内,高电平时间 / 总周期时间 就是占空比。
LED 调光、舵机角度、电机转速,本质都是调占空比。
占空比 D = CCR / (ARR + 1) × 100%
- CCR = 0 → 占空比 0% → 引脚一直低
- CCR = ARR → 占空比 100% → 引脚一直高
- CCR = ARR/2 → 占空比 50% → 方波
3.2 软件怎么调占空比
PWM 一旦启动(CEN=1),硬件会自动按 ARR 周期输出波形。
想改占空比?只要改 CCR 的值就行,硬件自动在下一个周期生效(CCR 有影子寄存器,默认缓冲)。
// 占空比从 50% 渐变为 100%,呼吸灯效果 for (uint16_t i = 0; i <= arr; i++) { __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, i); // 改 CCR1 HAL_Delay(5); }关键点:
改占空比不需要停定时器、不需要重配寄存器,只要写 CCR。
这就是"软件可调占空比"——CPU 只需要在合适时机改一下 CCR,剩下的全是硬件自动完成。
3.3 一个常见误区
- 改 ARR 会改频率(周期),改 CCR 只改占空比;
- 想保持频率不变只调亮度 → 只动 CCR;
- 想换 PWM 频率 → 动 ARR(必要时连 PSC 一起动)。
3.4 PWM 速通
3.4.1 PWM 两个核心数
设定 决定什么 怎么算 ARR PWM 的周期(频率) PWM 频率 = CK_CNT / (ARR+1) = TIMxCLK / ((PSC+1)(ARR+1)) CCR PWM 的占空比(高电平宽度) 占空比 = CCR / (ARR+1);高电平时间 = CCR × 计数周期 3.4.2 PWM 模式 1 的波形(向上计数)
CCR = 400, ARR = 1000 → 占空比 = 400/1001 ≈ 40% OC1REF ┌───────┐ ┌───────┐ │ │ │ │ │ │ │ │ ──┘ └───────────────┘ └──── │← 400 →←──── 601 ────→│ │←─────1001个计数 ─────→│ 高电平时间:400us(若 CK_CNT=1MHz) 周期:1001us ≈ 1kHzPWM 模式 1(向上计数)规则:
CNT < CCR→ OCxREF = 有效电平(一般是高)CNT ≥ CCR→ OCxREF = 无效电平(一般是低)PWM 模式 2 正好相反(CNT < CCR 输出无效)。
3.4.3 PWM 配置 7 步法(背下来)
① 开时钟:__HAL_RCC_TIM3_CLK_ENABLE()、__HAL_RCC_GPIOB_CLK_ENABLE() ② 配 GPIO:PB4 复用推挽输出(TIM3_CH1) ③ 配时基:PSC=71(1MHz)、ARR=999(1kHz) ④ 配 CCR:TIM3->CCR1 = 400(占空比 40%) ⑤ 配模式:CCMR1 = PWM 模式 1(OC1M=110,OC1PE=1 预装载使能) ⑥ 配输出:CCER = CC1E=1 使能通道、CC1P=0 高电平有效 ⑦ 启动:CR1.CEN=1;MOE(高级定时器才有)=13.4.4 PWM 计算经典题
题:TIMxCLK=72MHz,要产生 1kHz、占空比 75% 的 PWM,求 PSC、ARR、CCR。
选 CK_CNT = 1MHz(1us 分辨率)→ PSC+1 = 72 → PSC = 71 1kHz → 周期 1ms → (ARR+1) = 1000 → ARR = 999 75% 占空比 → CCR = 0.75 × (ARR+1) = 7503.5 通用定时器"4 方关系"
通用定时器在 PWM / 波形生成场景下,涉及四个角色的协作,理解这"4 方关系"就懂了定时器的实战骨架。
3.5.1 四方分别是谁
角色 硬件载体 职责 ① 时基单元 PSC / CNT / ARR 产生时间基准,定时器的"心跳"。
决定 PWM 频率
② 捕获/比较单元 CCMR / CCR / CCER 把时间基准"翻译"成引脚电平变化。
决定 PWM 占空比
③ DMA 控制器 DMA1_Channel5 等 自动搬运数据到 CCR,CPU 完全不参与。
比如做呼吸灯:把一组亮度值放数组,DMA 每次更新事件自动把下一个值写进 CCR
④ NVIC 中断 TIMx_UP_IRQ / TIMx_CCx_IRQ 事件通知 CPU。
比如"PWM 周期到了"或"捕获到外部信号边沿了",CPU 去处理
3.5.2 四方怎么协作(以呼吸灯为例)
① 时基单元:每 1ms 产生一次更新事件 UEV(PWM 周期) │ ├─► ② 捕获/比较单元:CNT 与 CCR 比较,硬件自动输出 PWM 到引脚 │ ├─► ③ DMA:响应 UEV 请求,自动把"亮度数组[下一个值]"写入 CCR │ → 下一个 PWM 周期占空比自动变化,CPU 全程不参与 │ └─► ④ NVIC 中断:可选,如果你想在每周期做点事就开 (比如数到 100 次后换方向:亮渐暗、暗渐亮)3.5.3 什么时候用哪一方?
场景 时基 比较 DMA 中断 简单呼吸灯
(固定渐变)
✅ ✅ ✅ 推荐(CPU 解放) ❌ 不需要 舵机控制
(按键调角度)
✅ ✅ ❌ 改一次 CCR 就够 ✅ 按键中断 电机 PID ✅ ✅ ❌ ✅ 高频更新(10kHz) 输入捕获测脉宽 ✅ ✅(输入模式) ✅ 高速测频 ✅ 边沿事件 本质:
时基单元是"发动机",比较单元是"变速箱",DMA 是"自动驾驶",中断是"报警铃"。
四个模块可独立开关,按需组合。
下面用一张图把"PWM 占空比 + 四方关系"一次看清:
面试题
一、概念题
Q1. 通用定时器 TIM2~5 比基础定时器 TIM6/7 多了哪些能力?
A:
① 3 种计数模式(向上/向下/中央对齐);
② 4 个独立捕获/比较通道 CH1~CH4;
③ 更多时钟源(内部 + 外部引脚 + 编码器 + 内部触发);
④ 输出能力(每个通道可输出 PWM/方波到 GPIO);
⑤ 输入捕获(测脉宽/频率)。
一句话:
基础定时器 = 只会数数;
通用定时器 = 数数 + 把数变成引脚波形。
Q2. 什么是输出比较?OCxREF 是什么?
A:
硬件把 CNT 计数器值和 CCR 比较寄存器值做比较,
当
CNT == CCR时,按 OCxM 设定的模式自动改变 OCxREF(输出比较参考信号),再经过极性选择输出到 GPIO。OCxREF 是通道内部的"波形原型",CCER 决定它最终是高电平还是低电平出去。
Q3. OCxM 的 7 种模式分别是干什么的?
A:
000 冻结(只发事件不改输出);
001 匹配时置有效;
002 匹配时置无效;
011 匹配时翻转(最简单的方波);
100/101 强制输出无效/有效;
110 PWM 模式 1、111 PWM 模式 2(最常用)。
二、PWM 计算题(高频)
Q4. PWM 的频率和占空比分别由什么寄存器决定?写出公式。
A:
频率由 ARR(配合 PSC)决定:
PWM 频率 = CK_CNT / (ARR+1) = TIMxCLK / ((PSC+1)(ARR+1))。占空比由 CCR 决定:
占空比 = CCR / (ARR+1) × 100%。改 ARR 改频率、改 CCR 只改占空比。
Q5. TIMxCLK=72MHz,要产生 1kHz、占空比 75% 的 PWM,求 PSC、ARR、CCR。
A:
取 CK_CNT=1MHz(1us 分辨率)→ PSC+1=72 → PSC=71;
1kHz 周期 1ms → ARR+1=1000 → ARR=999;
75% → CCR = 0.75 × 1000 = 750。
Q6. 想保持 PWM 频率不变,只调 LED 亮度,应该改哪个寄存器?为什么不需要停定时器?
A:
只改 CCR。
因为 CCR 有影子寄存器(OCxPE=1 预装载使能),改值后下一个更新事件 UEV 自动生效,当前周期完整跑完,不会打断波形,硬件自动完成,CPU 只需要写一次寄存器。
三、模式与配置题
Q7. PWM 模式 1 和模式 2 的区别?
A:
向上计数时:
PWM 模式 1(OCxM=110)
CNT < CCR 输出有效电平, CNT ≥ CCR 输出无效电平;
**PWM 模式 2(OCxM=111)**正好相反。
初学只用 PWM 模式 1 + 高电平有效即可。
Q8. 输出比较配置的 7 步是什么?
A:
① 开 TIM 和 GPIO 时钟;
② GPIO 配复用推挽输出;
③ 写 PSC、ARR 定频率;
④ 写 CCR 定占空比;
⑤ CCMR 选 PWM 模式 + 开预装载(OCxPE=1);
⑥ CCER 使能通道(CCxE=1)+ 选极性;
⑦ CR1.CEN=1 启动计数。
Q9. 什么是"软件可调占空比"?为什么它很有用?
A:
PWM 启动后,CPU 只要在合适时机修改 CCR 的值,硬件自动在下一周期用新占空比输出。
无需停定时器、无需重配寄存器。
呼吸灯(CCR 从 0 渐加到 ARR)、舵机(按键改 CCR)、电机调速(PID 输出写 CCR)都靠它。
四、四方关系题(重点)
Q10. 通用定时器实战中的"4 方"指哪 4 个模块?各自职责?
A:
① 时基单元(PSC/CNT/ARR):产生时间基准,定频率;
② 捕获/比较单元(CCMR/CCR/CCER):把时间基准翻译成引脚波形,定占空比;
③ DMA 控制器:响应 UEV 自动搬运数据到 CCR,CPU 完全不参与;
④ NVIC 中断:事件通知 CPU(更新/捕获/比较)。
Q11. 做一个呼吸灯,为什么推荐用 DMA + 定时器,而不是 CPU 循环改 CCR?
A:
CPU 循环改 CCR 要不停
HAL_Delay + 改 CCR,占用 CPU 且定时不准。用 DMA 响应 UEV 自动把"亮度数组"下一个值写入 CCR,CPU 完全解放,只需要在初始化时配一次,剩下的硬件自动跑;
只有在需要"亮渐暗/暗渐亮切换方向"时才开一个更新中断做计数。
Q12. 一个舵机控制场景,按键按下调角度,应该用哪几方?
A:
时基单元(产生 50Hz PWM 周期 20ms)+ 捕获/比较单元(CCR 决定 0.5ms~2.5ms 高电平,对应角度);
不需要 DMA(角度不常变,按键改一次 CCR 就够);
用外部中断或按键扫描中断检测按键。