13.4 通用定时器项目1:PWM呼吸灯
# 13.4 通用定时器项目1:PWM呼吸灯
本实验通过STM32的TIM3定时器实现三色LED呼吸灯效果,结合PWM占空比动态调节与中断控制,实现颜色渐变与切换。
核心功能:
- PWM呼吸灯:亮度从0%渐变至100%,再渐变为0%。
- 颜色轮换:红、绿、蓝三色LED周期性切换。
- OLED显示:实时显示当前激活的LED颜色。
# 13.4.1 LED硬件电路
# 三色LED连接
- 引脚分配:
- PA7 → TIM3_CH2(蓝色LED)
- PB0 → TIM3_CH3(红色LED)
- PB1 → TIM3_CH4(绿色LED)
- 限流电阻:防止LED过流损坏。
# 13.4.2 STM32CubeMX配置
# 1. 建立新工程
- 芯片型号:STM32F103C8T6
- 项目命名:13 TIM_PWM
- 主频:72MHz
- 调试接口:SWD
# 2. 配置TIM3
# 1. 使能TIM3
①点击“Pinout & Configuration”标签页→②在左侧导航栏中点击“Timers”→③双击“TIM3”进行配置→④点击“Internal Clock”内部时钟→⑤TIM3通道2、3、4选择“PWM Generation CH2、3、4”。

# 2. 配置TIM3工作参数
- Counter Mode(计数模式):Up;
- Counter Period(计数周期PRD):100-1;
- Internal Clock Divison(内部时钟分频CKD):No Division;
- Pulse(自动重装载寄存器ARR):50;
- auto-reload preload(自动重装载影子寄存器):Enable;
- Mode(输出模式):PWM mode 1;
- Output compare preload(输出比较影子寄存器):Enable;
- Fast Mode(快速模式):Disable;
- CH Polarity(通道极性):High;

# 3. 配置I2C1
参考第9章,使能I²C。
# 4. 引脚分配与复用
- PA7自动配置为TIM3_CH2;
- PB0自动配置为TIM3_CH3;
- PB1自动配置为TIM3_CH4;
- PB9自动配置为I2C1_SDA;
- PB8自动配置为I2C1_SCL;
- 若引脚未自动分配或分配到其他引脚,需手动点击选择功能。

