stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载十三章 PWM 输出实验

1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子

4f805f4f19bc895123379d270d243b73.png

第十三章 PWM 输出实验

上一章,我们介绍了STM32F7的通用定时器TIM3,用该定时器的中断来控制DS1的闪烁,

这一章,我们将向大家介绍如何使用 STM32F7 的 TIM3 来产生 PWM 输出。在本章中,我们将

使用 TIM3 的通道 4 来产生 PWM 来控制 DS0 的亮度。本章分为如下几个部分:

13.1 PWM 简介

13.2 硬件设计

13.3 软件设计

13.4 下载验证

13.5 STM32CubeMX 配置定时器 PWM 输出功能

13.1 PWM 简介

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用

微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽

度的控制,PWM 原理如图 13.1.1 所示:

c04a4a0dab42ee860080a22816da8210.png

图 13.1.1 PWM 原理示意图

图 13.1.1 就是一个简单的 PWM 原理示意图。图中,我们假定定时器工作在向上计数 PWM

模式,且当 CNT<CCRx 时,输出 0,当 CNT>=CCRx 时输出 1。那么就可以得到如上的 PWM

示意图:当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,

IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。

改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的

频率,这就是 PWM 输出的原理。

STM32F767 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高

级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多

达 4 路的 PWM 输出!这里我们仅使用 TIM3 的 CH4 产生一路 PWM 输出。

要使 STM32F767 的通用定时器 TIMx 产生 PWM 输出,除了上一章介绍的寄存器外,我们

还会用到 3 个寄存器,来控制 PWM 的。这三个寄存器分别是:捕获/比较模式寄存器

(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。

接下来我们简单介绍一下这三个寄存器。

首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有 2 个:TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。以下我们

将以 TIM3 为例进行介绍。TIM3_CCMR2 寄存器各位描述如图 13.1.2 所示:

79cd130ed2254cf862eb570f767e431b.png

图 13.1.2 TIM3_CCMR2 寄存器各位描述

该寄存器的有些位在不同模式下,功能不一样,所以在图 13.1.2 中,我们把寄存器分了 2

层,上面一层对应输出而下面的则对应输入。关于该寄存器的详细说明,请参考《STM32F7 中

文参考手册》第 701 页,23.4.7 节。这里我们需要说明的是模式设置位 OC4M,此部分由 4 位

组成。总共可以配置成 13 种模式,我们使用的是 PWM 模式,所以这 4 位必须设置为 0110/0111。

这两种 PWM 模式的区别就是输出电平的极性相反。另外 CC4S 用于设置通道的方向(输入/输

出)默认设置为 0,就是设置通道作为输出使用。

接下来,我们介绍 TIM3 的捕获/比较使能寄存器(TIM3_CCER),该寄存器控制着各个输

入输出通道的开关。该寄存器的各位描述如图 13.1.3 所示:

8f3d428010872e4b7156f3d829214b67.png

图 13.1.3 TIM3_ CCER 寄存器各位描述

该寄存器比较简单,我们这里只用到了 CC4E 位,该位是输入/捕获 4 输出使能位,要想

PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。该寄存器更详细的介

绍了,请参考《STM32F7 中文参考手册》第 706 页,23.4.9 这一节。

最后,我们介绍一下捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个

通道 CH1~4。我们使用的是通道 4,TIM3_CCR4 寄存器的各位描述如图 13.1.4 所示:

d0e87ce771768ed722831496557f1a00.png

图 13.1.4 寄存器 TIM3_CCR4 各位描述

在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,

我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。

如果是通用定时器,则配置以上三个寄存器就够了,但是如果是高级定时器,则还需要配

置:刹车和死区寄存器(TIMx_BDTR),该寄存器各位描述如图 13.1.5 所示:

71f8efedc2a6d8bf0f5f85912be79821.png

图 13.1.5 寄存器 TIMx_ BDTR 各位描述

该寄存器,我们只需要关注第 15 位:MOE 位,要想高级定时器的 PWM 正常输出,则必

须设置 MOE 位为 1,否则不会有输出。注意:通用定时器不需要配置这个。其他位我们这里就

不详细介绍了,请参考《STM32F7 中文参考手册》第 639 页,22.4.18 这一节。

本章,我们使用的是 TIM3 的通道 4,所以我们需要修改 TIM3_CCR4 以实现脉宽控制 DS0

的亮度。至此,我们把本章要用的几个相关寄存器都介绍完了,下面我们介绍通过 HAL 库来

配置该功能的步骤。

首先要提到的是,PWM 实际跟上一章节一样使用的是定时器的功能,所以相关的函数设

置同样在库函数文件 stm32f7xx_tim.h 和 stm32f7xx_tim.c 文件中。

1)开启 TIM3 和 GPIO 时钟,配置 PB1 选择复用功能 AF1(TIM3)输出。

