STM32 入门实战:用定时器中断 + PWM 实现多任务以及PWM呼吸灯
本次实验成功通过定时器中断实现了5秒串口定时发送和2秒LED闪烁,验证了定时器在多任务并发中的优势——精准定时且不占用CPU空闲资源。通过定时器PWM模式实现了2个LED的呼吸灯效果,理解了PWM占空比与LED亮度的关系,掌握了PWM参数(周期、占空比)的配置方法。对比定时器与非定时器方法可知,定时器是实现精准、高效多周期性任务的核心组件,非定时器方法仅适用于对精度要求低、无其他高优先级任务的场景
基于STM32的定时器中断与PWM输出实验报告
一、实验原理
- 定时器中断原理:STM32定时器可通过配置预分频系数(PSC)和自动重装载值(ARR)实现精准定时。定时器计数达到ARR值时触发中断,CPU暂停当前任务处理中断服务函数,处理完成后返回原任务,实现“闹钟式”定时,不占用CPU空闲资源。
- PWM原理:脉冲宽度调制(PWM)通过定时器生成周期性方波,通过改变高电平时间(占空比)调整输出平均电压。当占空比从0%逐步增加到100%时,LED亮度逐渐变亮;从100%逐步降低到0%时,LED亮度逐渐变暗,最终实现呼吸灯效果。
- 多任务并发原理:通过2个独立定时器(如TIM1、TIM2)分别触发中断,中断服务函数分别处理“串口发送”和“LED闪烁”任务,CPU在中断间隙处理其他任务,实现多周期性任务并行执行。
二、实验步骤
第一部分:定时器中断实现多任务(5秒串口发送+2秒LED闪烁)
1. STM32CubeMX配置
-
新建工程:打开STM32CubeMX,点击“File”→“New Project”,在搜索框输入“STM32F103C8T6”,选择对应芯片后点击“Start Project”。
-
配置时钟:
- 点击左侧“RCC”,在“High Speed Clock (HSE)”处选择“Crystal/Ceramic Resonator”(外部晶振)。
- 点击左侧“Clock Configuration”,将“HSE”设为8MHz,“PLL Multiplier”设为9,使系统时钟(SYSCLK)达到72MHz,然后依次配置AHB、APB1、APB2时钟,最终确保APB1 Timer clocks为36MHz(APB1预分频系数设为2),点击“OK”保存。



-
配置GPIO(LED引脚):
- 开发板自带LED接PC13,点击左侧“GPIO”,找到“PC13”引脚,设置为“GPIO_Output”。
- 点击“PC13”,在“GPIO mode and configuration”中,将“GPIO Pull-up/Pull-down”设为“Pull-up”(避免引脚悬空),“GPIO Output Level”设为“High”(初始状态LED灭)。

-
配置USART1(串口发送):
- 点击左侧“USART1”,将“Mode”设为“Asynchronous”(异步模式)。
- 配置串口参数:波特率115200、数据位8、停止位1、校验位None,其他默认。
- 点击“GPIO Settings”,确认TX引脚为PA9,RX引脚为PA10(默认引脚,无需修改)。

-
配置定时器TIM1(2秒LED闪烁):
- 点击左侧“TIM1”,将“Clock Source”设为“Internal Clock”(内部时钟)。
- 点击“Parameter Settings”,配置参数:
- Prescaler (PSC):35999(预分频系数,36MHz/(35999+1)=1kHz,计数频率1ms)。
- Counter Mode:Up(向上计数模式)。
- Auto Reload Preload:Enable(使能自动重装载)。
- Auto-reload value (ARR):1999(计数到2000时溢出,1ms×2000=2000ms=2秒)。
- 配置中断:点击“NVIC Settings”,勾选“TIM1 update interrupt”,将“Priority Group”设为“2”,“Preemption Priority”设为“1”,“Sub Priority”设为“0”(中断优先级可自定义,避免冲突即可)。


-
配置定时器TIM2(5秒串口发送):
- 点击左侧“TIM2”,将“Clock Source”设为“Internal Clock”。
- 点击“Parameter Settings”,配置参数:
- Prescaler (PSC):35999(计数频率1ms,与TIM1一致)。
- Counter Mode:Up。
- Auto Reload Preload:Enable。
- Auto-reload value (ARR):4999(1ms×5000=5000ms=5秒)。
- 配置中断:点击“NVIC Settings”,勾选“TIM2 update interrupt”,“Priority Group”设为“2”,“Preemption Priority”设为“0”,“Sub Priority”设为“0”(TIM2中断优先级高于TIM1,确保串口发送不被频繁打断)。

