【单片机/嵌入式】【梁山派】学习日志12:脉宽调制PWM

一、PWM原理

1.1PWM基础知识

1.1.1 什么是PWM

PWM(Pulse Width Modulation脉宽调制)利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波占空比调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有,要么完全无。比如我们的电压输出是5v的,那么经过改变PWM的占空比,可以达到在一定时间内输出3.3V或者1.3V的效果。

1.1.2 GD32 PWM介绍

前面我们介绍了GD32F450ZGT6一共有14个定时器,可以分为五种类型,高级定时器0/7、通用定时器(L0)1-4、通用定时器(L1)8/11、通用定时器(L2)9/10/12/13和基本定时器5/6。而PWM功能就是在定时器的基础上实现的,但不是所有的定时器都支持PWM输出功能。从用户手册上可以了解到,高级定时器拥有4个PWM通道,通用定时器L0拥有4个PWM通道,通用定时器L1拥有2个PWM通道,通用定时器L2拥有1个PWM通道,而基本定时器没有PWM通道。每一个PWM通道都对应单片机的一个管脚,这个引脚不是唯一固定的,可能有一个或者两个管脚都对应同一个通道。比如说TIMER1_CH2对应PA2和PB10,就是说PA2和PB10管脚都可以配置为定时器的通道2,我们在使用的时候可以任选其一进行配置。

1.1.2 PWM基本参数

PWM是脉冲宽度调制,具有两个非常重要的参数:频率和占空比。

频率:PWM的频率是整个周期的倒数

占空比:占空比是指一个周期内高电平所占的比例

1.1.3 控制方法

采样控制理论中有一个重要结论:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。PWM控制技术就是以该结论为理论基础,对半导体开关器件的导通和关断进行控制,使输出端得到一系列幅值相等而宽度不相等的脉冲,用这些脉冲来代替正弦波或其他所需要的波形。按一定的规则对各脉冲的宽度进行调制,即可改变逆变电路输出电压的大小,也可改变输出频率

1.1.4 基本原理

控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等但宽度不一致的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,即可改变逆变电路输出电压的大小,也可改变输出频率。

1.1.5定时器的PWM输出功能

在PWM输出模式下(PWM模式0是配置CHxCOMCTL为3’b110,PWM模式1是配置CHxCOMCTL为3’b111),通道根据TIMERx_CAR寄存器TIMERx_CHxCV寄存器的值,输出PWM波形。

根据计数模式,我们可以分为两种PWM波:EAPWM(边沿对齐PWM)和CAPWM(中央对齐PWM)。

EAPWM的周期由TIMERx_CAR寄存器值决定,占空比由TIMERx_CHxCV寄存器值决定。EAPWM时序图显示了EAPWM的输出波形和中断。

CAPWM的周期由(2*TIMERx_CAR寄存器值)决定,占空比由(2*TIMERx_CHxCV寄存器值)决定。CAPWM时序图显示了CAPWM的输出波形和中断。

PWM0模式下(CHxCOMCTL==3’b110),如果TIMERx_CHxCV寄存器的值大于TIMERx_CAR寄存器的值,通道输出一直为有效电平

PWM0模式下(CHxCOMCTL==3’b110),如果TIMERx_CHxCV寄存器的值等于0,通道输出一直为无效电平

 

 

1.2 PWM优点

PWM的一个优点是从处理器到被控制系统信号都是数字形式无需进行数模转换。让信号保持为数字形式可将噪声影响降到最小。噪声只有在强到足以将逻辑1改变为逻辑0或将逻辑0改变为逻辑1时,才能对数字信号产生影响。对噪声抵抗能力的增强是PWM相对于模拟控制的另一个优点,而且这也是在某些时候将PWM用于通信的主要原因。

1.3 PWM应用

PWM可应用于电机调速、功率调制、PID调节、通信等,配置简单、抗干扰能力强。可以通过PWM来控制LED灯的亮暗变化,可以通过PWM信号来控制无源蜂鸣器发出简单的声音以及实现功率继电器的线圈节能等。PWM用来驱动电机调节电机转速是非常重要的内容。

