ZZZ-10-定时器中断

如果说 GPIO 是单片机的“手脚”,中断是“神经反射”,那么定时器(Timer)就是单片机的“脉搏”。在 51 单片机里,配置定时器需要去算初值、写寄存器。而在 STM32 中,虽然寄存器更多,但有了 CubeIDE,我们只需要掌握一个核心公式就能实现极其精准的定时。

1. 形象比喻:定时器到底是怎么工作的

你可以把定时器想象成一个“自动数数的小学生”**:

  • 时钟源(Clock Source):老师敲黑板的速度(比如每秒敲 72,000,000 下)。
  • 预分频器(PSC, Prescaler):小学生觉得老师敲得太快了,数不过来,于是规定“老师敲 7200 下,我才记 1 个数”。
  • 计数器(Counter):小学生手里的小本子,每记满一个数就加 1。
  • 自动重装载寄存器(ARR, Auto-Reload Register):老师规定的“目标数”。比如数到 10000,就举手报告(触发中断),然后擦掉本子从 0 重新数。

核心公式(必须死记硬背,它是定时器的灵魂)

你想定多长时间,全靠这个公式:

$$定时时间 (秒) = \frac{(PSC + 1) \times (ARR + 1)}{定时器输入频率 (Hz)}$$

对于 STM32F103C8T6,如果你的时钟树配置为 72MHz

  • 如果你想要 1 秒(1Hz)触发一次中断:
    • 我们将 预分频器 PSC 设为 7199(即 $7200 - 1$)。
    • 我们将 自动重装载寄存器 ARR 设为 9999(即 $10000 - 1$)。
    • 计算:$\frac{7200 \times 10000}{72,000,000} = 1\text{ 秒}$。

为什么都要 -1? 因为计数器是从 0 开始数的。数 10 个数,实际上是 0 到 9。

3. STM32CubeIDE 配置步骤

我们以 TIM2(通用定时器)为例:

  1. 开启定时器:
    • .ioc 文件左侧找到 Timers -> TIM2。
    • Combined Channels 不用管,将 Clock Source 设为 Internal Clock(内部时钟)。
  2. 设置参数:
    • Prescaler (PSC): 输入 7199
    • Counter Period (ARR): 输入 9999
    • 其他默认即可(向上计数模式)。
  3. 开启中断 (NVIC):
    • 点击上方的 NVIC Settings 选项卡。
    • 勾选 TIM2 global interrupt 后面的 Enabled。(不勾这个,定时器只会数数,不会通知 CPU)
  4. 生成代码:按 Ctrl + S

4. 编写代码(开启与回调)

定时器配置完后,默认是关闭的,你需要手动在代码里给它“上发条”。

第一步:启动定时器中断

main.cMX_GPIO_Init() 之后,while(1) 之前,加入这行代码:

1
2
3
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2); // 以中断模式启动 TIM2
/* USER CODE END 2 */

第二步:编写回调函数

main.c 下方的 USER CODE BEGIN 4 区域,写下我们要执行的动作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* USER CODE BEGIN 4 */
// 定时器溢出(时间到)时,会自动调用这个函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2) // 确认是 TIM2 触发的
    {
        // 要执行的代码
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 翻转 LED 灯
        
    }
}
/* USER CODE END 4 */

5. 定时中断 vs HAL_Delay()

特性 HAL_Delay() 定时器中断
原理 阻塞式(CPU 在死等,啥也干不了) 非阻塞式(CPU 平时干别的,时间到才去处理)
精度 较低(会被其他任务干扰) 极高(硬件自动计数,不受软件干扰)
用途 简单的、不重要的等待 精准采样、控制电机速度、多任务并行

6. 进阶:如果你想要 1 毫秒定一次时

  • 输入频率 = 72,000,000 Hz
  • 目标时间 = 0.001 s
  • 设置:PSC = 71 ($72-1$),ARR = 999 ($1000-1$)。
  • 计算:$\frac{72 \times 1000}{72,000,000} = 0.001\text{ 秒}$。

常见避坑指南

  1. 忘记加 _IT:如果是调用 HAL_TIM_Base_Start(),定时器会跑,但不会进中断函数。必须用 HAL_TIM_Base_Start_IT()
  2. 公式没减 1:虽然不减 1 也能跑,但时间会差那么一点点。在追求极致精准的工业控制中,这 1 个周期的误差很重要。
  3. 中断里代码太长:如果你的定时器是 1ms 触发一次,但你在中断函数里执行了需要 2ms 才能跑完的代码,系统就会彻底乱套。
Licensed under CC BY-NC-SA 4.0