-
生成工程:
- 点击“Project Manager”→“Project”,设置“Project Name”(如“Timer_Interrupt”),“Project Location”选择保存路径,“Toolchain/IDE”选择“MDK-ARM V5”。
- 点击“Code Generator”,勾选“Generate peripheral initialization as a pair of .c/.h files per peripheral”,避免代码集中在main.c中,点击“Generate Code”生成工程,生成完成后点击“Open Project”打开Keil5。
2. Keil5代码编写与调试
-
打开工程后,展开“Src”文件夹,双击“stm32f1xx_it.c”(中断服务函数文件),找到以下两个中断服务函数并修改:
-
TIM1中断服务函数(处理2秒LED闪烁):
void TIM1_UP_IRQHandler(void) { /* USER CODE BEGIN TIM1_UP_IRQn 0 */ /* USER CODE END TIM1_UP_IRQn 0 */ HAL_TIM_IRQHandler(&htim1); /* USER CODE BEGIN TIM1_UP_IRQn 1 */ // 清除中断标志(HAL库函数已处理,此处可省略) // 翻转PC13引脚电平(LED闪烁) HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); /* USER CODE END TIM1_UP_IRQn 1 */ } -
TIM2中断服务函数(处理5秒串口发送):
void TIM2_IRQHandler(void) { /* USER CODE BEGIN TIM2_IRQn 0 */ /* USER CODE END TIM2_IRQn 0 */ HAL_TIM_IRQHandler(&htim2); /* USER CODE BEGIN TIM2_IRQn 1 */ // 向串口发送“hello windows!”,换行符\r\n用于串口助手换行 HAL_UART_Transmit(&huart1, (uint8_t*)"hello windows!\r\n", strlen("hello windows!\r\n"), 100); /* USER CODE END TIM2_IRQn 1 */ }
-
-
补充头文件:在“stm32f1xx_it.c”开头添加
#include <string.h>,否则strlen函数会报错。 -
使能定时器中断:双击“main.c”,在
main函数的/* USER CODE BEGIN 2 */后添加以下代码,启动TIM1和TIM2的定时器中断:// 启动TIM1定时器中断 HAL_TIM_Base_Start_IT(&htim1); // 启动TIM2定时器中断 HAL_TIM_Base_Start_IT(&htim2); -
编译工程:点击Keil5工具栏“Build”按钮(或按F7),确保编译无错误(0 Errors,0 Warnings)。
-
下载程序:
- 连接开发板:用USB线将开发板与电脑连接(若开发板无USB下载功能,需通过USB转串口模块连接,确保BOOT引脚配置正确:下载时BOOT0=1,BOOT1=0;下载后BOOT0=0,BOOT1=0)。
- 配置下载器:点击Keil5工具栏“Options for Target”(魔术棒图标),选择“Debug”→“Use”,选择对应下载器(如“ST-Link Debugger”),点击“Settings”,确认“Port”为“SW”,“Reset Mode”为“Auto Detect”,点击“OK”。
- 下载程序:点击“Download”按钮(或按F8),程序下载完成后,开发板自动重启。


3. 实验现象观察
- LED闪烁:开发板上PC13引脚的LED每2秒翻转一次(灭→亮→灭→亮…),周期2秒。
- 串口发送:打开串口调试助手,选择对应串口(如COM3),波特率115200,点击“打开串口”,每隔5秒接收一次“hello windows!”

第二部分:定时器PWM实现呼吸灯
步骤一:在原有工程上配置PWM
- 返回STM32CubeMX工程(
.ioc文件)。


2.配置TIM4的Channel1产生PWM:
- 找到 Timers > TIM4。
- 将 Channel1 设置为 PWM Generation CH1。这会将 PB6 引脚自动配置为复用功能。
- 在 Parameter Settings 选项卡下配置PWM参数:
- Prescaler:
71 - Counter Period:
499 - Pulse: 初始为0,代码中动态修改。
- CH Polarity: 保持 High
- Prescaler:
计算原理:
- TIM4时钟为72MHz。
- 分频:72MHz / (71 + 1) = 1 MHz。
- 周期:(499 + 1) / 1 MHz = 500 us -> PWM频率 = 2 kHz(频率足够高,人眼看不到闪烁)。
- Pulse 值即为比较寄存器(CCR)的值,它决定了占空比。占空比 = Pulse / (Period + 1)。

3.配置TIM3的Channel1产生PWM(用于板载LED):
- 注意:第一部分我们用了TIM3做2秒定时,现在需要修改。
- 找到 TIM3,将其 Clock Source 改为 Internal Clock。
- 将 Channel1 设置为 PWM Generation CH1。这会将 PA6 引脚自动配置。
- 配置参数与TIM4完全相同:
- Prescaler:
71 - Counter Period:
499 - Pulse: 0
- Prescaler:

步骤二:重新生成代码
- 点击 GENERATE CODE,选择“Yes”覆盖之前的工程。
步骤三:在Keil中修改代码
-
打开Keil工程。
-
在
main.c的/* USER CODE BEGIN 2 */部分,启动两个PWM通道。/* USER CODE BEGIN 2 */ // 启动TIM3和TIM4的PWM通道1 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); /* USER CODE END 2 */ -
在
main函数的while(1)循环中,编写呼吸灯逻辑。/* USER CODE BEGIN WHILE */ uint8_t pwmDirection = 0; // 0: 增加亮度, 1: 减小亮度 uint16_t pwmValue1 = 0; // 用于TIM3 (板载LED) 的Pulse值 uint16_t pwmValue2 = 0; // 用于TIM4 (外接LED) 的Pulse值 while (1) { // 更新TIM3 (板载LED) 的占空比 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwmValue1); // 更新TIM4 (外接LED) 的占空比,可以设置一个不同的初始值让效果错开 __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, pwmValue2); // 使用HAL_Delay控制变化速度 HAL_Delay(10); // 呼吸灯逻辑:先增后减 if(pwmDirection == 0) { // 增加亮度 pwmValue1 += 5; pwmValue2 += 5; if(pwmValue1 >= 500) // Period是499,所以最大值是500 { pwmValue1 = 500; pwmValue2 = 500; pwmDirection = 1; // 切换为减小 } } else { // 减小亮度 pwmValue1 -= 5; pwmValue2 -= 5; if(pwmValue1 <= 0) { pwmValue1 = 0; pwmValue2 = 0; pwmDirection = 0; // 切换为增加 } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
步骤四:硬件连接与测试
-
外接LED:将LED正极通过一个220Ω电阻连接到 PB6,负极连接GND。
- 板载LED已在PC13,但注意其是反向的(高电平熄灭,低电平点亮)。我们的PWM输出在PA6,所以需要用杜邦线将 PA6 连接到板载LED所在的 PC13 引脚上(或者飞线),或者修改代码逻辑。最简单的方法是:不接PC13,将两个外接LED分别接在PA6(TIM3_CH1)和PB6(TIM4_CH1)上。
-
重新编译并下载程序到开发板。
-
观察现象:两个LED均应呈现平滑的呼吸灯效果。

三、总结
- 本次实验成功通过定时器中断实现了5秒串口定时发送和2秒LED闪烁,验证了定时器在多任务并发中的优势——精准定时且不占用CPU空闲资源。
- 通过定时器PWM模式实现了2个LED的呼吸灯效果,理解了PWM占空比与LED亮度的关系,掌握了PWM参数(周期、占空比)的配置方法。
- 对比定时器与非定时器方法可知,定时器是实现精准、高效多周期性任务的核心组件,非定时器方法仅适用于对精度要求低、无其他高优先级任务的场景。
三、总结
- 本次实验成功通过定时器中断实现了5秒串口定时发送和2秒LED闪烁,验证了定时器在多任务并发中的优势——精准定时且不占用CPU空闲资源。
- 通过定时器PWM模式实现了2个LED的呼吸灯效果,理解了PWM占空比与LED亮度的关系,掌握了PWM参数(周期、占空比)的配置方法。
- 对比定时器与非定时器方法可知,定时器是实现精准、高效多周期性任务的核心组件,非定时器方法仅适用于对精度要求低、无其他高优先级任务的场景。
更多推荐



所有评论(0)