STM32 PWM介绍及实验:呼吸灯效果实现
目录
一、PWM介绍
什么是PWM
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
pwm的频率:
是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期);
也就是说一秒钟PWM有多少个周期
单位: Hz
表示方式: 50Hz 100Hz
pwm的周期:
T=1/f
周期=1/频率
50Hz = 20ms 一个周期
如果频率为50Hz ,也就是说一个周期是20ms 那么一秒钟就有 50次PWM周期
STM32F103C8T6 PWM资源
高级定时器(TIM1):7路
通用定时器(TIM2~TIM4):各4路
PWM输出模式:
PWM模式1:在向上计数时,一旦 CNT < CCRx 时输出为有效电平,否则为无效电平; 在向
下计数时,一旦 CNT > CCRx 时输出为无效电平,否则为有效电平。
PWM模式2:在向上计数时,一旦 CNT < CCRx 时输出为无效电平,否则为有效电平; 在向
下计数时,一旦 CNT > CCRx 时输出为有效电平,否则为无效电平。
PWM信号的周期计算公式
Tout:设定的时间周期
PSC:预分频系数
ARR:自动重装载值
Tclk:频率
PWM信号的频率计算公式:f=1/T
如何计算频率?
若PSC=71,ARR=499,则频率 f = 2000Hz
计算步骤:72*1000000/72/500=2000
PWM占空比:是一个脉冲周期内,高电平的时间与整个周期时间的比例 ,由TIMx_CCRx寄存器决定。
二、PWM实验——实现呼吸灯效果
需求:使用PWM点亮LED1实现呼吸灯效果。PSC=71,ARR=499。
LED灯为什么可以越来越亮,越来越暗?
这是由不同的占空比决定的。因为在低电平时LED灯会亮,所以低电平的时间越长,LED灯亮的时间也就越长,低电平的时间越短,LED灯亮的时间也就越短
1、初始化GPIO口引脚
#include "gpio.h"
void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOD_CLK_ENABLE(); //使能GPIOD时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
}
2、初始化定时器函数
#include "tim.h"
TIM_HandleTypeDef htim4; //调用定时器外设结构体
void MX_TIM4_Init(void) //初始化定时器函数的过程
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
//定时器外设结构体参数初始化
htim4.Instance = TIM4; //定时器4
htim4.Init.Prescaler = 71; //设置定时器预分频系数
htim4.Init.CounterMode = TIM_COUNTERMODE_UP; //定时器计数方式,向上计数方式
htim4.Init.Period = 499; //定时周期
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //设置时钟分频系数,这里代表不分频
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; //自动重装载,使能
if (HAL_TIM_Base_Init(&htim4) != HAL_OK) //初始化TIM4
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; //时钟源:内部
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) //配置TIM4的时钟源
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; //每次更新时间TRGO1输出一个上升沿
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; //使能TIM4主模式
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) //配置TIM4的主模式
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1; //PWM模式,CNT<CCR为有效电平
sConfigOC.Pulse = 0; //占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; //低电平有效时间
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; //快速比较使能
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) //PWM模式初始化
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim4); //硬件层初始户化
}
3、通过修改比较值、修改占空比值,来编写呼吸灯的逻辑
uint16_t pwmVal = 0; //uint16_t 代表无符号16位整数,调整占空比的参数
uint8_t dir = 0; //uint_8t代指unsigned char(无符号字符型),控制灯的亮度,0越来越亮,1越来越暗
while (1)
{
HAL_Delay(1); //延时1ms
if(dir == 0){ //当控制灯接近等于0时
pwmVal++; //pwmVal的值会一直的增加,灯会越来越亮
}else{ //反之,当控制灯不接近于0时
pwmVal--; //pwmVal的值会一直的减小,灯会越来越暗
}
if(pwmVal > 500){ //当pwmVal的值增加到一定的值时,不能大于ARR的值
dir = 1; //要控制灯的亮度,让灯越来越暗
}else if(pwmVal == 0){ //当pwmVal的值减小到0时
dir = 0; //要控制灯的亮度,让灯越来越亮
}
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal); //修改比较值,修改占空比
}
4、把上述呼吸灯的逻辑加入到main主函数中
#include "main.h"
#include "tim.h"
#include "gpio.h"
void SystemClock_Config(void); //因为这个函数在main函数中,如果想要使用这个函数,需要提前声明
int main(void)
{
uint16_t pwmVal = 0; //uint16_t 代表无符号16位整数,调整占空比的参数
uint8_t dir = 0; //uint_8t代指unsigned char(无符号字符型),控制灯的亮度,0越来越亮,1越来越暗
HAL_Init(); //初始化HAL库函数
SystemClock_Config(); //初始化系统时钟函数
MX_GPIO_Init(); //初始化GPIO引脚
MX_TIM4_Init(); //初始化定时器4函数
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3); //使能 Timer4 第3通道 PWM 输出
while (1)
{
HAL_Delay(1); //延时1ms
if(dir == 0){ //当控制灯接近等于0时
pwmVal++; //pwmVal的值会一直的增加,灯会越来越亮
}else{ //反之,当控制灯不接近于0时
pwmVal--; //pwmVal的值会一直的减小,灯会越来越暗
}
if(pwmVal > 500){ //当pwmVal的值增加到一定的值时,不能大于ARR的值
dir = 1; //要控制灯的亮度,让灯越来越暗
}else if(pwmVal == 0){ //当pwmVal的值减小到0时
dir = 0; //要控制灯的亮度,让灯越来越亮
}
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal); //修改比较值,修改占空比
}
}
void SystemClock_Config(void) //配置系统时钟函数
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
5、效果演示
STM32呼吸灯演示