【嵌入式】STM32 硬件 PWM 学习文档

STM32 硬件 PWM 学习文档(以 STM32F103VB + PA1/TIM2_CH2/LED17 为例)

1. 学习目标

本文档用于梳理 STM32 硬件 PWM 的基础概念、参数计算方法、配置思路,以及本次实验中

用例:PA1 -> TIM2_CH2 -> LED17 的实际应用过程。

通过本文档,应能回答下面几个问题:

  • 什么是 PWM
  • STM32 的硬件 PWM 是怎么产生的
  • 为什么 PSCARRCCR 要这样配置
  • PWM 频率占空比 分别由什么决定
  • 为什么某个 LED 能不能做硬件 PWM,要看引脚有没有 TIMx_CHx
  • 为什么 PA1 可以做 LED17 的硬件 PWM

2. 本次实验背景

本次实验使用芯片为:

  • STM32F103VB

板上 LED17 对应到 MCU 的:

  • PA1

PA1 具备复用功能:

  • TIM2_CH2

因此可以将 PA1 配置为定时器 2 的通道 2 输出脚,使用 TIM2_CH2 输出硬件 PWM ,进而控制 LED17 的亮度变化。


3. 什么是 PWM

PWM,全称:

Pulse Width Modulation(脉冲宽度调制)

它的核心思想是:

在一个固定周期内,通过控制高电平持续时间所占的比例,来控制输出的平均效果。

例如,一个周期为 10ms

  • 高电平持续 1ms,低电平持续 9ms,占空比为 10%
  • 高电平持续 5ms,低电平持续 5ms,占空比为 50%
  • 高电平持续 9ms,低电平持续 1ms,占空比为 90%

对于 LED 来说,人眼看到的不是每一个瞬间的高低电平,而是一个"平均亮度":

  • 占空比越大,通常越亮
  • 占空比越小,通常越暗

4. 硬件 PWM 和软件 PWM 的区别

4.1 软件 PWM

软件 PWM 的典型做法是:

  • 定时器周期性进入中断
  • 在中断里手动控制 GPIO 输出高低电平
  • 通过程序计算亮灭时间比例,模拟 PWM

特点:

  • 实现直观
  • 所有 GPIO 都可以尝试模拟
  • 但 CPU 参与较多
  • 中断负担更重

4.2 硬件 PWM

硬件 PWM 的典型做法是:

  • 使用定时器内部的 PWM 模式
  • 定时器自动产生 PWM 波形
  • 通过 TIMx_CHx 对应引脚直接输出

特点:

  • 波形由硬件自动产生
  • CPU 不需要反复翻转 GPIO
  • 更稳定、更高效
  • 但只能用具备 TIMx_CHx 功能的引脚

4.3 本次实验属于

PA1 -> TIM2_CH2 -> LED17 的实验,属于:

硬件 PWM

∵ PWM 波形由 TIM2 的通道 2 直接输出到 PA1,不是通过中断手动翻转 GPIO 实现的。


5. STM32 硬件 PWM 的本质

STM32 硬件 PWM 的本质可以理解为:

  1. 定时器以一定速度计数
  2. 计数器从 0 开始不断累加
  3. 当计数值小于某个比较值时,输出一种电平
  4. 当计数值大于等于该比较值时,输出另一种电平
  5. 当计数到周期上限后,重新回到 0,开始下一个周期

因此,硬件 PWM 的核心不是普通 GPIO,而是:

  • 定时器计数
  • 比较寄存器
  • 输出通道

6. PWM 须掌握的几个核心概念

6.1 系统时钟

系统时钟是 MCU 运行的基础时钟来源。

本次实验中,使用简单的时钟配置:

  • HSI = 8MHz

也就是说,很多外设的基础时钟,都与这 8MHz 有关。


6.2 定时器时钟

定时器本身并不是凭空计数的,它的计数速度来自输入时钟。

在入门阶段,可以先简单理解成:

定时器拿到一个时钟后,经过预分频,再开始计数。


6.3 Prescaler(预分频器,PSC)

PSC 的作用是:

把输入给定时器的时钟先"减慢"

公式为:

text 复制代码
定时器计数频率 = 定时器输入时钟 / (PSC + 1)

注意这里一定是:

text 复制代码
PSC + 1

不是直接除以 PSC

例如:

  • 输入时钟 = 8MHz
  • PSC = 7

那么:

text 复制代码
定时器计数频率 = 8MHz / (7 + 1) = 1MHz

说明:

定时器现在每 1us 计一次数

每秒1M次

f= 1/T


