完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:alientek 阿波罗 STM32F767 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子 第十三章 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 所示: 图 13.1.1 PWM 原理示意图 图 13.1.1 就是一个简单的 PWM 原理示意图。图中,我们假定定时器工作在向上计数 PWM 模式,且当 CNT 示意图:当 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 所示: 图 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 所示: 图 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 所示: 图 13.1.4 寄存器 TIM3_CCR4 各位描述 在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点, 我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。 如果是通用定时器,则配置以上三个寄存器就够了,但是如果是高级定时器,则还需要配 置:刹车和死区寄存器(TIMx_BDTR),该寄存器各位描述如图 13.1.5 所示: 图 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 可以从数据手 册表中查看: 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 所示: 图 13.4.1 PWM 控制 DS0 亮度 13.5 STM32CubeMX 配置定时器 PWM 输出功能 使用 STM32CubeMX 配置 PWM 输出的配置步骤和配置定时器中断的配置步骤非常接近, 步骤如下: ① 在 Pinout->TIM3 配置项中,配置 Channel4 的值为 PWM generation CH4,然后 Clock Source 为 Internal Clock。操作过程如下图 13.5.1 所示: 图 13.5.1 TIM3 配置 ② 进入 Configuration->TIM3 配置页,在弹出的界面中点击 Parameter Settings 选项卡, Counter Settings 配置栏下面的四个选项就是用来配置定时器的预分频系数,自动装载 值,计数模式以及时钟分频因子。在界面的 PWM Generation Channel4 配置栏配置 PWM 模式,比较值,极性等参数,操作方法如下图 13.5.2 所示: 图 12.5.2 TIM3 参数设置界面 本章 PWM 输出实验,我们并没有使用到中断,所以我们不需要使能中断和配置 NVIC。 经过上面的配置就可以生成工程源码,大家可以和本实验工程对比参考学习。 |
|
相关推荐
|
|
1307 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
1234 浏览 3 评论
2313 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
1404 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
1834 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-30 12:04 , Processed in 0.924684 second(s), Total 66, Slave 48 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号