STM32 TIM — 基本定时器定时(1ms 中断)
开发板:野火 EBF STM32F103 Mini
固件库:HAL 库
对应例程:TIM—基本定时器定时
参考源码:User/main.c、User/tim/bsp_basic_tim.c、User/tim/bsp_basic_tim.h
定时器(TIMER)是 MCU 的"心脏节拍器"。STM32F103 有基本定时器(TIM6/7)、通用定时器(TIM2~5)、高级定时器(TIM1/8)。本文用最基础的 TIM6 产生 1ms 中断,在中断里累计毫秒数,主循环据此每 1 秒翻转一次 LED——这是后续所有"精确计时"应用的地基。
一、功能简介
- 把 TIM6 配置为 1ms 触发一次更新(溢出)中断;
- 每次中断在
HAL_TIM_PeriodElapsedCallback回调里把全局time加 1; - 主循环检测
time == 1000(即 1 秒到),清零并翻转 LED1。
效果:LED1 每 1 秒翻转一次,主循环完全不阻塞,可以并行做别的事。
核心知识点:
- 定时器时基:预分频(PSC)与自动重装载(ARR)如何决定溢出周期;
- 更新中断 + NVIC 配置;
- HAL 的定时器回调机制。
二、硬件准备
| 项目 | 说明 |
|---|---|
| 开发板 | 野火 EBF STM32F103 Mini |
| 定时器 | TIM6(基本定时器,无外部引脚) |
| 时钟源 | 内部时钟,挂载在 APB1(36MHz) |
| 指示 | LED1 → PC2 |
| 调试输出 | USART1(115200) |
TIM6/7 是纯定时用途的基本定时器,没有输入捕获/输出比较/PWM,只有时基单元和 update 中断,最适合做"系统滴答"。本例也可切换到 TIM7(
bsp_basic_tim.h里改宏即可)。
三、完整代码(带中文注释)
1. bsp_basic_tim.h(参数与宏)
|
|
2. bsp_basic_tim.c(定时器初始化与回调)
|
|
3. main.c(启动定时器 + 主循环判断)
|
|
四、代码分段讲解
1. 溢出周期怎么算?
定时器时钟来自 APB1,经倍频后 TIM6 实际时钟为 72MHz(F103 中 APB1 定时器时钟 = PCLK1×2 = 72M)。溢出时间公式:
|
|
Prescaler = 71:72M 先 ÷72 → 计数频率 1MHz(每计数 1 次 = 1µs);Period = 1000:计满 1000 次 → 1ms 到,产生更新中断。 (注:ARR=1000 实际计 1001 个边沿,误差极小,工程上直接用 1000 即可。)
2. HAL_TIM_Base_Start_IT 做了什么?
它启动计数器,并使能更新中断(UIE)。当计数器从 ARR 溢出归零时,硬件置更新标志、触发中断,跳进 TIM6_IRQHandler。
3. 中断怎么进到回调的?
stm32f1xx_it.c 里的 TIM6_IRQHandler 调用 HAL_TIM_IRQHandler(&htimx),后者清标志并调用 HAL_TIM_PeriodElapsedCallback。我们把累加逻辑写在这个弱函数里,就实现了"每 1ms 自增"。这就是 HAL 的回调式中断编程,无需自己写清标志等寄存器操作。
4. 为什么 time 要加 volatile?
time 在中断里被修改、在主循环里被读取。加 volatile 告诉编译器"它可能随时被意外修改",禁止把它的读取优化进寄存器缓存,否则主循环可能永远读不到最新值。
5. 与 HAL_Delay 的区别
HAL_Delay 也基于 SysTick 1ms 滴答,但它是阻塞的;本例用 TIM 中断做出非阻塞计时框架,主循环随时能响应别的任务,是更专业的写法。
五、实验现象
- 下载程序,串口打印提示语;
- 板载 LED1 每 1 秒翻转一次(亮 1 秒、灭 1 秒);
- 与主循环
while(1)轮询延时不同,此时若在主循环里加别的任务,LED 翻转节奏依然精准。
六、常见问题
Q1:LED 不闪或闪烁频率不对?
- 确认
HAL_TIM_Base_Start_IT已调用(只Init不Start不会计数); - 检查
Prescaler/Period计算是否与系统时钟匹配(系统须为 72MHz); - 确认 NVIC 中断已使能(
HAL_NVIC_EnableIRQ)。
Q2:进入中断后卡死/反复进中断?
- 回调里不要做耗时操作;长时间占用中断会影响系统;
- 若用轮询清标志方式,记得在中断里清 UPDATE 标志,否则会连续触发。
Q3:想做更长时间定时(如 1 分钟)?
- 不要在中断里设超大 ARR,而是让
time累计——每 1000 次(1 秒)在主循环判断换算,或再加一层second计数。
七、小结
- 基本定时器 = 预分频 + 自动重装载,决定溢出周期;
- 定时流程:开时钟 → 配时基 → 配 NVIC → Start_IT → 在回调里处理逻辑;
volatile全局变量 + 中断回调,是构建非阻塞"时间片"系统的标准范式。
八、拓展应用
- 多路软定时:用
time派生出second、minute,或做一个SoftTimer数组,实现多个独立周期任务(如 100ms 采样、500ms 刷屏)。 - 精准延时函数:基于
time实现delay_ms_nonblock()替代阻塞延时。 - 通用定时器进阶:TIM2~5 支持输入捕获(测频率/脉宽)和 PWM 输出(调光/电机),见
TIM—通用定时器-输入捕获与 PWM 章节。 - RTOS 时基:在 TIM 中断里调用
xTaskIncrementTick(FreeRTOS),把定时器变成操作系统的心跳。