【STM32】HAL库教程-PWM输出

使用工具和依赖

什么是PWM

脉冲宽度调制(PWM = Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出,通过控制有效电平在一个周期内的占比,来近似模拟信号进行控制的一种技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。

PWM模拟连续信号:

PWM模拟连续信号

PWM基本概念:

  • PWM周期:PWM波形从低电平到高电平再到低电平所持续的时间
  • PWM频率:单位时间内一个PWM周期的重复次数,\(f=1/T\)
  • PWM占空比:一个PWM周期内高电平时间和总时间的比值,\(\sigma=T_{high}/T\)
PWM基本概念

定时器功能介绍

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开始计数,如此循环。每次发生计数器上溢和下溢事件都会生成更新事件。
定时器计数器寄存器的3种工作模式
  • 自动重载寄存器(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. 捕获比较通道的输出部分

输出比较的工作流程(如上):

  1. 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为无效电平,否则为有效电平

  2. oc1ref:根据CNT和CCR1的比较结果输出有效电平/无效电平
    oc1ref = 0 无效电平,oc1ref = 1 有效电平

  3. TIMx_CC1P:CC1P表示输出极性
    CC1P = 0 高电平为有效电平,CC1P = 1 低电平为有效电平

  4. TIMx_CCER:CC1E表示输出使能
    CC1E = 0 禁止输出,CC1E = 1 信号输出到对应的输出引脚

根据PWM模式1/2和向上/向下计数的不同选择,可排列组合出4种PWM输出波形:

PWM模式1 PWM模式2
向上计数 PWM模式1_向上计数 PWM模式2_向上计数
向下计数 PWM模式1_向下计数 PWM模式2_向下计数

呼吸灯实验讲解

我们结合刚才学习的PWM和定时器基础知识,完成呼吸灯实验,实现板上LED亮度的渐变调节功能。

CUBEMX初始化配置

  1. 设置外部时钟
    设置高速外部时钟HSE,选择RC振荡器
    设置RCC

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

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

  4. 设置定时器
    点击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
  5. 项目文件配置
    设置项目名称,设置存储路径,选择所用IDE(KEIL 5)
    项目文件设置1
    选择仅复制所需的.c和.h库文件,选择每个功能生成独立的.c和.h文件
    项目文件设置2 然后点击GENERATE CODE,创建工程

Keil代码编写

配置下载工具
新建的工程所有配置都是默认的,我们需要选择ST-Link下载调试接口,勾选上下载后复位运行 keil配置下载工具

我们修改main.c文件创建一个呼吸灯的例程。
定义变量:

1
2
3
/* USER CODE BEGIN 1 */
uint16_t pwmVal = 0;
/* USER CODE END 1 */

使能 TIM2 的 PWM Channel1 输出:

1
2
3
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
/* USER CODE END 2 */

在while循环中添加核心逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* USER CODE BEGIN WHILE */
while (1)
{
while(pwmVal < 500) {
pwmVal++;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, pwmVal);
HAL_Delay(1);
}
while(pwmVal) {
pwmVal--;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, pwmVal);
HAL_Delay(1);
}
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

参考链接

  1. 野火STM32库开发实战指南

  2. 【STM32】HAL库 STM32CubeMX教程