要使用 TIM3,我们必须先开启 TIM14 的时钟,这点相信大家看了这么多代码,应该明白

了。这里我们还要配置 PB1 为复用(AF1)输出,才可以实现 TIM13_CH4 的 PWM 经过 PB1

输出。 HAL 库使能 TIM3 时钟和 GPIO 时钟方法是:

__HAL_RCC_TIM3_CLK_ENABLE();

//使能定时器 3

__HAL_RCC_GPIOB_CLK_ENABLE();

//开启 GPIOB 时钟

接下来便是要配置 PB1 复用映射为 TIM3 的 PWM 输出引脚。关于 IO 口复用映射,在串口

通信实验中有详细讲解,主要是通过函数 HAL_GPIO_Init 来实现的:

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_1; //PB1

GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

GPIO_Initure.Alternate= GPIO_AF2_TIM3; //PB1 复用为 TIM3_CH4

HAL_GPIO_Init(GPIOB,&GPIO_Initure);

在 IO 口初始化配置中,我们只需要将成员变量 Mode 配置为复用推挽输出,同时成员变量

Alternate 配置为 GPIO_AF2_TIM3,即可实现 PB1 映射为定时器 3 通道 4 的 PWM 输出引脚。

这里还需要说明一下,对于定时器通道的引脚关系,大家可以查看 STM32F7 对应的数据

手册,比如我们 PWM 实验,我们使用的是定时器 3 的通道 4,对应的引脚 PB1 可以从数据手

册表中查看:

0cf1958285494378dcc176793ce254f4.png

2)初始化 TIM3,设置 TIM3 的 ARR 和 PSC 等参数。

根据前面的讲解,初始化定时器的 ARR 和 PSC 等参数是通过函数 HAL_TIM_Base_Init 来

实现的,但是这里大家要注意,对于我们使用定时器的 PWM 输出功能时,HAL 库为我们提供

了一个独立的定时器初始化函数 HAL_TIM_PWM_Init,该函数声明为:

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);

该函数实现的功能以及使用方法和 HAL_TIM_Base_Init 都是类似的,作用都是初始化定时

器 的 ARR 和 PSC 等参 数 。 为什么 HAL 库要提供这个函数而不直接让我们使用

HAL_TIM_Base_Init 函数呢?

这 是 因 为 HAL 库 为 定 时 器 的 PWM 输 出 定 义 了 单 独 的 MSP 回 调 函 数

HAL_TIM_PWM_MspInit,也就是说,当我们调用HAL_TIM_PWM_Init进行PWM初始化之后,

该函数内部会调用 MSP 回调函数 HAL_TIM_PWM_MspInit。而当我们使用 HAL_TIM_Base_Init

初始化定时器参数的时候,它内部调用的回调函数为 HAL_TIM_Base_MspInit,这里大家注意

区分。

所以大家一定要注意,使用 HAL_TIM_PWM_Init 初始化定时器时,回调函数为:

HAL_TIM_PWM_MspInit,该函数声明为:

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim);

一般情况下,上面步骤 1 的时钟使能和 IO 口初始化映射都编写在回调函数内部。

3)设置 TIM3_CH4 的 PWM 模式,输出比较极性,比较值等参数。

接下来,我们要设置 TIM3_CH4 为 PWM 模式(默认是冻结的),因为我们的 DS0 是低电

平亮,而我们希望当 CCR4 的值小的时候,DS0 就暗,CCR4 值大的时候,DS0 就亮,所以我

们要通过配置 TIM3_CCMR2 的相关位来控制 TIM3_CH4 的模式。

在 HAL 库中,PWM 通道设置是通过函数 HAL_TIM_PWM_ConfigChannel 来设置的:

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,

TIM_OC_InitTypeDef* sConfig, uint32_t Channel);

第一个参数 htim 是定时器初始化句柄,也就是 TIM_HandleTypeDef 结构体指针类型,这

和 HAL_TIM_PWM_Init 函数调用时候参数保存一致即可。

第二个参数 sConfig 是 TIM_OC_InitTypeDef 结构体指针类型,这也是该函数最重要的参数。

该参数用来设置 PWM 输出模式,极性,比较值等重要参数。首先我们来看看结构体定义:

typedef struct

{

uint32_t OCMode;

//PWM 模式

uint32_t Pulse;

//捕获比较值

uint32_t OCPolarity;

//极性

uint32_t OCNPolarity;

uint32_t OCFastMode;

//快速模式

uint32_t OCIdleState;

uint32_t OCNIdleState;

} TIM_OC_InitTypeDef;

该结构体成员我们重点关注前三个。成员变量 OCMode 用来设置模式,也就是我们前面讲解的

7 种模式,这里我们设置为 PWM 模式 1。成员变量 Pulse 用来设置捕获比较值。成员变量

TIM_OCPolarity 用 来 设 置 输 出 极 性 是 高 还 是 低 。 其 他 的 参 数 TIM_OutputNState ,

TIM_OCNPolarity,TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器才用到的。

第 三 个 参 数 Channel 用 来 选 择 定 时 器 的 通 道 , 取 值 范 围 为 TIM_CHANNEL_1~

TIM_CHANNEL_4。这里我们使用的是定时器3的通道4,所以取值为TIM_CHANNEL_4即可。

例如我们要初始化定时器 3 的通道 4 为 PWM 模式 1,输出极性为低,那么实例代码为:

TIM_OC_InitTypeDef TIM3_CH4Handler; //定时器 3 通道 4 句柄

TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择 PWM1

TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比


TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低

HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);

4)使能 TIM3,使能 TIM3 的 CH4 输出。

在完成以上设置了之后,我们需要使能 TIM3 并且使能 TIM3_CH4 输出。在 HAL 库中,

函数 HAL_TIM_PWM_Start 可以用来实现这两个功能,函数声明如下:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

该函数第二个入口参数 Channel 是用来设置要使能输出的通道号,这里我们使能的是定时器

的通道 4,值设置为 TIM_CHANNEL_4 即可。

对于单独使能定时器的方法,在上一章定时器实验我们已经讲解。实际上,HAL 库也同样

提供了单独使能定时器的输出通道函数,函数为:

void TIM_CCxChannelCmd(TIM_TypeDef* TIMx, uint32_t Channel, uint32_t ChannelState);

5)修改 TIM3_CCR4 来控制占空比。

最后,在经过以上设置之后,PWM 其实已经开始输出了,只是其占空比和频率都是固定

的,而我们通过修改比较值TIM3_CCR4 则可以控制CH4的输出占空比。继而控制DS0 的亮度。

HAL 库中并没有提供独立的修改占空比函数,这里我们可以编写这样一个函数如下:

//设置 TIM3 通道 4 的占空比

// compare:比较值

void TIM_SetTIM3Compare4(u32 compare)

{

TIM3->CCR4=compare;

}

实际上,因为调用函数 HAL_TIM_PWM_ConfigChanne 进行 PWM 配置的时候可以设置比

较值,所以我们也可以直接使用该函数来达到修改占空比的目的:

void TIM_SetCompare4(TIM_TypeDef *TIMx,u32 compare)

{

TIM3_CH4Handler.Pulse=compare;

HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);

}

这种方法因为要调用 HAL_TIM_PWM_ConfigChannel 函数对各种初始化参数进行重新设

置,所以大家在使用中一定要注意,例如在实时系统中如果多个线程同时修改初始化结构体相

关参数,可能导致结果混乱。

13.2 硬件设计

本实验用到的硬件资源有:

1) 指示灯 DS0

2) 定时器 TIM3

这两个我们前面都已经介绍了,因为 TIM3_CH4 可以通过 PB1 输出 PWM,而 DS0 就是直

接节在 PB1 上面的,所以电路上并没有任何变化。

13.3 软件设计

打开 PWM 输出实验工程可以看到,我们相比上一节,并没有添加其他任何 HAL 库文件,

因为 PWM 是使用的定时器资源,所以跟上一讲使用的是同样的 HAL 库文件。同时我们修改了

timer.c 和 timer.h 的内容,删掉了上一章实验源码,直接把 PWM 功能相关函数和定义放在了这

两个文件中。

timer.c 源文件代码如下:

TIM_HandleTypeDef TIM3_Handler; //定时器 3PWM 句柄

TIM_OC_InitTypeDef TIM3_CH4Handler; //定时器 3 通道 4 句柄

//PWM 输出初始化

//arr:自动重装值 psc:时钟预分频数

void TIM3_PWM_Init(u16 arr,u16 psc)

{

TIM3_Handler.Instance=TIM3;

//定时器 3

TIM3_Handler.Init.Prescaler=psc;

//定时器分频


TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式

TIM3_Handler.Init.Period=arr; //自动重装载值

TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

HAL_TIM_PWM_Init(&TIM3_Handler); //初始化 PWM

TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择 PWM1

TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比


TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低

HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,

TIM_CHANNEL_4); //配置 TIM3 通道 4

HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启 PWM 通道 4

}

//定时器底层驱动,时钟使能,引脚配置

//此函数会被 HAL_TIM_PWM_Init()调用

//htim:定时器句柄

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM3_CLK_ENABLE();

//使能定时器 3

__HAL_RCC_GPIOB_CLK_ENABLE();

//开启 GPIOB 时钟

GPIO_Initure.Pin=GPIO_PIN_1; //PB1

GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH;

//高速

GPIO_Initure.Alternate= GPIO_AF2_TIM3;

//PB1 复用为 TIM3_CH4

HAL_GPIO_Init(GPIOB,&GPIO_Initure);

}

//设置 TIM 通道 4 的占空比

//compare:比较值

void TIM_SetTIM3Compare4(u32 compare)

{

TIM3->CCR4=compare;

}

此部分代码包含三个函数,完全实现了前面 13.1 小节讲解的 5 个配置步骤。第一个函数

TIM3_PWM_Init 实现的是 13.1 小节讲解的步骤 2-4,首先通过调用定时器 HAL 库函数

HAL_TIM_PWM_Init 初始化 TIM3 并设置 TIM3 的 ARR 和 PSC 等参数,其次通过调用函数

HAL_TIM_PWM_ConfigChannel 设置 TIM3_CH4 的 PWM 模式以及比较值等参数,最后通过调

用函数 HAL_TIM_PWM_Start 来使能 TIM3 以及使能 PWM 通道 TIM3_CH4 输出。第二个函数

HAL_TIM_PWM_MspInit 是 PWM 的 MSP 初始化回调函数,该函数实现的是 13.1 小节步骤 1,

主要是使能相应时钟以及初始化定时器通道 TIM3_CH4 对应的 IO 口模式,同时设置复用映射

关系。第三个函数 TIM_SetTIM3Compare 4 是用户自定义的设置比较值函数,这在我们 13.1 小

节步骤 5 有详细讲解。

接下来,我们看看 main 函数内容如下:

int main(void)

{

u8 dir=1;

u16 led0pwmval=0;

Cache_Enable();

//打开 L1-Cache

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz

delay_init(216); //延时初始化

uart_init(115200);

//串口初始化

LED_Init(); //初始化 LED

TIM3_PWM_Init(500-1,108-1); //108M/108=1M 的计数频率,自动重装载为 500,

//那么 PWM 频率为 1M/500=2kHZ

while(1)

{

delay_ms(10);

if(dir)led0pwmval++;

//dir==1 led0pwmval 递增

else led0pwmval--;

//dir==0 led0pwmval 递减

if(led0pwmval>300)dir=0;

//led0pwmval 到达 300 后,方向为递减

if(led0pwmval==0)dir=1;

//led0pwmval 递减到 0 后,方向改为递增

TIM_SetTIM3Compare4(led0pwmval);//修改比较值,修改占空比

}

}

这里,我们从死循环函数可以看出,我们控制 LED0_PWM_VAL 的值从 0 变到 300,然后

又从 300 变到 0,如此循环,因此 DS0 的亮度也会跟着从暗变到亮,然后又从亮变到暗。至于

这里的值,我们为什么取 300,是因为 PWM 的输出占空比达到这个值的时候,我们的 LED 亮

度变化就不大了(虽然最大值可以设置到 499),因此设计过大的值在这里是没必要的。至此,

我们的软件设计就完成了。

13.4 下载验证

在完成软件设计之后,将我们将编译好的文件下载到阿波罗 STM32 开发板上,观看其运

行结果是否与我们编写的一致。如果没有错误,我们将看 DS0 不停的由暗变到亮,然后又从亮

变到暗。每个过程持续时间大概为 3 秒钟左右。

实际运行结果如下图 13.4.1 所示:

78bb647021c9dcf1789c650ecdb16cd9.png

图 13.4.1 PWM 控制 DS0 亮度

13.5 STM32CubeMX 配置定时器 PWM 输出功能

使用 STM32CubeMX 配置 PWM 输出的配置步骤和配置定时器中断的配置步骤非常接近,

步骤如下:

① 在 Pinout->TIM3 配置项中,配置 Channel4 的值为 PWM generation CH4,然后 Clock

Source 为 Internal Clock。操作过程如下图 13.5.1 所示:

d9a4cb912ac162a5ec25253219ca51fd.png

图 13.5.1 TIM3 配置

② 进入 Configuration->TIM3 配置页,在弹出的界面中点击 Parameter Settings 选项卡,

Counter Settings 配置栏下面的四个选项就是用来配置定时器的预分频系数,自动装载

值,计数模式以及时钟分频因子。在界面的 PWM Generation Channel4 配置栏配置 PWM

模式,比较值,极性等参数,操作方法如下图 13.5.2 所示:

929639d3cafaa520b237e3ffbf38926c.png

图 12.5.2 TIM3 参数设置界面

本章 PWM 输出实验,我们并没有使用到中断,所以我们不需要使能中断和配置 NVIC。

经过上面的配置就可以生成工程源码,大家可以和本实验工程对比参考学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值