STM32 高级定时器详解(从零开始)
你发的这份文档是 TIM1/TIM8 高级控制定时器的参考手册。我来从最基础的概念讲起,帮你建立完整的理解。
第一部分:定时器是什么?
定时器的本质就是一个自动数数的计数器。给它一个时钟脉冲,它就数一下。
时钟脉冲: __|¯|__|¯|__|¯|__|¯|__|¯|__|¯|__|¯|__|¯|__
计数器值: 0 1 2 3 4 5 6 7 ...
就这么简单。所有的定时器功能(PWM、输入捕获、输出比较...)都是在这个"数数"的基础上做文章。
第二部分:时基单元------定时器的心脏
时基单元由 4 个寄存器 组成,控制计数器"怎么数":
1. PSC(预分频器)------ 控制数数的速度
时钟太快了?用 PSC 先把时钟除一下再送给计数器。
原始时钟 240 MHz: |||||||||||||||||||||||| 太快了!
PSC = 239 后:
计数器时钟 = 240MHz / (239+1) = 1MHz
也就是每 1μs 数一下
公式:计数器时钟 = 定时器时钟 / (PSC + 1)
2. CNT(计数器)------ 当前数到几了
这就是那个一直在数数的寄存器,你可以随时读取它的值。
3. ARR(自动重载寄存器)------ 数到多少就归零
ARR = 999 时:
CNT: 0 → 1 → 2 → ... → 998 → 999 → 溢出! → 0 → 1 → ...
↑
产生更新事件(UEV)
4. RCR(重复计数器)------ 高级定时器独有
普通定时器每次溢出都产生更新事件。重复计数器让你可以"每 N+1 次溢出才产生一次更新事件"。
RCR = 2 时:
溢出第1次 → 不更新(RCR: 2→1)
溢出第2次 → 不更新(RCR: 1→0)
溢出第3次 → 更新! (RCR: 0→重载为2)
主要用于电机控制 PWM 中减少中断频率。
第三部分:回答你的问题------Auto-Reload Preload (ARPE)
ARPE 到底控制的是哪个寄存器?
控制的是 ARR(自动重载寄存器)。
ARR 寄存器其实在硬件内部有两层结构:
你写入的地方 实际起作用的地方
┌──────────────┐ ┌──────────────┐
│ 预装载寄存器 │ ───→ │ 影子寄存器 │ ←→ 与CNT比较
│ (Preload) │ │ (Shadow) │
└──────────────┘ └──────────────┘
你能读写的 真正干活的
ARPE = 0(Disable,你当前的配置):
你写 ARR = 500
↓ 立刻生效!
影子寄存器 = 500 (计数器马上用新值)
ARPE = 1(Enable):
你写 ARR = 500
↓ 先存着,不生效
预装载寄存器 = 500
↓ 等到下次更新事件(UEV)
影子寄存器 = 500 (这时才真正生效)
为什么要这么设计?
用 PWM 举例。假设你正在输出一个 PWM 波,想改变频率(改 ARR):
没有预装载(ARPE=0)------可能出问题:
ARR原来=1000,CNT正在数到800
此刻你改 ARR=500 → 立刻生效!
但 CNT 已经是 800 > 500 了!
计数器不知道什么时候该溢出,只能一直数到最大值才回来
→ 这一个周期的波形就乱了
有预装载(ARPE=1)------安全:
ARR原来=1000,CNT正在数到800
此刻你改 ARR=500 → 先存着
CNT继续: 800 → 999 → 1000 → 溢出 → 更新事件!
此时 ARR=500 才真正生效
→ 下一个周期完整地用新值,波形不会乱
第四部分:三种计数模式
递增计数(你当前用的)
ARR = 6
CNT: 0 → 1 → 2 → 3 → 4 → 5 → 6 → (溢出) → 0 → 1 → ...
↑ 更新事件
递减计数
ARR = 6
CNT: 6 → 5 → 4 → 3 → 2 → 1 → 0 → (下溢) → 6 → 5 → ...
↑ 更新事件
中心对齐(递增/递减交替)
ARR = 6
CNT: 0→1→2→3→4→5→6→5→4→3→2→1→0→1→2→...
↑ ↑
上溢事件 下溢事件
生成的PWM波形是对称的,常用于电机控制
第五部分:四大核心功能
功能1:输入捕获 ⭐(你的动平衡项目用的就是这个)
作用: 外部信号边沿到来时,硬件自动把 CNT 的值"拍照"存下来。
CNT在自由计数: 0 1 2 3 4 5 6 7 8 9 10 11 12 ...
外部信号: _____|¯¯¯¯¯¯¯¯¯¯|____________|¯¯¯¯
↑ ↑
CCR1 = 4 CCR1 = 12
(拍照!存下4) (拍照!存下12)
两次捕获的差 = 12 - 4 = 8 个tick → 就知道了信号周期
这就是你测相位差的原理:
光电头信号: _____|¯|_________________________
↑ CH1捕获,CCR1 = 1000
过零检测信号: _______________|¯|_______________
↑ CH2捕获,CCR2 = 4500
时间差 = 4500 - 1000 = 3500 ticks
周期 = 20000 ticks(50Hz)
相位差 = 3500/20000 × 360° = 63°
功能2:输出比较
作用: CNT 数到某个值时,自动改变引脚电平。
CCR1 = 5,模式 = 翻转
CNT: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
OC1: __________________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|____
↑ CNT=CCR1,翻转! ↑ 再次翻转
功能3:PWM 生成
本质就是输出比较的特殊应用。
ARR = 999(决定频率)
CCR1 = 300(决定占空比 = 300/1000 = 30%)
PWM模式1:CNT < CCR1 时输出高,否则输出低
CNT: 0 ──→ 299 ──→ 300 ──→ 999 ──→ 0
OC1: ¯¯¯¯¯¯¯¯¯¯¯¯|__________________|¯¯¯¯
← 高(30%) → ← 低(70%) →
改 CCR1 就改占空比,改 ARR 就改频率。
功能4:编码器接口
两个正交信号(A相、B相)接入,定时器自动判断旋转方向和位置:
正转时:A相超前B相 → CNT递增
反转时:B相超前A相 → CNT递减
CNT的值就直接代表编码器位置,全硬件完成,CPU不用管
第六部分:高级定时器 vs 通用定时器------多了什么?
| 特性 | 通用定时器(TIM2~TIM5) | 高级定时器(TIM1/TIM8) |
|---|---|---|
| 基本功能 | ✅ 全有 | ✅ 全有 |
| 互补输出+死区 | ❌ | ✅ 驱动半桥/全桥 |
| 断路保护 | ❌ | ✅ 紧急关断PWM |
| 重复计数器 | ❌ | ✅ 减少更新频率 |
| 位宽 | TIM2/TIM5: 32位, TIM3/TIM4: 16位 | 16位 |
互补输出 + 死区
驱动半桥电路(上管+下管),两个管子绝不能同时导通,否则短路:
OCxREF: ¯¯¯¯¯|___________|¯¯¯¯¯
↓ ↓
OC1 (上管): ¯¯¯|__|_________|__|¯¯ ← 提前关
OC1N(下管): ___|__|¯¯¯¯¯¯¯¯¯|__|__ ← 延迟开
↑ ↑
死区时间 死区时间
(两个都关) (两个都关)
断路保护
正常运行: PWM正常输出驱动电机
突然检测到过流! → BRK引脚触发
↓ 硬件自动(不需要CPU介入)
MOE = 0
所有PWM输出立刻变为安全状态
电机停转,功率管保护
第七部分:输入滤波器(ICxF)详解
你的配置中 Input Filter 我建议设为 8,它的含义是:
ICxF = 0000: 无滤波,任何边沿都算
ICxF = 0011: 连续采样8次都是新电平才算有效
ICxF = 1000: 以 fDTS/8 频率采样,连续6次才算有效 ← 你用的
作用就是抗毛刺:
实际信号(有毛刺): _____|¯|_|¯¯¯¯¯¯¯¯¯¯¯|___
↑ 假的!
滤波后: ________|¯¯¯¯¯¯¯¯¯¯¯¯|___
↑ 这才是真的边沿
回到你的动平衡项目
你只用到了高级定时器很小一部分功能中的输入捕获。但你选的是 TIM2(通用定时器),不是高级定时器 TIM1/TIM8,所以那些互补输出、死区、断路等高级功能跟你没关系。
你实际用到的:
PSC = 239 → 把240MHz分频到1MHz
ARR = 4294967295 → 32位最大值,让计数器自由跑
CH1 = 输入捕获 → 光电头信号,抓时间戳
CH2 = 输入捕获 → 过零检测信号,抓时间戳
ARPE = Disable → ARR不会改,无所谓
滤波器 = 8 → 抗毛刺
中断 = 使能 → 捕获到边沿时通知CPU读数据
这就是全部。定时器的其他功能虽然强大,但你的场景不需要。