1.4 PWM实验

通过驱动PWM实现一个呼吸灯的效果,通过PWM信号占空比的变化,可以实现流过LED电流的不同,实现LED亮暗的渐变。

一般人眼睛对于80HZ以上刷新频率则完全没有闪烁感,由于频率很高时看不到闪烁,占空比越大LED越亮,占空比越小LED越暗。所以在频率一定时,可以用不同占空比改变LED灯的亮度,使其达到一个呼吸灯的效果。

二、PWM呼吸灯

2.1配置流程

一般使用定时器PWM功能,都需要有以下几个步骤。

l  配置通道引脚GPIO

l  配置定时器

l  配置输出结构体

l  配置定时器输出通道

l  配置定时器输出通道占空比

l  定时器自动重载影子使能

2.1.1配置通道引脚GPIO

GD32F450ZGT6单片机一共有14个定时器,除了基本定时器没有PWM功能,其它定时器都有1个、2个或4个PWM通道。这一章节就用PWM实现一个呼吸灯的效果。首先LED灯连接在PA5引脚上,查找芯片数据手册的第46可知,PA5有好几个定时器通道的复用功能,如图所示。

 

这里选择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

初始化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);

}

上面的代码都是一些常规操作,需要注意的点就是设置复用功能的时候配置为GPIO_AF_1,因为我们使用的是PA5引脚AF1的功能。

2.1.2配置定时器

前面介绍过PWM输出是依赖于定时器的,所以要对定时器进行配置,但是我们不使用定时器的中断功能,故不用对定时器的中断进行配置

又因为我们使用的PA5的AF1是定时器1的通道0,所以我们要配置定时器1的参数。关于定时器1的宏定义如下:

/* TIMER */

#define BSP_PWM_TIMER_RCU  RCU_TIMER1  // 定时器时钟

#define BSP_PWM_TIMER    TIMER1   // 定时器

接下来就要配置定时器的参数

首先是定义定时器的参数结构体

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 = 199; //  时钟预分频值  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); // 初始化定时器

这里和之前的定时器实验相比就只有两处发生变化,一个就是时钟分频值修改为199,那PSC_CLK = 200MHZ / 200 = 1MHZ,周期值设置为10000 -1,那对应的PWM输出的频率就是1MHZ / 10000 = 100HZ。需要注意的是LED灯刷新率在50HZ以下会有明显的闪烁,故PWM的频率设置不能低于50HZ

最后不要忘记使能定时器

/* 使能定时器 */

timer_enable(BSP_PWM_TIMER);

2.1.3配置输出结构体

要使用定时器的PWM功能就是用定时器的输出功能,关于输出功能的参数配置有一个结构体,如图所示。

 

在gd32f4xx_timer.h文件中

 

ocpolarity:通道输出的极性,也就是配置为低电平有效还是高电平有效,这里配置为高电平有效TIMER_OC_POLARITY_HIGH。

outputstate:通道输出状态,一般是使能TIMER_CCX_ENABLE,使能PWM输出到端口

其它的参数都是高级定时器使用的,可以不用配置。

2.1.4配置定时器输出通道

配置好输出通道参数之后,需要初始化这个结构体,并且还需要配置使用定时器的通道几进行输出。void timer_channel_output_config(uint32_t timer_periph,uint16_t channel, timer_oc_parameter_struct* ocpara);这个函数是配置定时器通道的输出功能,有三个参数,第一个参数就是要使能的定时器外设,第二个参数是要使能的定时器通道,第三个参数是配置的输出结构体。固件库手册658

 

 

配置定时器输出功能代码如下:

/* 配置定时器输出功能 */

timer_channel_output_config(BSP_PWM_TIMER,TIMER_CH_3,&timer_ocintpara);

2.1.5配置定时器输出通道占空比