6.4 Period(自动重装值,ARR)

ARR 的作用是:

决定计数器数到多少后重新从 0 开始

例如:

  • ARR = 999

那么计数过程为:

text 复制代码
0, 1, 2, ... , 999

总共是 1000 个计数。

因此,一个完整 PWM 周期的计数长度为:

text 复制代码
ARR + 1

6.5 Compare(比较值,CCR)

CCR 的作用是:

决定一个 PWM 周期中,高电平持续多长时间

在常见的 PWM1 模式下,可以简单理解为:

  • CNT < CCR 时,输出高电平
  • CNT >= CCR 时,输出低电平

因此:

  • CCR 越大,高电平持续越久
  • 占空比越大

6.6 计数器(CNT)

CNT 是定时器当前正在计到的值。

比如:

  • 当前 CNT = 100
  • 当前 CCR = 500

若采用 PWM1 模式,则因为 100 < 500,输出保持高电平。

CNT 继续增加,到 500 及以上后,输出会切换为低电平。


7. PWM 频率和占空比公式

7.1 PWM 频率公式

PWM 频率由下面公式决定:

text 复制代码
PWM频率 = 定时器输入时钟 / ((PSC + 1) × (ARR + 1))

这说明:

  • PSC 影响计数速度
  • ARR 影响一个周期有多少个计数
  • 两者共同决定 PWM 的频率

7.2 占空比公式

占空比由下面公式决定:

text 复制代码
占空比 = CCR / (ARR + 1)

这说明:

  • ARR 决定总周期长度
  • CCR 决定高电平持续时间
  • 两者共同决定占空比

8. 举例为什么本次实验这样配置参数

本次实验采用的参数是:

  • 系统时钟:8MHz
  • PSC = 7
  • ARR = 999

目标是:

  • 产生一个大约 1kHz 的 PWM
  • 同时保留较高的占空比分辨率,便于 LED 调光更平滑

9. 参数计算过程详解

9.1 第一步:先确定系统时钟

本次实验使用最简单的内部高速时钟:

text 复制代码
HSI = 8MHz

9.2 第二步:先决定计数频率

为了便于理解和计算,先将定时器计数频率设置为:

text 复制代码
1MHz

为什么选 1MHz

因为:

  • 1MHz 表示每 1us 计一次数
  • 时间单位直观
  • 后续算周期和占空比都方便

根据公式:

text 复制代码
定时器计数频率 = 定时器输入时钟 / (PSC + 1)

代入:

text 复制代码
1MHz = 8MHz / (PSC + 1)

得到:

text 复制代码
PSC + 1 = 8
PSC = 7

所以设置:

text 复制代码
PSC = 7

9.3 第三步:确定 PWM 周期

目标 PWM 频率设为:

text 复制代码
1kHz

也就是:

text 复制代码
每秒 1000 个周期
每个周期 1ms

前面已经把计数频率设成了:

text 复制代码
1MHz

即:

text 复制代码
每 1us 计一次数

那么一个 1ms 周期内,需要计数:

text 复制代码
1ms = 1000us

也就是:

text 复制代码
1000 次

由于定时器从 0 开始计数,所以:

text 复制代码
ARR = 1000 - 1 = 999

因此设置:

text 复制代码
ARR = 999

9.4 第四步:验证 PWM 频率

将参数代入频率公式:

text 复制代码
PWM频率 = 8MHz / ((7 + 1) × (999 + 1))
         = 8MHz / (8 × 1000)
         = 1kHz

所以该配置是正确的。


10. 为什么不选别的参数组合

实际上,并不是只有这一组参数能得到 1kHz PWM

例如,也可以这样:

  • PSC = 79
  • ARR = 99

因为:

text 复制代码
8MHz / (79 + 1) = 100kHz
100kHz / (99 + 1) = 1kHz

最终频率也是 1kHz

但是这两种配置的差别在于:

配置一:PSC = 7, ARR = 999

优点:

  • 计数频率 1MHz,时间分辨率高
  • ARR = 999,占空比可调范围大约有 1000 档
  • LED 调光更细腻

配置二:PSC = 79, ARR = 99

特点:

  • 计数频率较低
  • ARR = 99,占空比只有大约 100 档
  • 调节会更粗一些

因此,在 LED 呼吸灯这类应用中,通常更偏向:

在 PWM 频率满足要求的前提下,让 ARR 稍大一些,从而获得更高的占空比分辨率


11. 为什么修改 CCR 就能改变 LED 亮度

假设当前:

  • ARR = 999

那么一个周期总共是 1000 个计数。

