【无标题】基于 STM32 定时器的多任务调度与 PWM 呼吸灯实现
基于 STM32 定时器的多任务调度与 PWM 呼吸灯实现
前言
LED 闪烁是 STM32 嵌入式开发最基础的实验案例,是单片机入门的典型程序。本实验在基础点灯实验之上,基于定时器、PWM 与外部中断技术,实现呼吸灯效果。同时对比阻塞式延时的缺陷,采用定时器中断构建事件驱动的裸机多任务框架。
本文实验基于 STM32F103 芯片,不使用实时操作系统,利用片内定时器外设,完成定时器多任务调度与双路 PWM 呼吸灯功能,验证事件驱动程序架构的优势。
1. 为什么 HAL_Delay 是**“伪并发”**?
参考如下典型阻塞式延时代码:
程序表面能够实现 LED 闪烁功能,但HAL_Delay属于阻塞式忙等待。程序执行到该函数时,CPU 持续循环等待,无法处理其他任务。如果后续增加别的周期性任务,任务会串行执行,相互耦合,无法实现真正的并发运行。
HAL_Delay底层原理源码:HAL_GetTick()读取 SysTick 中断每 1ms 更新的软件毫秒计数器,HAL_Delay依靠该计数器做轮询判断,属于忙等待机制。该方式逻辑简单,适合单任务简易程序;但多任务场景下会占用大量 CPU 资源,降低系统实时性,不适合对时序要求较高的嵌入式项目。
2. 真正的 “并发”:事件驱动 + 硬件定时器
单核单片机无法实现物理层面并行执行,但可以通过事件驱动实现宏观并发。
核心思想:CPU 正常运行主业务逻辑,当定时器计时到达设定时间后,硬件产生中断信号,唤醒 CPU 执行对应的定时任务。
STM32 通用定时器(TIM6、TIM7 等)可作为硬件定时单元实现该功能。
2.1 什么是定时器
定时器是单片机内部集成,可以通过编程控制。单片机的定时功能是通过计数来实现的,当单片机每一个机器周期产生一个脉冲时,计数器就加一。定时器的主要功能是用来计时,时间到达之后可以产生中断,提醒计时时间到,然后可以在中断函数中去执行功能。比如我们想让一个led灯1秒钟翻转一次,就可以使用定时器配置为1秒钟触发中断,然后在中断函数中执行led翻转的程序。
2.2 STM32 定时器
中等容量的STM32F103C8T6一共有7个定时器,包括1个高级控制定时器、3个通用定时器、2个看门狗定时器和Systick定时器。
2.3 高级定时器
高级定时器支持输入捕获、输出比较、PWM 波形生成,可输出带死区时间的互补 PWM 信号。依靠预分频器与 RCC 时钟,能够灵活调整脉冲宽度与波形周期。
高级控制定时器 (TIM1 和 TIM8) 与通用定时器相互独立,硬件资源不共享。
高级定时器特性:TIM1 可作为 6 通道三相 PWM 发生器,支持可编程死区互补 PWM;4 个独立通道支持输入捕获、输出比较、边沿 / 中心对齐 PWM、单脉冲模式。调试模式下可冻结计数器,关闭 PWM 输出,保护后端功率器件。
高级定时器与通用定时器架构相近,支持定时器联动,能够和通用定时器同步触发、链式事件响应。
2.4 通用定时器
通用定时器TIM2、TIM3和TIM4是4通道独立定时器,支持输入捕获和输出比较,可以产生PWM信号控制电机和电源管理。
通用定时器主要特性:
STM32F103 高性能系列内置多个通用定时器,为 16 位递增 / 递减计数器,搭配 16 位预分频器,每个定时器包含 4 个独立通道,支持输入捕获、输出比较、PWM 生成与单脉冲输出。
通用定时器可配合高级定时器完成同步触发,调试模式支持计数器冻结;可生成 PWM 波形,支持 DMA 请求;可采集正交编码器、霍尔传感器信号。
2.5 定时器基本参数
预分频
预分频器将定时器时钟TIMER_CK进行分频,分频后时钟PSC_CLK驱动计数器计数。分频系数由寄存器TIMERx_PSC配置,寄存器带缓冲,参数更新将在下一次更新事件生效。
分频公式:
PSC_CLK = TIMER_CK/ (TIMERx_PSC +1)
向上计数模式
计数器从 0 开始累加计数,到达自动重装载寄存器 ARR 设定值后溢出清零,同时产生更新事件。
更新事件
计数器上溢 / 下溢时触发更新事件,可触发 DMA 请求,在新计数周期开始时更新定时器参数,适用于实时控制场景。
2.6 定时器如何工作
- 配置定时器时钟源(本实验使用 72MHz 系统时钟);
- 设置预分频器 PSC 与自动重装载值 ARR;
- 启动定时器,硬件独立完成计数;
- 计数溢出后,自动产生定时器更新中断;
- CPU 响应中断,执行预设的中断回调函数。
整个计数过程由硬件独立完成,不需要 CPU 持续参与,直到中断事件触发。
3. 动手实现:独立定时器,周期性 LED 翻转任务
3.1 硬件资源规划
表格
| 功能 | 资源 | 说明 |
|---|---|---|
| 2 秒 LED 闪烁 | TIM3 通用定时器 | 定时中断触发 LED 电平翻转 |
| 板载 LED | PC13 | 低电平点亮,注意引脚极性 |
3.2 CubeMX 配置(关键参数)
基础工程配置参考相关文档,本节不再赘述。
开启时钟
从上图可以看到定时器的时钟来源主要分为两部分,第一部分来源于APB1,第二部分来源于APB2。APB1最大36MHz,APB2最大72MHz。
这里使用TIMER3和TIMER2,就要先使能其时钟,又因为TIMER3和TIMER2时钟来源于APB1,APB1的时钟在system_stm32f10x.c中定义。
对于APB1时钟来说,APB1是AHB总线的2分频率也就是 72 / 2 = 36,定时器时钟拥有一个x2的倍频会将当前总线APB1频率x2,将x2后的时钟频率作为挂载在APB1总线上定时器的时钟频率。
故综上所示,TIM2和TIM3的时钟是72MHz。
2.选取时钟源
3.配置定时器基本参数(以2s为例)
4.使能定时中断
3.3 代码架构 :中断回调 + 标志位
在主函数中开启 TIM3 定时中断:
定义全局标志变量以及定时器中断回调函数:
变量添加
volatile修饰:该变量在中断服务函数内修改、主循环读取。不加 volatile,编译器会进行优化,导致主循环无法读取到标志位更新。
主循环检测标志位,执行对应的任务逻辑:
3.4 效果展示
LED 每 2s 完成一次电平翻转;定时任务不受主循环任务影响,时序稳定。
定时器与PWM
4. 深入原理:为什么这个方案更优?
4.1 时间精度高
定时器依靠硬件系统时钟计时,误差仅由外部晶振稳定性决定;任务时序不受主循环负载影响,抖动小。
4.2 CPU 利用率高
计数过程由硬件完成,CPU 空闲时可执行其他业务逻辑,也可进入低功耗模式降低功耗。
4.3 任务解耦,易于扩展
新增定时任务,可新增定时器,或者利用同一个定时器采用取余方式实现多周期计时;修改定时周期,仅调整 ARR 参数,无需大量修改主程序逻辑。
5. 小总结:从 “忙等陷阱” 到 “事件驱动” 的嵌入式时间管理
嵌入式开发中,时序是重要的系统资源。HAL_Delay虽然使用简单,但会阻塞 CPU 执行,多任务场景下只能实现伪并发。
采用 STM32 硬件定时器 + 中断机制,能够搭建事件驱动的程序架构,具备以下优势:
- 时序精度高,基于 72MHz 硬件时钟,定时可控;
- 任务解耦,各个周期性任务独立运行,互不干扰;
- CPU 资源利用率高,主循环仅在事件触发时处理任务,可拓展性强。
该编程思想的核心转变:从主动轮询判断时间,改为等待硬件事件触发任务。单核单片机也可以实现多周期任务调度。
(二) 用 STM32 的 TIM3 和 TIM4 实现双路呼吸灯:PWM 的艺术,从 “亮灭” 到 “渐变”
上一节介绍了定时器中断实现多任务调度,摆脱阻塞延时带来的限制。本节在此基础上,实现呼吸灯效果。呼吸灯可以实现 LED 亮度平滑渐变,核心原理是 PWM 脉宽调制。
本实验使用 STM32 的 TIM3、TIM4 通用定时器,输出两路独立 PWM 信号,驱动两个 LED 实现同步渐变呼吸效果。
1. 呼吸灯的本质:PWM + 动态占空比
LED 只有高低电平两种状态,只能实现点亮与熄灭。利用人眼视觉暂留效应,快速切换 LED 的亮灭状态,就可以模拟出中间亮度。这就是 PWM(脉宽调制)原理:
- 固定 PWM 频率;
- 改变高电平持续时间,即占空比;
- 占空比 0%:熄灭;100%:最大亮度;50%:中等亮度。
呼吸灯通过周期性修改 PWM 占空比,实现渐亮、渐暗循环变化。
1.1 STM32 PWM 介绍
2. 为什么选 TIM3 和 TIM4?
STM32F103 的 TIM3、TIM4 属于通用定时器,每路定时器包含 4 个输出通道,能够直接输出硬件 PWM 波形。
定时器通道与 GPIO 引脚映射:
TIM3_CH1 → PA6
TIM4_CH1 → PB6
3. 系统设计
3.1 硬件连接
表格
| LED | 引脚 | 驱动方式 |
|---|---|---|
| 外接 LED | PA6 → LED → GND | TIM3_CH1 硬件 PWM 输出 |
| 外接 LED | PB6 → LED → GND | TIM4_CH1 硬件 PWM 输出 |
3.2 呼吸周期设计
- 呼吸总周期:2 s(1 s 渐亮 + 1 s 渐暗)
- PWM 载波频率:1 kHz,周期 1ms,消除人眼闪烁感
- 占空比更新步长:每 10ms 更新一次,一共 200 个亮度档位
3.3 CubeMX 配置详解(关键参数)
TIM3:硬件 PWM 输出(外接 LED)
- Clock Source: Internal Clock
- Channel 1: PWM Generation CH1
- Prescaler (PSC): 71 → 72 MHz / 72 = 1 MHz(计数周期 1 μs)
- Counter Period (ARR): 999 → PWM 周期 = (999+1) × 1μs = 1 ms(1 kHz)
- Pulse (CCR1): 初始值 0,程序运行中动态修改
TIM4:硬件 PWM 输出(外接 LED)
- Clock Source: Internal Clock
- Channel 1: PWM Generation CH1
- Prescaler (PSC): 71 → 72 MHz / 72 = 1 MHz(计数周期 1 μs)
- Counter Period (ARR): 999 → PWM 周期 = (999+1) × 1μs = 1 ms(1 kHz)
- Pulse (CCR1): 初始值 0
TIM2:10ms 节拍定时器
- PSC = 7199 → 计数时钟 10 kHz(100 μs 每计数)
- ARR = 99 → 中断周期 = 100 × 100 μs = 10 ms
3.4 核心代码实现
采用状态机实现呼吸逻辑,每 10ms 更新一次 PWM 占空比,两路 LED 同步变化。
全局变量定义:
主函数初始化配置,开启定时器中断与 PWM 输出:
定时器中断回调函数,同步修改两路 CCR 比较值:
✅ 关键点:
使用__HAL_TIM_SET_COMPARE修改 CCR 寄存器,执行效率高;两路 LED 使用同一占空比变量,保证同步渐变;亮度计算在中断内部完成,主循环负载低。
4. 效果展示
定时器与PWM
LED 同步完成渐亮、渐暗循环,无明显频闪,亮度变化平滑。
5. 小总结:PWM 是嵌入式工程师的 “调光魔法”
通过本实验,掌握多路硬件 PWM 配置方法、利用统一定时器节拍同步控制多个外设,搭建中断驱动、直接操作寄存器的高效程序架构。
PWM 技术不局限于 LED 调光,也是电机调速、音频信号生成、开关电源控制等项目的基础技术。
(三) 总结:从 “点灯” 到 “造节拍”,定时器在嵌入式系统中的应用
两组实验分别验证了定时器的两种典型用途:定时器中断实现周期性任务调度,硬件 PWM 实现模拟量输出。
二者核心硬件模块均为定时器。在 STM32 系统中,定时器是多功能外设,可作为多任务调度时基、PWM 波形发生器、输入捕获计时单元、电机控制驱动单元。HAL_Delay仅能实现简单延时;结合中断与 PWM 技术后,定时器可以构建一套时序可控的裸机系统。
在编写裸机程序时,应当优先考虑硬件外设完成时序控制,减少 CPU 阻塞等待。合理利用硬件资源,是嵌入式程序设计的核心思路。