【STM32】HAL库教程-PWM输出
使用工具和依赖
- 芯片:STM32F411CEU6(芯片手册)(STM32中文参考手册)
- 代码初始化软件:STM32CUBEMX
- IDE: MDK-ARM Keil5
- 依赖:stm32f4xx hal库
什么是PWM
脉冲宽度调制(PWM = Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出,通过控制有效电平在一个周期内的占比,来近似模拟信号进行控制的一种技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
PWM模拟连续信号:
PWM基本概念:
- PWM周期:PWM波形从低电平到高电平再到低电平所持续的时间
- PWM频率:单位时间内一个PWM周期的重复次数,\(f=1/T\)
- PWM占空比:一个PWM周期内高电平时间和总时间的比值,\(\sigma=T_{high}/T\)
定时器功能介绍
STM32F411xC/E系列共有8个定时器:
高级定时器(TIM1);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM9、TIM10、TIM11)。
通用定时器TIMx的功能包括: - 16位(TIM3和TIM4)或 32位(TIM2和TIM5) 向上、向下、向上/向下自动装载计数器 - 16位可编程 预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值 - 4个独立通道 - 输入捕获(Input capture) - 输出比较(Output compare) - PWM生成(边缘或中间对齐模式) - 单脉冲模式输出 - 使用外部信号控制定时器和定时器互连的同步电路 - 如下时间发生时产生中断/DMA: - 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) - 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) - 输入捕获 - 输出比较 - 支持针对定位的增量(正交)编码器和霍尔传感器电路 - 触发输入作为外部时钟或者按周期的电流管理
通用定时器框图如下:

输出比较/PWM工作原理
和输出比较/PWM相关的时基单元:
预分频器寄存器(TIMx_PSC)
预分频器PSC,有一个输入时钟CK_PSC和一个输出时钟CK_CNT。输入时钟CK_PSC就是系统时钟源的输出,输出CK_CNT则用来驱动计数器CNT计数。通过设置预分频器PSC的值(0~65535)可以得到不同的CK_CNT,计算公式:\(f_{CNT}=f_{PSC}/(PSC[15:0]+1)\)计数器寄存器(TIMx_CNT)
高级/通用定时器的计数器有三种计数模式,分别为向上计数模式、向下计数模式和中心对齐计数模式。- 向上计数模式:计数器从0开始计数,每来一个CK_CNT脉冲计数器就增加1,直到计数器的值与自动重载寄存器ARR值相等, 然后计数器又从0开始计数并生成计数器上溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成上溢事件就马上生成更新事件(UEV); 如果使能重复计数器,每生成一次上溢事件重复计数器内容就减1,直到重复计数器内容为0时才会生成更新事件。
- 向下计数模式:计数器从自动重载寄存器ARR值开始计数,每来一个CK_CNT脉冲计数器就减1,直到计数器值为0, 然后计数器又从自动重载寄存器ARR值开始递减计数并生成计数器下溢事件,计数器总是如此循环计数。如果禁用重复计数器, 在计数器生成下溢事件就马上生成更新事件;如果使能重复计数器,每生成一次下溢事件重复计数器内容就减1,直到重复计数器内容为0时才会生成更新事件。
- 中心对齐模式:计数器从0开始递增计数,直到计数值等于(ARR-1)值生成计数器上溢事件, 然后从ARR值开始递减计数直到1生成计数器下溢事件。然后又从0开始计数,如此循环。每次发生计数器上溢和下溢事件都会生成更新事件。
自动重载寄存器(TIMx_ARR)
自动重载寄存器ARR用来存放与计数器CNT比较的值,如果两个值相等CNT就复位并递减重复计数器。可以通过TIMx_CR1寄存器的ARPE位控制自动重载影子寄存器功能, 如果ARPE位置1,自动重载影子寄存器有效,只有在事件更新时才把TIMx_ARR值赋给影子寄存器。如果ARPE位为0,则修改TIMx_ARR值马上有效。捕获/比较寄存器(TIMx_CCR)
通过比较计数器CNT的值跟比较寄存器CCR的值,两者相等时输出参考信号OCxREF的信号的极性就会改变,其中OCxREF=1(高电平)称之为有效电平, OCxREF=0(低电平)称之为无效电平,并且会产生比较中断CCxI,相应的标志位CCxIF(SR寄存器中)会置位。 然后OCxREF再经过一系列的控制之后就成为真正的输出信号OCx/OCxN.
输出比较的工作流程(如上):
TIMx_CCMR1:OC1M[2:0]用于输出比较模式选择
000: 冻结,001: 匹配时有效,010: 匹配时无效,011: 翻转电平,100: 强制无效,101: 强制有效,110: PWM模式1,111: PWM模式2
PWM模式1:TIMx_CNT < TIMx_CCR1 时通道1为有效电平,否则为无效电平
PWM模式2:TIMx_CNT < TIMx_CCR1 时通道1为无效电平,否则为有效电平oc1ref:根据CNT和CCR1的比较结果输出有效电平/无效电平
oc1ref = 0 无效电平,oc1ref = 1 有效电平TIMx_CC1P:CC1P表示输出极性
CC1P = 0 高电平为有效电平,CC1P = 1 低电平为有效电平TIMx_CCER:CC1E表示输出使能
CC1E = 0 禁止输出,CC1E = 1 信号输出到对应的输出引脚
根据PWM模式1/2和向上/向下计数的不同选择,可排列组合出4种PWM输出波形:
| PWM模式1 | PWM模式2 | |
|---|---|---|
| 向上计数 | ![]() |
![]() |
| 向下计数 | ![]() |
![]() |
呼吸灯实验讲解
我们结合刚才学习的PWM和定时器基础知识,完成呼吸灯实验,实现板上LED亮度的渐变调节功能。
CUBEMX初始化配置
设置外部时钟
设置高速外部时钟HSE,选择RC振荡器

设置烧录和调试接口
设置Debug选项为Serial Wire

设置时钟树
点击锁相环PLLCLK,将系统时钟HCLK设置为最大值100MHz

设置定时器
点击TIM2,设置定时器时钟源为内部时钟源,设置定时器CH1为PWM Generation模式

- PWM频率 \(f_{pwm}=f_{clk}/(PSC+1)/(ARR+1)=100M/100/500=2kHz\),注意:PSC和ARR要在实际值基础减1
- PWM占空比 \(duty=(CCR+1)/(ARR+1)\),CCR可以在Pulse中设为固定值,也可以在程序中再修改
- Mode 选择PWM模式1
- Pulse(占空比值) 先按默认值0
- Fast Mode PWM脉冲快速模式,和我们配置无关,不使能
- PWM 极性 设置为低电平 PS: 由于LED是低电平点亮,所以我们把极性设置为low
项目文件配置
设置项目名称,设置存储路径,选择所用IDE(KEIL 5)

选择仅复制所需的.c和.h库文件,选择每个功能生成独立的.c和.h文件
然后点击GENERATE
CODE,创建工程
Keil代码编写
配置下载工具
新建的工程所有配置都是默认的,我们需要选择ST-Link下载调试接口,勾选上下载后复位运行

我们修改main.c文件创建一个呼吸灯的例程。
定义变量:
1
2
3/* USER CODE BEGIN 1 */
uint16_t pwmVal = 0;
/* USER CODE END 1 */
使能 TIM2 的 PWM Channel1 输出:
1 | /* USER CODE BEGIN 2 */ |
在while循环中添加核心逻辑:
1 | /* USER CODE BEGIN WHILE */ |