情况 1:CCR = 100

text 复制代码
占空比 = 100 / 1000 = 10%

这意味着:

  • 一个周期中只有约 10% 的时间输出高电平
  • LED 平均亮度较低

情况 2:CCR = 500

text 复制代码
占空比 = 500 / 1000 = 50%

这意味着:

  • 一个周期中约一半时间输出高电平
  • LED 亮度中等

情况 3:CCR = 900

text 复制代码
占空比 = 900 / 1000 = 90%

这意味着:

  • 大部分时间输出高电平
  • LED 平均亮度较高

因此,修改 CCR 就是在修改占空比,而占空比决定 LED 的平均亮度。


12. 为什么 PA1 必须配置为复用推挽输出

做硬件 PWM 时,PA1 不能再当普通 GPIO 使用,而要把该引脚交给定时器通道来控制。

因此必须配置为:

c 复制代码
GPIO_MODE_AF_PP

含义为:

  • AF:Alternate Function,复用功能
  • PP:Push Pull,推挽输出

如果配置成普通推挽输出:

c 复制代码
GPIO_MODE_OUTPUT_PP

PA1 只是一个普通 GPIO,无法输出 TIM2_CH2 的 PWM 波形。

所以这一步是硬件 PWM 成功输出的关键条件之一。


13. 为什么要使用 TIM2_CH2

因为本次 LED17 接到的是:

  • PA1

PA1 具备的定时器通道功能是:

  • TIM2_CH2

所以如果想让 LED17 走硬件 PWM,必须满足下面这个链路:

text 复制代码
TIM2 -> CH2 -> PA1 -> LED17

也就是说,硬件 PWM 不是任意 GPIO 都能做 ,而是要看该引脚有没有对应的 TIMx_CHx 功能。


14. 如何判断某个 LED 能不能做硬件 PWM

判断步骤通常如下:

第一步:看原理图

确认该 LED 接到了 MCU 的哪个引脚。

第二步:看该引脚复用功能

确认该引脚是否具备:

text 复制代码
TIMx_CHx

例如:

  • PA1 -> TIM2_CH2
  • PA0 -> TIM2_CH1

第三步:确认工程里能否将该引脚配置为复用输出

如果具备 TIMx_CHx,则可以尝试配置为硬件 PWM。

第四步:若没有 TIMx_CHx

则通常不能直接做硬件 PWM,只能考虑:

  • 软件 PWM
  • 或更换到支持 PWM 的引脚

15. 为什么本次 LED17 能做 PWM

本次实验中:

  • LED17 -> PA1
  • PA1 具备 TIM2_CH2

因此满足硬件 PWM 的基本条件。

虽然板级原理图中 LED17 还经过了反相器等外围电路,但这不影响:

  • PA1 输出 PWM 波形
  • 定时器使用 TIM2_CH2

只是可能会影响最终看到的亮暗逻辑方向。

也就是说:

  • 占空比变大,可能看起来越来越亮
  • 也可能因为板级反相,表现为越来越暗

但这不影响硬件 PWM 已经正确输出。


16. 为什么要选 1kHz 作为 LED PWM 频率

LED 做 PWM 调光时,频率不能太低。

如果频率过低,容易出现:

  • 肉眼可见闪烁
  • 视觉体验差

1kHz 是一个很常见、也很适合入门实验的频率,因为它:

  • 足够高,不容易明显闪烁
  • 参数容易计算
  • 调试方便

当然,并不是只能选 1kHz,实际应用中还可能使用:

  • 500Hz
  • 2kHz
  • 5kHz

但对学习阶段来说,1kHz 是一个很好的起点。


17. 一次完整的 PWM 参数设计思路

以后遇到 PWM 配置问题,可以按下面这个流程思考。

第一步:确定系统时钟

例如:

text 复制代码
8MHz

第二步:确定目标 PWM 频率

例如:

text 复制代码
1kHz

第三步:先选一个好理解的定时器计数频率

例如:

text 复制代码
1MHz

这时:

text 复制代码
PSC = 8MHz / 1MHz - 1 = 7

第四步:再根据 PWM 频率算 ARR

因为:

text 复制代码
1MHz / 1kHz = 1000

所以:

text 复制代码
ARR = 1000 - 1 = 999

第五步:根据占空比算 CCR

例如,若想要 50% 占空比:

text 复制代码
CCR = 1000 × 50% = 500

这样整组参数就完整了。


18. 与本次实验相关的关键结论

18.1 参数结论

本实验设置:

  • PSC = 7
  • ARR = 999

