一、前言
这一节开始分享定时器(TIMER)的功能,定时器的功能有很多,比如:常用的定时计数(延时)、电机驱动或电源驱动PWM波、系统的时间片、中断服务函数等。本小节以输出一个带死区的互补PWM,先简单介绍PWM的基本实现原理及概念。
下图信息:蓝色线是一个从0到99的三角载波(循环);红色线是一个30的比较器值(可理解为计数器计数到30)、黄色线是99的周期值(可理解为计数器计数到99)、绿色线是输出对应的PWM波。
他们之间的数学关系有:
CK_PSC是定时器时钟源频率,GD32502最大时钟频率为100MHz,也可以配置其他大小的时钟频率;
PSC是预分频系数,若分频1,则分频系数为1;
ARR是计数器计数最大值,此处为99;
CCR是设定有效脉宽值,此处为30;
上述信息可得:PWM频率:Freq = 100MHz / (1 + 1)/ (99 + 1)= 500KHz
PWM占空比:Duty = 30 / (99 + 1)= 30%
PWM分辨率:Reso = 1 /(99 + 1)= 0.01
为什么图中绿线计数器到0时,PWM波置高,计数器计数到脉宽值30置低呢?
那是因为用到了输出比较控制器,根据输出比较寄存器设定的值与计数器计数的大小关系,从而来配置PWM输出的波形。
二、定时器介绍(TIMER)
定时器主要分为三种(基本定时器、通用定时器、高级定时器):能够实现的功能依次增加,由于本文选择输出一个带死区的互补PWM波,结合下图可知只能选择定时器0/7/19/20(高级定时器),因此选择了定时器0。
高级定时器的主要特征如下:
如果要使用其他功能,请参考用户手册。为了实现带死区的互补PWM波,需要使用到时钟源为内部时钟、预分频器、计数模式向上计数、PWM模式、死区时间配置等。
三、配置代码步骤
配置步骤如下:
1、开启时钟及预分频器等(GPIO时钟、系统配置时钟、定时器时钟)
2、配置GPIO引脚模式(输出PWM通道口)
3、配置比较器模式及死区配置(上升沿、死区、影子寄存器、使能等)
补充1:查找GD32502的数据手册可知,选用高级定时器TIME0,输出的两个通道引脚口分别为PE4(TIMER0_MCH1)和PE5(TIMER0_CH1)。
补充2:GPIO引脚口复用功能选择,PE4和PE5功能为PWM发波(TIMER0_MCH1和TIMER0_CH1),故选择为AF1。
接下来,跟到我一起来实现开头那种绿色线的输出PWM波(频率500KHz、占空比30%)
代码展示:
#include "gd32e502.h"
#include <stdio.h>
void gpio_config(void)
{
//打开GPIOE时钟
rcu_periph_clock_enable(RCU_GPIOE);
//设置GPIO模式 备用功能模式 带上拉电阻 GPIO_PIN_x
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4 | GPIO_PIN_5);
//设置GPIO输出模式和速度 推挽输出模式 最大输出速度50M GPIO_PIN_x
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 |
GPIO_PIN_5);
//复用功能选择
gpio_af_set(GPIOE, GPIO_AF_1, GPIO_PIN_4 | GPIO_PIN_5);
}
void timer_config(void)
{
timer_parameter_struct timer_initpara; //定时器0配置结构体名重定义
timer_oc_parameter_struct timer_ocintpara; //PWM模式配置结构体名重定义
timer_break_parameter_struct timer_breakpara; //死区结构体配置名重定义
rcu_periph_clock_enable(RCU_TIMER0); //初始化定时器0时钟
timer_deinit(TIMER0); //定时器默认初始化
//定时器0配置
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = 1; //预分配系数1
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; //无中央对齐计数模式
timer_initpara.counterdirection = TIMER_COUNTER_UP; //向上计数
timer_initpara.period = 99; //周期计数值(100-1)
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; //时钟分配因子
timer_initpara.repetitioncounter = 0; //重新计数从0开始
timer_init(TIMER0, &timer_initpara);
// PWM模式配置
timer_channel_output_struct_para_init(&timer_ocintpara);
timer_ocintpara.outputstate = TIMER_CCX_ENABLE; //输出通道使能
timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE; //互补通道使能
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; //通道输出高电平
timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; //互补通道输出高电平
timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; //空闲状态下通道输出低电平
timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;//空闲状态下互补通道输出低电平
//选择定时器0通道CH1
timer_channel_output_config(TIMER0, TIMER_CH_1, &timer_ocintpara);
//死区配置
timer_break_struct_para_init(&timer_breakpara);
timer_breakpara.runoffstate = TIMER_ROS_STATE_DISABLE;
timer_breakpara.ideloffstate = TIMER_IOS_STATE_DISABLE ;
timer_breakpara.deadtime = 10; //10*(1/100MHz)= 100ns
timer_breakpara.breakpolarity = TIMER_BREAK_POLARITY_HIGH;
timer_breakpara.outputautostate = TIMER_OUTAUTO_ENABLE;
timer_breakpara.protectmode = TIMER_CCHP_PROT_0;
timer_breakpara.breakstate = TIMER_BREAK_ENABLE;
timer_break_config(TIMER0, &timer_breakpara);
//PWM通路CH1/MCH1配置
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, 30); //占空比30%
timer_channel_output_mode_config(TIMER0, TIMER_CH_1, TIMER_OC_MODE_PWM0); //选择PWM0
模式
timer_channel_output_shadow_config(TIMER0, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE); //
不使能影子寄存器
timer_auto_reload_shadow_enable(TIMER0); //自动重新装载使能
timer_primary_output_config(TIMER0, ENABLE); //输出通道使能
timer_enable(TIMER0); //使能定时器0
}
int main(void)
{
gpio_config();
timer_config();
while(1);
}
四、效果展示
成功输出了一对互补PWM波,满足500KHz,占空比30%。(不带死区时间)
加了死区100ns后的PWM波,满足500KHz,占空比变为25%。(带死区时间)
预告:下一小节来实现DAC采样功能