
STM32 通用定时器 TIM 详解:从概念到 PWM 实战
1. 通用定时器概念

1.1 通用定时器 vs 基础定时器
| 计数方向 | 只向上(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 为例)

| 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 ≈ 1kHz
PWM 模式 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(高级定时器才有)=1
3.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) = 750
3.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 什么时候用哪一方?
|
简单呼吸灯 (固定渐变) |
✅ | ✅ | ✅ 推荐(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 就够);
用外部中断或按键扫描中断检测按键。






