十八、PWM 呼吸灯
配置流程
我们外接一个 LED 灯进行 PWM 实现,注意一下要选择合适规格的 LED。
连接天空星开发板的 PB05 引脚!!
一般使用定时器 PWM 功能,都需要有以下几个步骤。
- 使能时钟
- 配置 GPIO
- 配置定时器
- 配置 PWM
- 使能 TIMER
- 调整定时器输出通道占空比
使能时钟
STM32F407VET6 单片机一共有 14 个定时器,除了基本定时器没有 PWM 功能,其它定时器都有 1 个、2 个或 4 个 PWM 通道。这一章节就用 PWM 实现一个呼吸灯的效果。首先 LED 灯连接在 PA5 引脚上,查找数据手册的第 43 页可知,PB5 有好几个定时器通道的复用功能,如图所示。
这里选择 PB5 的复用功能,也就是使用 TIMER3_CH2 进行 PWM 输出。
使能时钟:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIO外设
2
配置 GPIO
前面介绍过 PWM 输出是依赖于定时器的,所以要对定时器进行配置,但是我们不使用定时器的中断功能,顾不用对定时器的中断进行配置。
又因为我们使用的 PB5 是定时器 3 的通道 0,所以我们要配置 PB5 的 GPIO 参数。
首先是 GPIO 的参数结构体:
GPIO_InitTypeDef GPIO_InitStructure; // GPIO结构体
然后 GPIO 配置参数:
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形GPIOB5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
2
3
4
使能 GPIO 配置。
/* 使能定时器 */
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
2
使用复用功能:
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3); //复用
/* 这行代码的作用是将GPIOB端口的第5引脚(即PB5)配置为定时器TIM3的复用功能。
这意味着PB5引脚不再作为普通的I/O引脚使用,而是用来输出TIM3的PWM信号。*/
2
3
GPIO_PinAFConfig
函数是用于配置 GPIO 引脚的复用功能。STM32 的 GPIO 引脚具有多种功能,除了基本的输入输出功能外,还可以被配置为各种外设的复用输出,比如定时器、USART、I2C 等。这个功能允许 STM32 的一个外设通过 GPIO 引脚与外界通信或控制。
函数原型:
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
GPIOx
:指定 GPIO 端口。STM32 有多个 GPIO 端口(如 GPIOA、GPIOB 等),每个端口控制一组引脚。GPIO_PinSource
:指定 GPIO 引脚的源。这个参数指定了要配置的具体是哪一个引脚,如GPIO_PinSource0
、GPIO_PinSource1
等,对应端口上的 PIN0、PIN1 等。GPIO_AF
:指定引脚的复用功能。STM32 的每个引脚可以复用为多种功能,这个参数指定了具体的复用功能,如GPIO_AF_TIM3
表示将引脚复用为定时器 3 的功能。
配置定时器
要使用定时器参数配置有一个结构体,如图所示。
TIM_Prescaler
:预分频器值。用于除以 TIM 的时钟频率。这个参数可以是 0x0000 到 0xFFFF 之间的任何数值。预分频器的实际作用是减慢定时器的计数速度,从而允许定时器在更宽的时间范围内工作。TIM_CounterMode
:计数器模式。这个参数指定定时器的计数模式,可以是以下几种模式之一:TIM_CounterMode_Up
:向上计数模式。TIM_CounterMode_Down
:向下计数模式。TIM_CounterMode_CenterAligned
:中心对齐模式,计数器向上计数到一个值然后向下计数到 0。
这些模式决定了定时器是如何计数的。
TIM_Period
:周期值。这个参数指定了自动重装载寄存器的值,在下一个更新事件时被加载。这个参数必须是 0x0000 到 0xFFFF 之间的数值。周期值实际上定义了定时器溢出(或者达到预设的计数值)的时间点,从而触发相关的中断或事件。TIM_ClockDivision
:时钟分频。这个参数可以是以下几种值之一,用于进一步分频定时器的时钟:TIM_CKD_DIV1
:不分频。TIM_CKD_DIV2
:时钟分频 2。TIM_CKD_DIV4
:时钟分频 4。
这个分频是在预分频器之后进一步对时钟进行分频。
TIM_RepetitionCounter
:重复计数器值。每次 RCR(重复计数器)倒数到 0 时,都会生成一个更新事件,并且计数重新从 RCR 值(N)开始。在 PWM 模式下,这意味着(N+1)对应于:- 边沿对齐模式下的 PWM 周期数。
- 中心对齐模式下的半 PWM 周期数。
这个参数必须是 0x00 到 0xFF 之间的数值。注意,这个参数只对 TIM1 和 TIM8 有效。
结构体定义:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
相关配置如下:
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc - 1; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
2
3
4
5
初始化配置:
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
2
配置 PWM
配置好 TIMER3 参数之后,需要配置 PWM 输出结构体。
结构体定义如下:
TIM_OCInitTypeDef TIM_OCInitStructure;
结构体配置如下:
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性
2
3
4
初始化配置:
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
注意:每个通道有不同的函数。通道 2 是 TIM_OC2Init,通道 3 是 TIM_OC3Init,如此以此类推。。。。
使能预装载寄存器:
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
注意:每个通道有不同的函数。通道 2 是 TIM_OC2PreloadConfig,通道 3 是 TIM_OC3PreloadConfig,如此以此类推。。。。
使能 TIMER
我们使用函数 TIM_Cmd 初始化函数:
TIM_Cmd(TIM3, ENABLE); //使能TIM3
调整定时器输出通道占空比
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint32_t Compare2)
这个函数 TIM_SetCompare2
是用于设置 STM32 定时器的捕获比较 2 寄存器(Capture Compare 2 Register,简称 CC2R)的值。通过设置 CC2R,可以控制与定时器相关的输出比较(Output Compare)功能或输入捕获(Input Capture)功能。这个功能在生成精确的时间延迟、测量脉冲宽度或产生复杂的 PWM 输出等应用中非常有用。
函数的参数说明如下:
TIMx
:这个参数指定了要操作的定时器实例。STM32 系列微控制器通常包含多个定时器(如 TIM1、TIM2 等),这个参数就是用来指定具体哪一个定时器。参数中的x
可以是 1、2、3、4、5、8、9 或 12,这取决于具体的 STM32 型号和它所支持的定时器。Compare2
:这个参数指定了捕获比较 2 寄存器的新值。这个值用于与定时器的计数值进行比较,从而控制定时器的行为(如在特定计数值时触发中断或改变输出引脚的状态)。Compare2
的具体作用取决于定时器配置为输出比较模式还是输入捕获模式。
函数没有返回值(@retval None
)。
在 PWM(脉冲宽度调制)应用中,可以通过改变 CC2R 的值来调整 PWM 信号的占空比。例如,增加 CC2R 的值会增加 PWM 高电平的持续时间,从而增加占空比。
呼吸灯函数
要实现一个呼吸灯的效果,首先我们来看呼吸灯产生的原理。呼吸灯产生的原理就是 LED 灯逐渐变亮再逐渐变暗,然后一直循环下去。控制 LED 灯的亮暗是通过改变 PWM 的占空比,占空比越大,LED 灯越亮,占空比越小,LED 灯越暗。所以,我们只需要调节 PWM 的占空比就可以实现呼吸灯的效果。设置 PWM 的占空比的函数在之前介绍过,编写呼吸灯代码如下:
void pwm_breathing_lamp(void)
{
uint32_t brightness = 0;// 当前亮度
int step = 10; // 亮度改变的步长
// 逐渐增加亮度
for(brightness = 0; brightness < 1000; brightness += step)
{
TIM_SetCompare2(TIM3, brightness); // 使用TIM3的Channel 2
delay_ms(10);
}
// 逐渐减少亮度
for(brightness = 1000; brightness > 10; brightness -= step)
{
TIM_SetCompare2(TIM3, brightness); // 使用TIM3的Channel 2
delay_ms(10);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 初始化亮度和步长:亮度
brightness
从 0 开始,表示灯光最暗。步长step
设置为 10,表示每次亮度变化的量。 - 逐渐增加亮度:通过一个 for 循环,从 0 逐渐增加亮度到 1000(假设这是最大亮度值)。在每次循环中,使用
TIM_SetCompare2(TIM3, brightness)
来设置 PWM 的占空比,从而调整亮度。每次亮度调整后,通过delay_ms(10)
函数暂停 10 毫秒,以便观察到亮度的逐渐变化。 - 逐渐减少亮度:亮度达到最大值后,通过另一个 for 循环逐渐减少亮度回到最暗。逻辑与增加亮度时相同,只是这次是减少亮度值。
实验现象
这一章节的代码
在开发板介绍百度网盘链接中:立创·梁山派·天空星STM32F407VET6开发板资料/第03章软件资料/代码例程/009 PWM呼吸灯。
烧写我们的代码之后,会观察到 LED 灯由暗变亮,继而由亮变暗的效果。