void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse); 这个函数是配置定时器通道输出的脉冲值。有三个参数,第一个参数是要配置的定时器,第二个参数是要配置的定时器通道,第三个参数是要设置的脉冲值。脉冲值的取值范围为(0-65535。一般我们调节占空比也是调用这个函数,通过设置这个通道的输出脉冲值,改变不同的占空比

固件库手册660

 

 

这里我们将占空比设置为50%,配置如下:

timer_channel_output_pulse_value_config(BSP_PWM_TIMER,TIMER_CH_3,5000 - 1); // 配置定时器通道输出脉冲值

这里设置输出脉冲(宽度)值 pulse5000-1,我们设置的定时器的周期10000-1,则占空比5000 / 10000 = 50%

void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel,uint16_t ocmode); 这个函数是配置外设定时器通道输出比较模式。有三个参数,第一个参数是要配置的定时器外设,第二个参数是要配置的定时器通道,第三个参数是要配置的比较模式。

固件库手册659

 

 

这里设置为PWM模式0,也就是配置为TIMER_OC_MODE_PWM0。配置代码如下:

timer_channel_output_mode_config(BSP_PWM_TIMER,TIMER_CH_3,TIMER_OC_MODE_PWM0);// 配置定时器通道输出比较模式

void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel, uint16_t ocshadow); 这个函数是配置定时器通道输出比较影子寄存器功能。有三个参数,第一个参数是要配置的定时器外设,第二个参数是要配置的定时器通道,第三个参数是输出比较影子寄存器功能的状态。

固件库手册661

 

 

我们禁止输出比较影子寄存器,配置如下:

timer_channel_output_shadow_config(BSP_PWM_TIMER,TIMER_CH_3,TIMER_OC_SHADOW_DISABLE);// 配置定时器通道输出影子寄存器

2.1.6定时器自动重载影子使能

void timer_auto_reload_shadow_enable(uint32_t timer_periph); 这个函数是自动重载影子使能。有1个参数是要使能的定时器。

固件库手册639

 

配置如下:

/* auto-reload preload enable */

 timer_auto_reload_shadow_enable(BSP_PWM_TIMER);

到此,关于PWM的配置就完成了。

2.1.7影子寄存器

简言之,影子寄存器起到了缓冲作用,使预分频器具有缓冲功能,因此可对预分频器进行实时更改(实时写入数据)。新的预分频比将在下一更新事件发生时被采用。用户设定值->寄存器->影子寄存器->起作用。

所有真正需要起作用的寄存器(shadowregister)可以在同一个时间(发生更新事件时)被更新为所对应的preloadregister的内容,这样可以保证多个通道的操作能够准确地同步。、

如果没有shadowregister,或者preloadregister和shadowregister是直通的,即软件更新preloadregister时,同时更新了shadowregister,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的

参考文章:

【STM32 影子寄存器】https://zhuanlan.zhihu.com/p/349545023

【定时器-影子寄存器】https://www.cnblogs.com/ckk-blog/p/13770439.html

【STM32定时器中断之影子寄存器的介绍】https://blog.csdn.net/safasdfe/article/details/122714919

2.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_3,value); // 配置定时器通道输出脉冲值   

    delay_1ms(50);

}   

这里简单介绍一下,如果direct为0则为正方向变化,脉冲值value逐渐变大,LED灯逐渐变亮,当超出最大周期的时候,也就是亮度最大的时候,改变direct的值为1,脉冲值value逐渐变小,LED灯逐渐变暗,当小于等于0的时候,又改变direct的值为0,继续正方向变化,然后一直循环下去。每一次脉冲值变化后都会重新配置。delay_1ms(50)是为了有一个变化的过程,不然执行太快看不出来效果,如果配置为delay_1ms(10)将会执行的非常快,直接从暗变化到亮,再从亮变化到暗,能实现一个爆闪的效果。

2.3实验现象

 

posted @ 2022-11-12 17:50  U羊U  阅读(1152)  评论(1编辑  收藏  举报