文章目录
- [1 准备材料](#1 准备材料)
- [2 知识讲解](#2 知识讲解)
-
- [2.1 PWM 是什么](#2.1 PWM 是什么)
- [2.2 STM32 定时器产生 PWM 需要的四个关键参数](#2.2 STM32 定时器产生 PWM 需要的四个关键参数)
- [2.3 必记公式](#2.3 必记公式)
- [2.4 CNT 与 CCR 比较时的输出电平规则](#2.4 CNT 与 CCR 比较时的输出电平规则)
- [3 实验](#3 实验)
-
- [3.1 低频 PWM 闪烁观察](#3.1 低频 PWM 闪烁观察)
- [3.2 PWM控制呼吸灯](#3.2 PWM控制呼吸灯)
-
- [3.2.1 为PWM选择合适引脚](#3.2.1 为PWM选择合适引脚)
- [3.2.2 CubeMX 配置](#3.2.2 CubeMX 配置)
- [3.2.3 编写代码](#3.2.3 编写代码)
- [3.2.4 硬件连接](#3.2.4 硬件连接)
1 准备材料
- 开发板(STM32F103C8T6)
- STM32CubeMX软件(Version 6.15.0)
- ST-LINK/V2 驱动
- keil µVision5 IDE(MDK-Arm)
2 知识讲解
2.1 PWM 是什么
PWM(脉冲宽度调制) 是一种通过快速开关信号,并用 每个周期内高电平的时间比例 来控制平均输出电压(或亮度、速度等)的技术。
- 核心思想:开关速度快到人眼/负载反应不过来,只能感受到平均值。
- 两个关键参数 :
- 频率 :每秒开关的次数(单位 Hz)。频率越高,波动越小。
- 频率(Hz)= 1 / 周期(秒)
- 例如 1000 Hz → 周期 = 1/1000 秒 = 1 ms。
- 例如 1 Hz → 周期 = 1 秒 = 1000 ms。
- 占空比:一个周期内高电平时间占整个周期的百分比。
- 频率 :每秒开关的次数(单位 Hz)。频率越高,波动越小。
举个 LED 例子 :
频率 = 1000 Hz(每秒开关 1000 次),对应周期为1ms,占空比 = 50% → 每个周期内亮 0.5 ms、灭 0.5 ms,人眼看到半亮。
占空比越大,LED 越亮。
2.2 STM32 定时器产生 PWM 需要的四个关键参数
在 STM32 的通用定时器(如 TIM3)中,要产生 PWM 波形,需要配置 4 个数值:
| 参数 | 全称 | 作用 | 补充说明 |
|---|---|---|---|
| PSC | Prescaler(预分频器) | 控制每数一步的时间(计数时钟频率) | PSC 越大,每步时间越长,频率越低 |
| ARR | Auto Reload Register(自动重装载值) | 一个 PWM 周期内计数多少步 | 步数 = ARR+1,决定占空比分母 |
| CCR | Capture/Compare Register(捕获/比较寄存器) | 决定占空比 | 与 CNT 比较,决定输出有效电平的步数(极性 High 时有效电平为高电平) |
| 占空比 | Duty Cycle | 实际效果:高电平时间 / 周期时间 | CCR / (ARR+1) |
注意:ARR 和 CCR 都是寄存器 ,通过 CubeMX 设置 ARR,通过
__HAL_TIM_SET_COMPARE设置 CCR。
2.3 必记公式
- PWM 频率 = 定时器时钟源频率 ÷ ( 预分频器 PSC + 1) ÷ ( 自动重装载值 ARR + 1)
- 占空比 = CCR ÷ (ARR + 1)
2.4 CNT 与 CCR 比较时的输出电平规则
在 STM32 定时器的 PWM 模式中,极性 仅仅反转有效电平的定义:
CNT为定时器计数器值
| 极性设置 | CNT< CCR | CNT ≥ CCR |
|---|---|---|
| High(高电平有效) | 输出高电平 | 输出低电平 |
| Low(低电平有效) | 输出低电平 | 输出高电平 |
本次实验中使用极性 High,因此 CCR 越大,高电平时间越长,LED 越亮。

3 实验
3.1 低频 PWM 闪烁观察
目的:把 PWM 频率降低到人眼能分辨的范围(< 10 Hz),亲眼看到LED 在一个周期内"先亮后灭"(或"先灭后亮")的完整过程,从而直观理解占空比 = 亮的时间 / 周期 的含义。
本次以1Hz为例
PWM 频率 = 定时器时钟源频率 ÷ ( 预分频器 PSC + 1) ÷ ( 自动重装载值 ARR + 1)
PSC = 35999 → PSC+1 = 36000
ARR = 1999 → ARR+1 = 2000
频率 = 72,000,000 ÷ 36000 ÷ 2000 = 1 Hz
周期 = 1 秒 = 1000 毫秒
| 系统时钟(定时器时钟源) | 72MHz 1M = 1000千 |
|---|---|
| PSC | 35999 |
| ARR | 1999 |


占空比 = CCR ÷ (ARR + 1)
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 1000); 这个函数的作用是:设置定时器 TIM3 的通道 1 的比较值(也就是占空比的控制值)为 1000 其中占空比比例为 1000 /(ARR+1)=1000 / (1999+1)= 50%


即可以肉眼观察到led亮0.5s,灭0.5s的过程

可以尝试修改 CCR 为 500(25% 占空比)或 1500(75% 占空比),观察亮灭时间的变化。
3.2 PWM控制呼吸灯
3.2.1 为PWM选择合适引脚

第一步:查看数据手册中的引脚定义表
关键点 :在 "默认复用功能" 列中,如果某个引脚标注了 TIMx_CHy(比如 TIM3_CH1),就表示它可以作为定时器 TIM3 的通道 y 输出 PWM。
第二步:理解命名规则
TIMx:定时器编号,如 TIM1、TIM2、TIM3、TIM4 等。CHy:通道编号,每个定时器通常有 1~4 个通道(CH1~CH4)。
例如 TIM3_CH1 表示:定时器 3 的通道 1。
第三步:确认定时器时钟来源
不同的定时器挂在不同的总线上:
- TIM2、TIM3、TIM4 挂在 APB1 总线上
- TIM1、TIM8 挂在 APB2 总线上

本次我们使用引脚PA6演示

3.2.2 CubeMX 配置
-
新建工程:打开 STM32CubeMX,选择你的 MCU 型号(如 STM32F103C8T6)
-
配置调试接口:进入 Pinout & Configuration 视图,点击 SYS,Debug 选择 Serial Wire(避免 JTAG 引脚冲突影响调试)
-
配置时钟源:点击 RCC,High Speed Clock (HSE) 选择 Crystal/Ceramic Resonator
-
配置时钟树:将 HCLK 设为 72MHz(STM32F103 最高主频),系统会自动计算各总线分频
-
配置 TIM3 PWM 输出:
- 在 Pinout 视图中找到 PA6 引脚,点击选择 TIM3_CH1
- 点击 TIM3,勾选 Channel 1 为 PWM Generation CH1
- Clock Source 选择 Internal Clock
- Parameter Settings 中配置:
- Prescaler:71(预分频值)
- Counter Mode:Up(向上计数)
- Counter Period:999(自动重装载值 ARR)
- Auto-reload preload:Enable
- PWM Generation Channel 1 中配置:
- Mode:PWM mode 1
- Pulse:0(初始占空比)
- CH Polarity:High(高电平有效)
PWM 频率计算公式:频率 = 72MHz / (Prescaler+1) / (Counter Period+1) = 72000000 / 72 / 1000 = 1000Hz

-
配置 GPIO :CubeMX 会自动将 PA6 设为复用推挽输出模式,无需手动调整
-
生成代码:
- Project Manager 中设置工程名、保存路径、Toolchain/IDE(如 MDK-ARM)
- 勾选 "Generate peripheral initialization as a pair of '.c/.h' files"
- 点击 GENERATE CODE
3.2.3 编写代码
在生成的工程中,找到 main.c,在 /* USER CODE BEGIN */ 和 /* USER CODE END */ 注释块之间添加代码,注意不要写在注释块外面,否则重新生成代码时会被覆盖。
c
/* USER CODE BEGIN 0 */
uint16_t dutyCycle = 0; // 占空比变量
uint8_t direction = 1; // 1=渐亮, 0=渐暗
/* USER CODE END 0 */
c
/* USER CODE BEGIN 2 */
// 启动 TIM3 通道 1 的 PWM 输出
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
/* USER CODE END 2 */
c
/* USER CODE BEGIN 3 */
while (1)
{
// 逐渐改变占空比,实现呼吸效果
if (direction == 1) // 渐亮
{
dutyCycle += 5;
if (dutyCycle >= 990) // 到达最亮后切换方向
{
dutyCycle = 990;
direction = 0;
}
}
else // 渐暗
{
dutyCycle -= 5;
if (dutyCycle <= 10) // 到达最暗后切换方向
{
dutyCycle = 10;
direction = 1;
}
}
// 设置比较寄存器值,改变占空比
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, dutyCycle);
HAL_Delay(7); // 控制呼吸速度
}
/* USER CODE END 3 */
代码说明:定时器的自动重装载值 ARR 设置为 999,因此 dutyCycle 取值范围为 0 到 999,对应占空比 0% 到 100%。这里设置 dutyCycle 范围为 10 到 990,保留一定的亮暗余量,避免 LED 完全熄灭或过亮。
3.2.4 硬件连接
| 组件 | 连接点 |
|---|---|
| STM32 PA6 引脚 | 限流电阻一端 |
| 限流电阻(220Ω~1kΩ)另一端 | LED 正极(阳极) |
| LED 负极(阴极) | GND |
最终效果如下:
| 组件 | 连接点 |
|---|---|
| STM32 PA6 引脚 | 限流电阻一端 |
| 限流电阻(220Ω~1kΩ)另一端 | LED 正极(阳极) |
| LED 负极(阴极) | GND |
最终效果如下:


