正点原子【STM32

您所在的位置:网站首页 stm32输出pwm波形 正点原子【STM32

正点原子【STM32

2024-06-26 06:33:49| 来源: 网络整理| 查看: 265

1)资料下载:点击资料即可下载

2)对正点原子Linux感兴趣的同学可以加群讨论:935446741

3)关注正点原子公众号,获取最新资料更新

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

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

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

14.1 PWM 简介

14.2 硬件设计

14.3 软件设计

14.4 下载验证

14.1 PWM 简介

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

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

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

图 14.1.1 PWM 原理示意图

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

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

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

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

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

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

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

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

路的 PWM 输出!这里我们仅使用 TIM14 的 CH1 产生一路 PWM 输出。

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

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

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

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

首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有 2 个:TIMx _CCMR1

和 TIMx _CCMR2,不过 TIM14 只有一个。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2

控制 CH3 和 4。以下我们将以 TIM14 为例进行介绍。TIM14_CCMR1 寄存器各位描述如图 14.1.2

所示:

图 14.1.2 TIM14_CCMR1 寄存器各位描述

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

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

中文参考手册》第 476 页,16.6.4 节。这里我们需要说明的是模式设置位 OC1M,此部分由 3

位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。

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

出)默认设置为 0,就是设置通道作为输出使用。注意:这里是因为我们的 TIM14 只有 1 个通

道,所以才只有第八位有效,高八位无效,其他有多个通道的定时器,高八位也是有效的,具

体请参考《STM32F4xx 中文参考手册》对应定时器的寄存器描述。

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

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

图 14.1.3 TIM14_ CCER 寄存器各位描述

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

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

绍了,请参考《STM32F4xx 中文参考手册》第 478 页,16.6.5 这一节。同样,因为 TIM14 只有

1 个通道,所以才只有低四位有效,如果是其他定时器,该寄存器的其他位也可能有效。

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

通道 CH1~4。不过 TIM14 只有一个,即:TIM14_CCR1,该寄存器的各位描述如图 14.1.4 所示:

图 14.1.4 寄存器 TIM14_ CCR1 各位描述

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

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

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

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

图 14.1.5 寄存器 TIMx_ BDTR 各位描述

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

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

详细介绍了,请参考《STM32F4xx 中文参考手册》第 386 页,14.4.18 这一节。

本章,我们使用的是 TIM14 的通道 1,所以我们需要修改 TIM14_CCR1 以实现脉宽控制

DS0 的亮度。至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过 TIM14_CH1

输出 PWM 来控制 DS0 的亮度。下面我们介绍通过库函数来配置该功能的步骤。

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

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

1)开启 TIM14 和 GPIO 时钟,配置 PF9 选择复用功能 AF9(TIM14)输出。

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

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

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

__HAL_RCC_TIM14_CLK_ENABLE(); //使能定时器 14

__HAL_RCC_GPIOF_CLK_ENABLE(); //开启 GPIOB 时钟

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

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

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM14_CLK_ENABLE(); //使能定时器 14

__HAL_RCC_GPIOF_CLK_ENABLE(); //开启 GPIOF 时钟

GPIO_Initure.Pin=GPIO_PIN_9; //PF9

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

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

GPIO_Initure.Alternate= GPIO_AF9_TIM14; //PF9 复用为 TIM14_CH1

HAL_GPIO_Init(GPIOF,&GPIO_Initure);

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

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

册表中查看:

2)初始化 TIM14,设置 TIM14 的 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)设置 TIM14_CH1 的 PWM 模式,使能 TIM14 的 CH1 输出。

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

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

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

在 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。这里我们使用的是定时器 14 的通道 1,所以取值为 TIM_CHANNEL_1 即

可。

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

TIM_OC_InitTypeDef TIM14_CH1Handler; //定时器 14 通道 1 句柄

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)使能 TIM14。

在完成以上设置了之后,我们需要使能 TIM14。使能 TIM14 的方法前面已经讲解过:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

该函数第二个入口参数 Channel 是用来设置要使能输出的通道号。

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

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

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

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

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

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

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

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

//compare:比较值

void TIM_SetTIM14Compare1(u32 compare)

{

TIM14->CCR1=compare;

}

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

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

void TIM_SetCompare1(TIM_TypeDef *TIMx,u32 compare)

{

TIM14_CH1Handler.Pulse=compare;

HAL_TIM_PWM_ConfigChannel(&TIM14_Handler,&TIM14_CH1Handler,

TIM_CHANNEL_1);

}

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

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

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

14.2 硬件设计

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

1) 指示灯 DS0

2) 定时器 TIM14

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

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

14.3 软件设计

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

而是添加了几个函数

Timer.c 源文件代码如下:

//TIM14 PWM 部分初始化

//arr:自动重装值。

//psc:时钟预分频数

//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.

//Ft=定时器工作频率,单位:Mhz

void TIM14_PWM_Init(u16 arr,u16 psc)

{

TIM14_Handler.Instance=TIM14; //定时器 14

TIM14_Handler.Init.Prescaler=psc; //定时器分频

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

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

TIM14_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

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

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

TIM14_CH1Handler.Pulse=arr/2;

//设置比较值,此值用来确定占空比,默认比较值为自动重装载值

//的一半,即占空比为 50%

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

HAL_TIM_PWM_ConfigChannel(&TIM14_Handler,&TIM14_CH1Handler,

TIM_CHANNEL_1);//配置 TIM14 通道 1

HAL_TIM_PWM_Start(&TIM14_Handler,TIM_CHANNEL_1);//开启 PWM 通道 1

}

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

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

//htim:定时器句柄

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM14_CLK_ENABLE(); //使能定时器 14

__HAL_RCC_GPIOF_CLK_ENABLE(); //开启 GPIOF 时钟

GPIO_Initure.Pin=GPIO_PIN_9; //PF9

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

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

GPIO_Initure.Alternate= GPIO_AF9_TIM14; //PF9 复用为 TIM14_CH1

HAL_GPIO_Init(GPIOF,&GPIO_Initure);

}

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

//compare:比较值

void TIM_SetTIM14Compare1(u32 compare)

{

TIM14->CCR1=compare;

}

此部分代码包含了上面介绍的 PWM 输出设置的前 5 个步骤。这里我们关于 TIM14 的设置

就不再说了。

接下来,我们看看主程序里面的 main 函数如下:

int main(void)

{

u8 dir=1;

u16 led0pwmval=0;

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz

delay_init(168); //初始化延时函数

uart_init(115200); //初始化 USART

LED_Init(); //初始化 LED

TIM3_Init(5000-1,8400-1); //定时器 3 初始化,周期为 500ms

TIM14_PWM_Init(500-1,84-1); //84M/84=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_SetTIM14Compare1(led0pwmval);//修改比较值,修改占空比

}

}

这里,我们从死循环函数可以看出,我们将 led0pwmval 这个值设置为 PWM 比较值,也就

是通过 led0pwmval 来控制 PWM 的占空比,然后控制 led0pwmval 的值从 0 变到 300,然后又

从 300 变到 0,如此循环,因此 DS0 的亮度也会跟着信号的占空比变化从暗变到亮,然后又从

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

我们的 LED 亮度变化就不大了(虽然最大值可以设置到 499),因此设计过大的值在这里是没

必要的。至此,我们的软件设计就完成了。

14.4 下载验证

在完成软件设计之后,将我们将编译好的文件下载到探索者 STM32F4 开发板上,观看其

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

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

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

图 14.4.1 PWM 控制 DS0 亮度



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