十八、PWM 呼吸灯
1.1.配置流程
一般使用定时器 PWM 功能,都需要有以下几个步骤。
- 配置通道引脚 GPIO
- 配置定时器
- 配置输出结构体
- 配置定时器输出通道
- 配置定时器输出通道占空比
- 定时器自动重载影子使能
1.1.1.配置通道引脚 GPIO
GD32F450ZGT6 单片机一共有 14 个定时器,除了基本定时器没有 PWM 功能,其它定时器都有 1 个、2 个或 4 个 PWM 通道。这一章节就用 PWM 实现一个呼吸灯的效果。首先 LED 灯连接在 PA5 引脚上,查找数据手册的第 46 页可知,PA5 有好几个定时器通道的复用功能,如图 1-1-1 所示。
这里选择 PA5 的复用功能 AF1 进行操作,也就是使用 TIMER1_CH0 进行 PWM 输出。
要操作 GPIO 引脚,必不可少的就是对 GPIO 进行配置,开启时钟,配置模式,配置输出,设置复用功能等,还是这一系列的操作。
PA5 引脚宏定义如下:
/* PA5 TIMER1_CH0 */
#define BSP_PWM_RCU RCU_GPIOA
#define BSP_PWM_PORT GPIOA
#define BSP_PWM_PIN GPIO_PIN_5
2
3
4
初始化 GPIO 引脚的配置如下:
static void pwm_gpio_config(void)
{
/* 开启时钟 */
rcu_periph_clock_enable(BSP_PWM_RCU);
gpio_mode_set(BSP_PWM_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, BSP_PWM_PIN);
gpio_output_options_set(BSP_PWM_PORT,GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,BSP_PWM_PIN);
/* 配置IO为定时器的通道 */
gpio_af_set(BSP_PWM_PORT, GPIO_AF_1, BSP_PWM_PIN);
}
2
3
4
5
6
7
8
9
上面的代码都是一些常规操作,需要注意的点就是设置复用功能的时候配置为 GPIO_AF_1,因为我们使用的是 PA5 引脚的 AF1 的功能。
1.1.2.配置定时器
前面介绍过 PWM 输出是依赖于定时器的,所以要对定时器进行配置,但是我们不使用定时器的中断功能,顾不用对定时器的中断进行配置。
又因为我们使用的 PA5 的 AF1 是定时器 1 的通道 0,所以我们要配置定时器 1 的参数。关于定时器 1 的宏定义如下:
/* TIMER */
#define BSP_PWM_TIMER_RCU RCU_TIMER1 // 定时器时钟
#define BSP_PWM_TIMER TIMER1 // 定时器
2
3
接下来就要配置定时器的参数
首先是定义定时器的参数结构体:
timer_parameter_struct timer_initpara; // 定义定时器结构体
然后使能定时器时钟和配置参数:
/* 开启时钟 */
rcu_periph_clock_enable(BSP_PWM_TIMER_RCU); // 开启定时器时钟
/* CK_TIMERx = 4 x CK_APB1 = 4x50M = 200MHZ */
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // 配置定时器时钟
timer_deinit(BSP_PWM_TIMER); // 复位定时器
/* 配置定时器参数 */
timer_initpara.prescaler = 200 -1; // 时钟预分频值 PSC_CLK= 200MHZ / 200 = 1MHZ
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timer_initpara.period = 10000 -1; // 周期 T = 10000 * 1MHZ = 10ms f = 100HZ
/* 在输入捕获的时候使用 数字滤波器使用的采样频率之间的分频比例 */
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; // 分频因子
/* 只有高级定时器才有 配置为x,就重复x+1次进入中断 */
timer_initpara.repetitioncounter = 0; // 重复计数器 0-255
timer_init(BSP_PWM_TIMER,&timer_initpara); // 初始化定时器
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这里和之前的定时器实验相比就只有两处发生变化,一个就是时钟分频值修改为 199,那 PSC_CLK = 200MHZ / 200 = 1MHZ,周期值设置为 10000 -1,那对应的 PWM 输出的频率就是 1MHZ / 10000 = 100HZ。需要注意的是 LED 灯刷新率在 50HZ 以下会有明显的闪烁,顾 PWM 的频率设置不能低于 50HZ。
最后不要忘记使能定时器。
/* 使能定时器 */
timer_enable(BSP_PWM_TIMER);
2
1.1.3.配置输出结构体
要使用定时器的 PWM 功能就是用定时器的输出功能,关于输出功能的参数配置有一个结构体,如图 1-3-1 所示。
- ocpolarity:通道输出的极性,也就是配置为低电平有效还是高电平有效,这里配置为高电平有效 TIMER_OC_POLARITY_HIGH。
- outputstate:通道输出状态,一般是使能 TIMER_CCX_ENABLE,使能 PWM 输出到端口。
其它的参数都是高级定时器使用的,可以不用配置。
1.1.4.配置定时器输出通道
配置好输出通道参数之后,需要初始化这个结构体,并且还需要配置使用定时器的通道几进行输出。
void timer_channel_output_config( uint32_t timer_periph,uint16_t channel, timer_oc_parameter_struct* ocpara);
这个函数是配置定时器通道的输出功能,有三个参数,第一个参数就是要使能的定时器外设,第二个参数是要使能的定时器通道,第三个参数是配置的输出结构体。
配置定时器输出功能代码如下:
/* 配置定时器输出功能 */
timer_channel_output_config(BSP_PWM_TIMER,TIMER_CH_0,&timer_ocintpara);
2
1.1.5.配置定时器输出通道占空比
void timer_channel_output_pulse_value_config( uint32_t timer_periph, uint16_t channel, uint32_t pulse);
这个函数是配置定时器通道输出的脉冲值。有三个参数,第一个参数是要配置的定时器,第二个参数是要配置的定时器通道,第三个参数是要设置的脉冲值。脉冲值的取值范围为(0-65535)。一般我们调节占空比也是调用这个函数,通过设置这个通道的输出脉冲值,改变不同的占空比。
这里我们将占空比设置为 50%,配置如下:
timer_channel_output_pulse_value_config(BSP_PWM_TIMER,TIMER_CH_0,5000 - 1); // 配置定时器通道输出脉冲值
这里设置输出脉冲值为 5000-1,我们设置的定时器的周期为 10000-1,则占空比为 5000 / 10000 = 50%。
void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel,uint16_t ocmode);
这个函数是配置外设定时器通道输出比较模式。有三个参数,第一个参数是要配置的定时器外设,第二个参数是要配置的定时器通道,第三个参数是要配置的比较模式。
关于第三个参数的可选选项如图 1-5-1 所示。
这里设置为 PWM 模式 0,也就是配置为 TIMER_OC_MODE_PWM0。配置代码如下:
timer_channel_output_mode_config(BSP_PWM_TIMER,TIMER_CH_0,TIMER_OC_MODE_PWM0);// 配置定时器通道输出比较模式
void timer_channel_output_shadow_config( uint32_t timer_periph, uint16_t channel, uint16_t ocshadow);
这个函数是配置定时器通道输出比较影子寄存器功能。有三个参数,第一个参数是要配置的定时器外设,第二个参数是要配置的定时器通道,第三个参数是输出比较影子寄存器功能的状态。我们禁止输出比较影子寄存器,配置如下:
timer_channel_output_shadow_config(BSP_PWM_TIMER,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);// 配置定时器通道输出影子寄存器
1.1.6.定时器自动重载影子使能
void timer_auto_reload_shadow_enable(uint32_t timer_periph);
这个函数是自动重载影子使能。有 1 个参数是要使能的定时器。配置如下:
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(BSP_PWM_TIMER);
2
到此,关于 PWM 的配置就完成了。
1.2.呼吸灯函数
要实现一个呼吸灯的效果,首先我们来看呼吸灯产生的原理。呼吸灯产生的原理就是 LED 灯逐渐变亮再逐渐变暗,然后一直循环下去。控制 LED 灯的亮暗是通过改变 PWM 的占空比,占空比越大,LED 灯越亮,占空比越小,LED 灯越暗。所以,我们只需要调节 PWM 的占空比就可以实现呼吸灯的效果。设置 PWM 的占空比的函数在之前介绍过,编写呼吸灯函数如下:
void pwm_breathing_lamp(void)
{
static uint16_t value = 0;
static uint8_t direct = 0;
if(direct == 0)
{
value += 300;
if(value > 10000)
direct = 1; // 改变方向
}else
{
value -= 300;
if(value <= 0)
direct = 0; // 改变方向
}
timer_channel_output_pulse_value_config(BSP_PWM_TIMER,TIMER_CH_0,value); // 配置定时器通道输出脉冲值
delay_1ms(50);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里简单介绍一下,如果 direct 为 0 则为正方向变化,脉冲值 value 逐渐变大,LED 灯逐渐变亮,当超出最大周期的时候,也就是亮度最大的时候,改变 direct 的值为 1,脉冲值 value 逐渐变小,LED 灯逐渐变暗,当小于等于 0 的时候,又改变 direct 的值为 0,继续正方向变化,然后一直循环下去。每一次脉冲值变化后都会重新配置。delay_1ms(50)是为了有一个变化的过程,不然执行太快看不出来效果,如果配置为 delay_1ms(10)将会执行的非常快,直接从暗变化到亮,再从亮变化到暗,能实现一个爆闪的效果。
1.3.实验现象
关于这一章节的代码,在资源包/04 软件资料/代码例程/里面的 009PWM 呼吸灯。
烧写我们的代码之后,会观察到 LED4 灯由暗变亮,继而由亮变暗的效果。