基于STM32CubeMX和keil采用通用定时器中断实现固定PWM&可调PWM波输出分别实现LED闪烁与呼吸灯

前言

上一篇博客里面写了基本定时器实现LED闪烁,主要就是一个预分频以及计数的使用,再加上一个电平翻转,原理以及具体操作比较简单。
这篇博客选择通用定时器进行稍微难一点的效果演示,用到输出PWM波的功能。
这篇博客写输出固定PWM以及可变PWM,进一步实现LED0闪烁以及LED0呼吸灯效果。
功能实现效果:
PF9输出PWM波控制LED0闪烁,1s周期,0.5s改变一次状态。
PF9输出PWM波控制LED0实现呼吸灯,一个时钟周期20ms。


1. PWM波阐述

PWM波,即脉宽调制波(Pulse Width Modulation),是一种在电信号中调整脉冲宽度的技术。它是通过控制信号的高电平时间和低电平时间的占空比来传输信息或实现对输出的精确控制。
在PWM波中,周期是固定的,周期是指一个完整高低电平的时间长度。而脉冲宽度则是高电平或低电平的持续时间,可以根据需要进行调整。脉冲宽度与信号的幅度呈正比,即脉冲宽度越宽,信号幅度越大,脉冲宽度越窄,信号幅度越小。

PWM波的优点包括:
1.简单可靠:PWM调制电路相对简单,成本低廉,可靠性高。
2.高效节能:由于PWM波的输出平均值可以通过调节占空比来控制,因此可以实现高效能的能量转换,减少功率损耗。
3.精确控制:通过调整脉冲宽度,可以精确控制输出的电压、电流或功率。
4.适应性强:PWM技术适用于不同类型的负载,可以调整频率和占空比来满足各种需求。

简单点概括一下就是,输出高低电平的一个方波,这个占空比可调。


2. 通用定时器

2.1 为什么用TIM14

首先是功能需求,这篇博客因为是LED0的闪烁以及呼吸灯效果,所以其实PWM波的输出都要给到PF9引脚上面去。
我们通过下面的原理图可以看到,PF9不仅接着LED0,同时也可以作为TIM14_CH1的输出。
在这里插入图片描述
在这里插入图片描述
如果直接在STM32CubeMX里面启用TIM14,可能并不是PF9,因为TIM14在STM32F407里面有两个引脚可以配置。如果配置了PA7,那么输出的PWM波其实并不能给到LED0上。这里为了方便起见,我们使用LED0的PF9的TIM14_CH1进行配置。
在这里插入图片描述

2.2 TIM14功能介绍

TIM14属于通用定时器的一种,它具有如下功能,其中这篇博客我们用到的是PWM生成功能。
1.计时功能:TIM14可以用作一个简单的计时器,用于测量时间间隔或持续时间。通过递增计数器的值来记录经过的时间,并根据预分频系数和时钟源来确定计时的精度。
2.定时中断:TIM14可以配置为在达到特定计数值时产生中断请求。通过设置计数器的自动重载值和使能中断,可以触发中断事件来执行特定的任务或操作。
3.PWM生成:TIM14可以用于生成简单的脉冲宽度调制(PWM)信号。通过设置计数器的自动重载值和比较寄存器的值,可以实现对脉冲的频率和占空比的控制。
4.单脉冲模式:TIM14还支持单脉冲模式,在特定的触发条件下,生成一个单脉冲信号。可以配置触发源、触发极性和脉冲宽度,用于各种应用场景。
5.捕获/比较模式:TIM14可以在输入捕获模式下,检测外部事件或信号的边沿,并记录计数器的当前值。同时,也可以在比较模式下,与比较寄存器中的值进行比较,触发相应的事件或输出行为。

2.3 一些配置参数解释

关于预分频系数等这些东西,详情参考基于STM32CubeMX和keil采用STM32F407的基本定时器中断实现LED闪烁,这里就不再赘述。
这里仅阐述相比于上一篇博客,PWM新增的部分功能阐述,即PWM Generation Channel 1配置

