摘要:TIM 配置 1ms 中断,示波器测出来却是 1.024ms?或者中断偶尔丢失?不是 SystemCoreClock 算错,而是Counter Mode(计数模式) 与Center-Aligned(中心对齐) 配置导致的隐性误差。本文还原 TIM 中断的真实时序。
一、问题描述(现象)
**TIM 配置 1ms 进一次中断;
用 IO 翻转测周期,发现有时 1ms,有时 2ms;
或者在主循环中加了一句延时,中断就不准了。**
很多工程师的排查方向是:
SystemCoreClock不对?Prescaler算错了?中断里代码太多?
二、原理分析
1. 物理模型
TIM 中断触发链:
CLK -> PSC -> CNT -> ARR -> Update Event -> IRQ2. 核心参数
Prescaler (PSC):预分频器。
ARR:自动重装值。
Counter Mode:向上 / 向下 / 中心对齐。
3. 反直觉真相
“向上计数”和“中心对齐”的 ARR 含义完全不同。
Up Counting(向上):
CNT 从 0 数到 ARR,溢出产生中断。
实际周期 =
(ARR + 1)个计数周期。Center-Aligned(中心对齐):
CNT 从 0 → ARR → 0。
实际周期 =
(2 × ARR)个计数周期。
如果你把 Center-Aligned 当 Up 来配,定时直接翻倍。
三、工程级解决方案
方案 1:标准 1ms 定时配置(STM32 @ 72MHz)
向上计数模式(最常用):
// 72MHz / 72 = 1MHz (1us) htim.Init.Prescaler = 72 - 1; // 1000 次 = 1ms htim.Init.Period = 1000 - 1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(&htim);方案 2:避免中断里“堵车”
不要在 TIM 中断里做这些事:
printfHAL_Delay复杂运算
正确做法:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 只做标记 g_tim_flag = 1; }方案 3:Center-Aligned 的正确打开方式
如果必须用中心对齐(如 PWM 对称输出):
htim.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; htim.Init.Period = 500 - 1; // 注意:ARR 要减半四、选型避坑建议
HAL 库的坑:
__HAL_TIM_SET_AUTORELOAD()默认不生成 Update 事件,可能导致第一次中断延迟。
优先级:
TIM 中断优先级不要设太低,防止被 SPI/DMA 长时间抢占。
不要在中断里清 UIF:
HAL 会自动清,手动清可能会丢失下一次中断。
五、总结 Checklist
[ ] 是否确认了 Counter Mode(Up / Center)?
[ ] 向上计数时,ARR 是否减了 1?
[ ] 中断里是否只做了轻量级操作?
[ ] 是否避免了在中断中使用 HAL_Delay?
六、写在最后(关注我,少走弯路)
我是 gqqsherry,一个拒绝调包、专注底层逻辑的嵌入式工程师。
TIM 是基础中的基础,但越是基础的东西,坑往往越隐蔽。
关注我的专栏《嵌入式底层避坑指南》,至此《TIM 底层避坑指南》系列正式完结。
接下来我们将开启《电源管理底层避坑指南》。
👉新系列预告:《LDO 啸叫怎么来的?别只换电容,看看环路稳定性与 ESR》
原创文章,转载请注明出处。