细说STM32单片机使用通用定时器生成固定占空比和可变占空比PWM波的方法
目录
一、本实例测试的目的
二、硬件和CubeMX项目配置
1、硬件开发板
2、项目配置
(1)定时器TIM2_CH1
(2)时钟和Debug
(3) NVIC
(4)GPIO
3、输出固定占空比的PWM波源码
(1)启动定时器
(2)控制LED
(3)下载与测试
4、输出可变占空比的PWM波
(1)改变占空比的方法
5、输出可变占空比的PWM波源码
(1)新增变量
(2)启动定时器
(3)控制LED
(4)下载与测试
一、本实例测试的目的
使用TIM2的CH1输出生成PWM波, 首先输出固定占空比的PWM波,然后再改动程序后输出可变占空比的PWM波。
二、硬件和CubeMX项目配置
1、硬件开发板
开发板型号NUCLEO-G474RE,MCU型号STM32G474RET6,该MCU有1个高精度定时器、3个高级控制定时器、7个通用定时器(TIM2~TIM5、TIM15~TIM17)和2个基础定时器。此外还有2个看门狗定时器和1个SysTick定时器。
开发板上TIM2的CH1连接的引脚PA5。PA5引脚不能设置为GPIO_Output,而要设置为TIM2_CH1。在开发板上,PA5管脚本身就是指示灯LD2,高电平亮。本实验不仅要控制LD2开与关还要控制它的明亮程度。
开发板上PB11设置为LED1,PB11输出高电平时LED1亮,否则,LED1灭。可以用TIM2的CH1输出PWM波控制LED1的开与关(以PWM的频率)。PB11引脚设置为GPIO_Output。
2、项目配置
(1)定时器TIM2_CH1
Channel1选择PWM Generation CH1;Clock Source选择Internal Clock;
- Prescaler,预分频寄存器值,设置为16999,所以预分频系数为17000。定时器使用内部时钟信号频率为170MHz,经过预分频后进入计数器的时钟频率就10kHz,即
- Counter Period,计数周期(ARR的值),设置为199,所以一个计数周期是
- Internal Clock Division,内部时钟分频,是在定时器控制器部分对内部时钟进行分频,可以设置为1、2或4分频,选项No Division就是无分频,使得CK_PSC等于CK INT。
- auto-reload preload,自动重载预装载,即设置TIM2_CR1寄存器中的ARPE位。如果设置为Disable,就是不使用预装载,设置的新ARR的值立即生效;如果设置为Enabled, 设置的新ARR的值在下一个UEV事件时才生效。
- Mode,PWM模式,选项有PWM Mode 1(PWM模式1)和PWM Mode 2(PWM模式2)。这两种模式的定义如下:
PWM模式1:在递增计数模式下,只要CNT<CCR,通道就是有效状态,否则为无效状态。在递减计数模式下,只要CNT<CCR,通道就变为无效状态,否则为有效状态。
PWM模式2:其输出与PWM模式1正好相反,例如,在递增计数模式下,只要CNT<CCR,通道就是无效状态,否则为有效状态。
- Pulse,PWM脉冲宽度,就是设置32位的捕获/比较寄存器CCR的值。脉冲宽度的值应该小于计数周期的值,这里设置为50,因为计数器的时钟频率是10kHz,所以脉冲宽度为5ms。
- Output compare preload,输出比较预装载。CCR有预装载功能,寄存器TIMx_CCMRy中的OCyPE(Output Capture y Preload Enable)位可以使能或禁用其预装载功能。这个参数就是设置这个位的值,设置为Enable时,修改CCR的值需要到下一个UEV事件时才生效,否则立刻生效。
- Fast Mode,是否使用输出比较快速模式,就是设置寄存器TIMx_CCMR1中的OC1FE位,用于加快触发输入事件对CC输出的影响,一般设置为Disable即可。
- CH Polarity,通道极性,就是CCR与CNT比较输出的有效状态,可以设置为高电平 (High)或低电平(Low)。通道极性和PWM模式的组合可以生成不同的PWM波形。
(2)时钟和Debug
HSE选择外部晶振,24MHZ,APB1 = APB2 = 170MHz;
Debug选择Serial Wire;
(3) NVIC
Time Base修改为0;
TIM2 global interrupt设置为1;
(4)GPIO
设置PB11为GPIO OUTPUT,默认高电平,PP,高速,别名LED1。
经过这样的设置,在启动定时器TIM2后,在引脚PA5(TIM2_CH1通道)上输出的PWM 波形:通道极性为高,PWM模式为1。PWM波的周期为20ms,由ARR的值决定;高电平脉冲宽度为5ms,由CCR的值决定。
同时扩展板上的PB11的LED1闪烁。
3、输出固定占空比的PWM波源码
(1)启动定时器
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2); //以中断方式启动TIM2
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1); //TIM2通道1,启动生成PWM
/* USER CODE END 2 */
MX_TIM2_Init()是定时器TIM2的初始化函数。要启动TIM2的PWM波输出,需要先执行函数HAL_TIM_Base_Start_IT()启动定时器,再执行函数HAL_TIM_PWM_Start IT()启动CH1的PWM波输出。
(2)控制LED
控制LED1(PB11)的开与关。
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
HAL_GPIO_TogglePin (LED1_GPIO_Port,LED1_Pin);
}
/* USER CODE END 4 */
控制LD2(PA5),自动生成。
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspPostInit 0 */
/* USER CODE END TIM2_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM2 GPIO Configuration
PA5 ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM2_MspPostInit 1 */
/* USER CODE END TIM2_MspPostInit 1 */
}
}
(3)下载与测试
下载到开发板连续运行,用示波器可以观察PA5引脚上输出的固定占空比的PWM波形。用眼睛可以看到闪烁的LED1(PB11)和恒亮的LD2(PA5)。改变PWM高电平脉冲宽度可以改变LD2的亮度,因为PA5引脚为高电平时LED亮,所以PWM脉冲宽度越大,LD2越亮。在函数MX_TIM2_Init()的代码里,直接修改PWM参数结构体变量sConfigOC的成员变量的赋值,可以观察不同参数取值的影响,例如,修改PWM模式参数OCMode、有效极性参数OCPolarity、脉冲宽度参数Pulse等。
//sConfigOC成员
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 50;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
4、输出可变占空比的PWM波
(1)改变占空比的方法
LD2的亮度是由PWM波的占空比控制的,例1的程序输出PWM波是固定占空比,所以 LD2的亮度是固定的。如果在程序运行过程中动态修改PWM的占空比,就可以使LD2从暗到明、从明到暗地变化,形成一种呼吸的效果。LED1的亮度不受影响。
在程序中动态改变PWM波的占空比,就是要修改寄存器TIMx_CCR1的值。下图是动态修改PWM波的占空比的示意图。在本示例中,PWM波的周期是200个时钟周期,在发生比较匹配事件时,会产生TIM_IT_CC1中断事件(置位CCyF中断标志位),可以在此中断里修改CCR的值。
CCR是有预装载功能的。如果禁止CCR预装载功能,对CCR的修改立即生效;如果启用了CCR预装载功能,设置新的CCR值需要在下一个UEV事件时才生效。参数Output compare preload控制是否启用CCR的预装载功能,本例里需要设置为 Enable。
如果CCR的值等于定时器计数器的值,就会置位中断事件标志位CCyIF,即产生TIM_IT_CCy中断事件。生成PWM波是输出比较,HAL_TIM_PWM_PulseFinishedCallback() 是对应的回调函数,可以在这个回调函数里修改CCR的值。当CCR预装载功能使能时,在下次UEV事件时对CCR的修改就会生效,从而可以动态地改变PWM波的占空比。
使用宏函数__HAL_TIM_SET_COMPARE()可以设置CCR的值,其原型定义为:
__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)
其中,__HANDLE__是定时器对象指针,__CHANNEL__是定时器通道,__COMPARE__是需要为CCR设置的值。例如,将TIM2的CH1通道的CCR设置为0x0037的语句为:
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,0x0037);
另外,宏函数__HAL_TIM_GET_COMPARE()可以获取CCR当前的值,其函数原型定义如下,这个函数返回结果是16位或32位整数,与具体定时器的CCR的长度有关。
__HAL_TIM_GET_COMPARE(__HANDLE__ ,__CHANNEL__)
5、输出可变占空比的PWM波源码
(1)新增变量
/* USER CODE BEGIN PV */
uint16_t pulseWidth = 50; //脉宽
uint8_t dirInc = 1; //脉宽变化方向,1=递增,0=递减
/* USER CODE END PV */
(2)启动定时器
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2); //以中断方式启动TIM2
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1); //TIM2通道1,启动生成PWM
/* USER CODE END 2 */
(3)控制LED
控制LED1开与关的频率不变,亮度不受影响。LD2的亮度受PWM占空比的影响,呈现呼吸状态。
/* USER CODE BEGIN 4 */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_TogglePin (LED1_GPIO_Port,LED1_Pin);
if (htim->Instance != TIM2)
return;
if(dirInc == 1) //脉宽递增
{
pulseWidth++;
if (pulseWidth >= 195)
{
pulseWidth = 195;
dirInc=0; //脉宽递减
}
}
else
{
pulseWidth--;
if (pulseWidth <= 5)
{
pulseWidth = 5;
dirInc=1;//脉宽递增
}
}
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,pulseWidth);//设置CCR的值
}
/* USER CODE END 4 */
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspPostInit 0 */
/* USER CODE END TIM2_MspPostInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM2 GPIO Configuration
PA5 ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM2_MspPostInit 1 */
/* USER CODE END TIM2_MspPostInit 1 */
}
}
(4)下载与测试
该方法实现了按脉宽递增或递减方向修改CCR的值,因为开启了CCR预装载功能,所以新设置的CCR的值在下一个UEV事件时才生效。下载并运行此程序,我们就可以观察到LD2 由明到暗,再由暗到明的循环往复变化效果。