# 5. 使能TIM3的更新中断
参考第7章,在“VNIC Settings”中勾选TIM3的更新中断。
# 6. 生成代码
参考第4章,在“Project Manager”标签页中的“Code Generator”导航栏,勾选为每个外设生成单独的“.c”和“.h”文件的选项。 最后,点击“Project→Generate Code”或工具栏的齿轮图标,再或者按下“Ctrl+S”快捷键保存,生成工程代码。在弹出的对话框中勾选“Open project”选项,即可进入代码编辑界面。经过上述的操作,STM32CubeIDE软件配置基本完成。
# 13.4.3 通用定时器项目1软件设计
# 1. TIM3初始化函数(代码清单13‑3)
首先介绍TIM3的初始化函数,初始化函数将用户定义的初始化结构体中的配置参数传递给底层硬件。该初始化函数设置TIM3寄存器(如预分频值、自动重装载值等),并使能外设时钟、配置GPIO引脚复用功能以及设置PWM模式。通过结构体参数即可完成配置。同时,确保TIM3外设的硬件状态与软件配置严格匹配。
static void MX_TIM3_Init(void)
{
// 定义并初始化定时器时钟配置结构体
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
// 定义并初始化定时器主模式配置结构体
TIM_MasterConfigTypeDef sMasterConfig = {0};
// 定义并初始化定时器输出比较配置结构体
TIM_OC_InitTypeDef sConfigOC = {0};
htim3.Instance = TIM3; // 配置TIM3定时器的实例
// 设置预分频器,720-1表示时钟频率为72MHz / 720 = 100kHz
htim3.Init.Prescaler = 720-1;
// 设置计数器模式为向上计数
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
// 设置自动重装载寄存器的值,100-1表示计数到100时溢出
htim3.Init.Period = 100-1;
// 设置时钟分频为不分频
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// 禁用自动重装载预装载功能
htim3.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}// 初始化定时器基础配置,如果初始化失败则调用错误处理函数
// 配置定时器时钟源为内部时钟
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
// 应用时钟源配置,如果配置失败则调用错误处理函数
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}// 初始化定时器的PWM模式,如果初始化失败则调用错误处理函数
// 配置定时器的主模式,设置主输出触发为复位
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
// 禁用主从模式
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if(HAL_TIMEx_MasterConfigSynchronization(&htim3,
&sMasterConfig) != HAL_OK)
{
Error_Handler();
}// 应用主模式配置,如果配置失败则调用错误处理函数
// 配置PWM模式为PWM模式1
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 50; // 设置PWM的脉冲宽度为50
// 设置输出极性为高电平有效
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
// 禁用输出比较快速模式
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if(HAL_TIM_PWM_ConfigChannel(&htim3,&sConfigOC,TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}// 配置TIM3的通道2为PWM输出,如果配置失败则调用错误处理函数
// 禁用通道2的预装载功能
__HAL_TIM_DISABLE_OCxPRELOAD(&htim3, TIM_CHANNEL_2);
if(HAL_TIM_PWM_ConfigChannel(&htim3,&sConfigOC,TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}// 配置TIM3的通道3为PWM输出,如果配置失败则调用错误处理函数
// 禁用通道3的预装载功能
__HAL_TIM_DISABLE_OCxPRELOAD(&htim3, TIM_CHANNEL_3);
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC,
TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}// 配置TIM3的通道4为PWM输出,如果配置失败则调用错误处理函数
// 禁用通道4的预装载功能
__HAL_TIM_DISABLE_OCxPRELOAD(&htim3, TIM_CHANNEL_4);
// 初始化定时器的MSP(MCU Specific Package)后处理
HAL_TIM_MspPostInit(&htim3);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 2. 主函数代码(代码清单13‑4)
主函数首先初始化硬件外设,包括GPIO、TIM3、I2C1和OLED,并启动TIM3定时器中断。在主循环中,通过两个for循环动态调整TIM3的三个PWM通道的占空比,实现LED亮度的渐变呼吸灯效果。同时通过OLED_NewFrame( )、OLED_PrintString( )和OLED_ShowFrame( )函数实时更新OLED显示内容,显示当前LED状态信息。
#include "main.h"
#include "font.h"
#include <stdio.h>
#include <string.h>
#include "oled.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
MX_I2C1_Init();
HAL_Delay(20);
OLED_Init();
HAL_TIM_Base_Start_IT(&htim3);
while (1)
{
OLED_NewFrame();
OLED_PrintString(1,1,message,&font16x16,OLED_COLOR_NORMAL);
OLED_ShowFrame();
for(int i=0;i<100;i++)
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,i);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_3,i);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,i);
HAL_Delay(10);
}
for(int i=99;i>=0;i--)
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,i);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_3,i);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_4,i);
HAL_Delay(10);
}
Led ++;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 3. 中断回调函数(代码清单13‑5)
在定时器中断回调函数中,根据全局变量Led的值,每次主循环中Led++对3取模,循环切换三个PWM通道的启动和停止状态,对应红色、绿色和蓝色LED的亮灭,并通过sprintf( )函数更新message的内容以反映当前LED状态。
/* USER CODE BEGIN 0 */
int Led=0;
char message[20];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim3)
{
switch(Led%3)
{
case 0:
sprintf(message,"Led:Red");
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);
break;
case 1:
sprintf(message,"Led:Green");
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
break;
case 2:
sprintf(message,"Led:Blue");
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_3);
break;
}
}
}
/* USER CODE END 0 */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 13.4.4 通用定时器项目1下载验证
# 现象与步骤
- 烧录程序:通过ST-LINK将代码下载至开发板。
- 运行效果:
- LED依次显示红、绿、蓝三色呼吸灯效果。
- OLED同步显示当前颜色(如
Led:Red
)。
- 硬件验证:
- 呼吸灯亮度平滑变化,无闪烁。
- 颜色切换周期与中断触发同步。
# 
关键说明:
- PWM周期计算:T_PWM = (PSC+1) × (ARR+1) / CLK = (720 × 100) / (72×10⁶) = 1 ms
- 中断触发:每次计数器溢出(1ms)触发颜色切换。
- 占空比控制:通过
__HAL_TIM_SET_COMPARE
动态修改CCR值实现亮度调节。