定时中断进不来?别只怪 Prescaler,看看 Counter Mode
摘要: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 -> IRQ
2. 核心参数
-
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 中断里做这些事:
-
printf -
HAL_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》
原创文章,转载请注明出处。
更多推荐

所有评论(0)