在这里插入图片描述

Mode:这里可以选择PWM mode 1和PWM mode 2模式,两者的区别在于:
前者在递增计数下,只要CNT<CCR,即计数器的值小于设定的比较寄存器的值,通道就是有效。反之有效。
前者在递减计数下,只要CNT<CCR,即计数器的值小于设定的比较寄存器的值,通道就是无效。反之有效。
后者与前者正好相反,例如递增计数情况下,CNT<CCR,通道无效。反之有效。

Pulse:PWM脉冲宽度,即设置CCR的值。这里的值应该小于整个计数周期。
Output compare preload:输出比较预装载。和auto-reload preload类似。这个值为enable时,我们修改CCR的值(这里也就是pluse的值),修改的值需要等到下一个UEV事件时才生效,否则立刻生效。
Fast Mode:是否使用输出比较快速模式,就是设置寄存器TIMx_CCMR1中的OC1FE位,用于加快触发输入事件对CC输出的影响,一般设置为Disable即可。
CH Polarity:通道极性。就是有效的状态是什么,如果设置为High,则通道有效的时候,输出为高电平。


2.4 PWM实现流程&中断

PWM实现的整体过程大致如下:
1.初始化定时器:首先,需要选择一个合适的定时器,并初始化它的基本配置。这包括选择定时器的模式、设置时钟源和预分频系数,以及配置计数器的位数。
2.设置PWM周期:根据需要设置PWM波的周期。这可以通过设置定时器的自动重载值(自动装载寄存器)来实现。自动重载值决定了计数器计数到多少后重新加载,并重新开始计数,从而确定PWM波的周期。
3.设置PWM占空比:根据期望的PWM波形的占空比,设置定时器的比较值(比较寄存器)。比较寄存器的值决定了计数器计数到多少时,PWM波的状态(高电平或低电平)发生变化。通过适当调整比较值,可以实现所需的占空比。
4.使能定时器和PWM输出:启用定时器并使能PWM输出。这通常包括使能定时器的计数器、开启PWM输出通道,以及配置相应的引脚和输出端口。
5.等待定时器中断或轮询:根据具体的实现方式,可以选择等待定时器中断请求,或者通过轮询定时器的状态来判断是否达到比较值,从而进行适当的处理或更新PWM波形的状态。

这个实验在做的时候我用STM32CubeMX配置了一堆,烧录了之后发现没作用,仔细查看代码,发现没给它使能。
下面写一下配置完成后如何使能启用PWM。

2.4.1 非中断PWM输出(LED闪烁)

PWM的输出是需要使能的,需要使用到函数HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel),这个函数的第一个参数是TIM的一个事件处理类的对象,这里也就是我们的TIM14的一个对象,后面的是通道。因为TIM14里面只有一个通道,所以这里我们应该为CH1。

在使用HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)这个函数之前,还需要使用HAL_TIM_Base_Start(TIM_HandleTypeDef *htim),这个函数是前者的前置条件,也就是先有Base_Start,再有PWM_Start。这也比较好理解,就是先有基本一些功能启用,再开启高级一点的功能。
LED闪烁其实就是固定占空比的情况,所以只需要输出固定占空比的PWM波,不需要中断。

2.4.2 中断PWM输出(LED呼吸灯)

中断PWM输出其实就是每一个PWM的周期结束,都会触发一次中断。那么和上面一样,也需要先开启基本的功能,再开启高级的功能。因为是中断方式,所以除了初始化中断以及配置中断向量优先级之类的,只需要调用对应的中断相关的启动函数。中断相关的函数后面一般都会有个IT(Interrupt)

HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);

使用中断的好处就是我可以每一次中断触发的时候在回调函数里修改CCR的值,从而改变占空比,从而实现呼吸灯效果。


3. STM32CubeMX配置

3.1 GPIO配置

在这里插入图片描述

3.2 时钟配置