可得到:

  • 定时器计数频率:1MHz
  • PWM 频率:1kHz

18.2 占空比调节方式

通过修改:

  • CCR

即可改变占空比,从而改变 LED17 的平均亮度。

18.3 引脚配置要求

PA1 必须配置为:

  • 复用推挽输出

不能配置为普通 GPIO 输出。

18.4 引脚前提

某 LED 想做硬件 PWM,必须先确认其对应引脚具有:

  • TIMx_CHx

PA1 具备:

  • TIM2_CH2

所以可以实现硬件 PWM。


19. 容易混淆的几个点

19.1 为什么公式里总是 +1

无论是:

  • PSC
  • ARR

都要注意,硬件计数通常是从 0 开始的。

所以:

  • PSC = 7 实际表示 8 分频
  • ARR = 999 实际表示一个周期共 1000 个计数

这是初学者最容易写错的地方之一。


19.2 PWM 频率和占空比不要混淆

PWM 频率由什么决定

由:

  • 输入时钟
  • PSC
  • ARR

决定

占空比由什么决定

由:

  • CCR
  • ARR

决定

因此:

  • PSCARR,主要是在改 PWM 有多快
  • CCR,主要是在改 LED 有多亮

19.3 并不是所有 GPIO 都能直接输出硬件 PWM

只有当引脚带有:

text 复制代码
TIMx_CHx

这类复用功能时,才适合直接做硬件 PWM。

如果没有该功能,则通常只能:

  • 做普通 GPIO
  • 做软件 PWM

20. 本次实验的理解模型

可以把 PWM 的参数理解成三层:

第一层:PSC

决定定时器"走得有多快"

第二层:ARR

决定"走多少步算一个周期"

第三层:CCR

决定"这个周期里有多少步保持高电平"

这个模型非常适合初学者建立整体认知。


21. 小结

本次基于 STM32F103VBT6PA1/TIM2_CH2/LED17 的硬件 PWM 学习,核心结论如下:

  1. PWM 本质上是在固定周期内控制高电平持续时间比例的技术
  2. STM32 的硬件 PWM 由定时器自动产生,不需要 CPU 反复翻转 GPIO
  3. PWM频率 = 定时器时钟 / ((PSC+1) × (ARR+1))
  4. 占空比 = CCR / (ARR+1)
  5. PSC 用于分频,ARR 用于确定周期,CCR 用于确定占空比
  6. 本实验中 8MHz -> PSC=7 -> 1MHz计数频率 -> ARR=999 -> 1kHz PWM
  7. PA1 因为具备 TIM2_CH2 功能,所以可以用于硬件 PWM 输出
  8. PA1 必须配置为复用推挽输出,不能配置为普通 GPIO 输出
  9. 通过动态修改 CCR,即可实现 LED17 的亮度调节和呼吸灯效果

22. 适合后续继续学习的方向

在掌握本文内容后,可以继续学习下面几个方向:

  • PWM1PWM2 模式的区别
  • 边沿对齐模式与中心对齐模式
  • 高级定时器与普通定时器的区别
  • 多通道 PWM 同步输出
  • 通过按键切换占空比档位
  • 通过 DMA 自动修改 CCR 实现更平滑呼吸灯
  • PWM 驱动蜂鸣器、电机、舵机的不同参数配置思路
相关推荐
头疼的程序员3 小时前
计算机网络:自顶向下方法(第七版)第五章 学习分享(二)
学习·计算机网络
A_nanda3 小时前
一款前端PDF插件
前端·学习·pdf·vue
若风的雨3 小时前
【deepseek】PCIe上电时序的详细
嵌入式硬件
安逸sgr3 小时前
MCP 协议深度解析(八):Prompts 提示模板与 Sampling 采样机制!
人工智能·分布式·学习·语言模型·协议·mcp
科技林总3 小时前
【系统分析师】12.2 软件架构风格
学习
’长谷深风‘3 小时前
51单片机入门
c语言·单片机·嵌入式硬件·51单片机
●VON3 小时前
旗舰基座大模型 MiMo-V2-Pro 初体验与实战指南
学习·小米·模型·von·mimo-v2-pro
QYQ_11273 小时前
嵌入式学习——ARM部分概念
arm开发·学习
沐欣工作室_lvyiyi3 小时前
基于物联网的体温心率监测系统(论文+源码)
stm32·单片机·嵌入式硬件·物联网·体温心率
苦瓜小生3 小时前
【黑马点评学习笔记 | 实战篇 】| 5-分布式锁+初步秒杀优化
笔记·分布式·学习