摘要:TIM 配置 1ms 中断,示波器测出来却是 1.024ms?或者中断偶尔丢失?不是 SystemCoreClock 算错,而是 Counter Mode(计数模式)​ 与 Center-Aligned(中心对齐)​ 配置导致的隐性误差。本文还原 TIM 中断的真实时序。


一、问题描述(现象)

**TIM 配置 1ms 进一次中断;

用 IO 翻转测周期,发现有时 1ms,有时 2ms;

或者在主循环中加了一句延时,中断就不准了。**

很多工程师的排查方向是:

  1. SystemCoreClock不对?

  2. Prescaler算错了?

  3. 中断里代码太多?


二、原理分析

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 要减半

四、选型避坑建议

  1. HAL 库的坑

    • __HAL_TIM_SET_AUTORELOAD()默认不生成 Update 事件,可能导致第一次中断延迟。

  2. 优先级

    • TIM 中断优先级不要设太低,防止被 SPI/DMA 长时间抢占。

  3. 不要在中断里清 UIF

    • HAL 会自动清,手动清可能会丢失下一次中断。


五、总结 Checklist

  • [ ] 是否确认了 Counter Mode(Up / Center)?

  • [ ] 向上计数时,ARR 是否减了 1?

  • [ ] 中断里是否只做了轻量级操作?

  • [ ] 是否避免了在中断中使用 HAL_Delay?


六、写在最后(关注我,少走弯路)

我是 gqqsherry,一个拒绝调包、专注底层逻辑的嵌入式工程师。

TIM 是基础中的基础,但越是基础的东西,坑往往越隐蔽

关注我的专栏《嵌入式底层避坑指南》,至此 《TIM 底层避坑指南》系列正式完结

接下来我们将开启 电源管理底层避坑指南

👉 新系列预告:《LDO 啸叫怎么来的?别只换电容,看看环路稳定性与 ESR》


 

原创文章,转载请注明出处。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