开启外部晶振(其实也可以不开启,看你时钟的最终频率设置多少,一般开启外部晶振是为了设置时钟的时候能适配更多的时钟频率)
在这里插入图片描述下图的时钟树中我的时钟来自于内部高速晶振,其实和外部晶振开启与否并没有关系,因为我最终得到100MHz的频率使用内部高速晶振经过分频器和锁相环就可以得到。
下面时钟的HSI RC提供16MHz,经过分配器,变为2MHz,再倍频100,再除以2,片选PLLCLK,一倍分频,就得到了我们最终所设置的100MHz。
由于TIM14是在APB1总线上,所以我们还要关注一下APB1总线的时钟频率,这里自动设置为了50MHz。
也就是说TIM14获得的最初始的时钟是50MHz,后面的定时器的分频是在50MHz基础上进行的。
在这里插入图片描述

3.3 定时器相关参数配置

如果实现0.5s闪烁则配置如下
这里分频系数的设定是为了获得单次时钟周期为1ms时钟,ARR设置为999是为了获得周期为1s的PWM波周期。
Pulse为500是为了获得占空比为 500 ∗ 1 m s 1000 m s = 500 m s 1000 m s = 0.5 \frac{500*1ms}{1000ms}=\frac{500ms}{1000ms}=0.5 1000ms5001ms=1000ms500ms=0.5的PWM波。

在这里插入图片描述
如果实现呼吸灯效果则配置如下
这里分频系数的设定是为了获得单次时钟周期为0.1ms时钟,ARR设置为199是为了获得周期为20ms的PWM波周期。
Pulse为50是为了获得占空比为 50 ∗ 0.1 m s 20 = 5 m s 20 m s = 0.25 \frac{50*0.1ms}{20}=\frac{5ms}{20ms}=0.25 20500.1ms=20ms5ms=0.25的PWM波。
在这里插入图片描述

为什么要分开设置,是因为我自己这样设置效果比较明显,你也可以根据自己的需要进行修改。


3.4 Debug配置

在这里插入图片描述

3.5 中断配置

在这里插入图片描述

3.6 代码生成

在这里插入图片描述
在这里插入图片描述


4.代码编写

4.1 LED闪烁代码编写

如果不使用中断,代码比较简单,其实只需要在main函数里面使能即可。
在这里插入图片描述

	//不使用中断即输出固定占空比的PWM
	HAL_TIM_Base_Start(&htim14);
	HAL_TIM_PWM_Start(&htim14,TIM_CHANNEL_1);

LED闪烁的初始化里面参数:
在这里插入图片描述

4.2 LED呼吸灯代码编写

呼吸灯要用到中断,因为要调整PWM的占空比即CCR变量的值。
首先是中断使能

	//使用中断,在回调函数里改变占空比
	//HAL_TIM_Base_Start_IT(&htim14);
	//HAL_TIM_PWM_Start_IT(&htim14,TIM_CHANNEL_1);

回调函数是HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim),这个函数在计数器到达脉冲设置的值是触发。
为了调整脉宽等,我们还需要设定参数。

uint16_t plusWidth=500;//脉宽
uint16_t dirInc=1;//脉宽变化方向,1为递增,0递减

回调函数代码:

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
	//只针对TIM14进行中断处理
  if(htim->Instance!=TIM14)
	{
		return;
	}
	//如果是脉宽递增则增加脉宽
	if(dirInc==1)
	{
		plusWidth++;
		if(plusWidth>=195)
		{
			plusWidth=195;
			dirInc=0;
		}
	}
	//如果是脉宽递减则减少脉宽
	if(dirInc==0)
	{
		plusWidth--;
		if(plusWidth<=5)
		{
			plusWidth=5;
			dirInc=1;
		}
	}
	//__HAL_TIM_SetCompare(定时器对象指针,定时器通道,需要设置的CCR值);
	__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,plusWidth);
}	

呼吸灯的初始化参数配置
在这里插入图片描述


总结

这篇博客对定时器的PWM功能进行了归纳总结以及应用实现,写的非常详细,对应的MX工程以及Keil代码我放在了开头,需要自取。

猜你喜欢

转载自blog.csdn.net/Edwinwzy/article/details/131966919