0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

GD32开发实战指南(基础篇) 第8章 定时器

嵌入式大杂烩 2023-05-12 22:14 次阅读

开发环境:

MDK:Keil 5.30

开发板:GD32F207I-EVAL

MCU:GD32F207IK

1 PWM输出的工作原理

脉冲宽度调制(PWM) ,是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。

GD32 的定时器除了 TIMER5 和 6(基本定时器)。其他的定时器都可以用来产生 PWM 输出。

每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,,将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)。

在上一节,讲解了定时器的相关寄存器即基本原理,本节将不再赘述。下面谈谈如何使用定时器的寄存器进行PWM输出的。若配置脉冲计数器TIMERx_CNT为向上计数,而重载寄存器TIMERx_CAR配置为N,即TIMERx_CNT的当前计数值数值X在CK_TIMER时钟源的驱动下不断累加,当TIMERx_CNT的数值X大于N时,会重置TIMERx_CNT数值为0重新计数。而在TIMERx_CNT计数的同时,TIMERx_CNT的计数值X会与比较寄存器TIMERx_CHxCV预先存储了的数值A进行比较,当脉冲计数器TIMERx_CNT的数值X小于比较寄存器TIMERx_CHxCV的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。如此循环,得到的输出脉冲周期就为重载寄存器TIMERx_CAR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMERx_CHxCV的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。

估计很多初学者看了上面的一段话都很蒙圈,没关系,下面以向上计数模式为例进行讲解。

1683894461407ai9zjve8vv

在PWM输出模式下,除了CNT(计数器当前值)、CAR(自动重装载值)之外,还多了一个值CHxCV(捕获/比较寄存器值)。当CNT小于CHxCV时,CHxCV通道输出低电平;当CNT等于或大于CHxCV时,CHxCV通道输出高电平。因此得到PWM的一个周期如下:

1.定时器从0开始向上计数;

2.当0-t1段,定时器计数器CNT值小于CHxCV值,输出低电平;

3.t1-t2段,定时器计数器CNT值大于CHxCV值,输出高电平;

4.当CNT值达到CAR时,定时器溢出,重新向上计数...循环此过程。

至此一个PWM周期完成。针对PWM重点关注两个寄存器, CAR寄存器确定PWM频率,CHxCV寄存器确定占空比

上文提到了PWM的输出模式,下面讲解PWM的工作模式:

  • PWM模式1(向上计数) :计数器从0计数加到自动重装载值(CAR),然后重新从0开始计数,并且产生一个计数器溢出事。
  • PWM模式2(向下计数) :计数器从自动重装载值(CAR)减到0,然后重新从重装载值(CAR)开始递减,并且产生一个计数器溢出事件。

这里我们仅利用 TIMER2产生多路 PWM 输出。如果要产生多路输出,大家可以根据我们的代码稍作修改即可。具体不同定时器对应引脚在对应芯片数据手册的引脚说明(pin description) 中查看。

[ps] 本文以F2系列为例进行讲解,GD不同系列其定时器个数不同

2 PWM输出的寄存器描述

同样,我们首先通过对 PWM 相关的寄存器进行讲解,大家了解了定时器 TIMER2的 PWM原理之后,我们再讲解怎么使用库函数产生 PWM 输出。

要使 GD32 的通用定时器 TIMERx 产生 PWM 输出,除了上一章介绍的寄存器外,我们还会用到 3 个寄存器,来控制 PWM 的。这三个寄存器分别是:通道控制寄存器(TIMERx_CHCTL0/1)、通道控制寄存器(TIMERx_CHCTL2)、捕获/比较寄存器(TIMERx_CHxCV)。接下来我们简单介绍一下这三个寄存器。

首先是通道控制寄存器(TIMERx_CHCTL0/1),该寄存器总共有2个,TIMERx_CHCTL0和TIMERx_CHCTL1。TIMERx_CHCTL0控制 CH1 和 2,而TIMERx_CHCTL1 控制 CH3 和 4。该寄存器的各位描述如下图。

1683894462034r9plgle44a

1683894462440oz34fdco6x

该寄存器的有些位在不同模式下,功能不一样,所以在上图中,我们把寄存器分了2层,上面一层对应输出而下面的则对应输入。关于该寄存器的详细说明,请参考《GD32F20x User Manual》。这里我们需要说明的是模式设置位CH0COMCTL,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种PWM 模式的区别就是输出电平的极性相反。

接下来,我们介绍通道控制寄存器(TIMERx_CHCTL2),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如下图。

1683894462744tsklgtikai

1683894463026lzyiovgssl

该寄存器比较简单, 我们这里只用到了CHxEN位,该位是输入/捕获 2 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。

最后,我们介绍一下捕获/比较寄存器(TIMERx_CHxCV),该寄存器总共有 4 个,对应 4 个输通道 CH0~3。因为这 4 个寄存器都差不多,我们仅以TIMERx_CH0CV为例介绍,该寄存器的各位描述如下图。

1683894463366hvxr57gmdc

在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。

假如我们要利用 TIMER2的 CH1 输出 PWM 来控制 DS0 的亮度,但是 TIMER2_CH1默认是接在 PA7上面的,这就可以通过重映射功能,把 TIMER2_CH1映射到 PB5 上。

GD32 的重映射控制是由复用重映射和调试 IO 配置寄存器控制的,该寄存器的各位描述如上图。我们这里用到的是 TIMER2的重映射,从上图可以看出,TIMER2_REMAP 是由[11:10]这 2 个位控制的。TIMER2_REMAP[1:0]重映射控制表如下表。

1683894463750rgwb4veuvf

默认条件下,TIMER2_REMAP[1:0]为 00,是没有重映射的,所以 TIMER2_CH0~TIMER2_CH3 分别是接在 PA6、 PA7、 PB0 和 PB1 上的,而我们想让 TIMER2_CH1 映射到 PB5 上, 则需要设置TIMER2_REMAP[1:0]=10,即部分重映射,这里需要注意,此时TIMER2_CH0 也被映射到 PB4 上了。

TIMER定时器的四路通道CHx_O输出PWM。

16838944641193rhskkcdf5

3 PWM输出实现

3.1 PWM代码分析

本章要实现通过TIMER2实现四路方波的输出,以TIMER2_CH0 输出 PWM 为例进行讲解。下面我们介绍通过库函数来配置该功能的步骤。

首先要提到的是,PWM 相关的函数设置在库函数文件gd32f20x_timer.h和gd32f20x_timer.c文件中。

1) 开启 TIMER2 时钟以及GPIO的时钟,配置 PA6为复用输出。

要使用 TIMER2,我们必须先开启 TIMER2的时钟,这点相信大家看了这么多代码,应该明白了。库函数使能 TIMER2及PA6时钟的方法是:

/* enable the GPIOA clock */
rcu_periph_clock_enable(RCU_GPIOA);
//Enable TIMER2 clock
rcu_periph_clock_enable(RCU_TIMER2);

库函数设置 AFIO 时钟的方法是:

/* 开启复用功能时钟 */
rcu_periph_clock_enable(RCU_AF);

2) 初始化 TIMER2,设置 TIMER2的 CAR 和 PSC。

在开启了 TIMER2 的时钟之后,我们要设置 CAR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。这在库函数是通过timer_init()函数实现的,在上一节定时器中断章节我们已经有讲解,这里就不详细讲解,调用的格式为:

/* TIMER2 configuration */
timer_init_struct.prescaler         = 0;
timer_init_struct.alignedmode       = TIMER_COUNTER_EDGE;
timer_init_struct.counterdirection  = TIMER_COUNTER_UP;
timer_init_struct.period            = 999;
timer_init_struct.clockdivision     = TIMER_CKDIV_DIV1;
timer_init_struct.repetitioncounter = 0;
timer_init(TIMER2, &timer_init_struct);

3) 设置 TIMER2_CH0的 PWM 模式,使能 TIMER2的 CH0输出。

接下来,我们要设置 TIMER2_CH0为 PWM 模式(默认是冻结的),在库函数中,PWM通道设置是通过函数 timer_channel_output_config()来设置的,我们直接来看看结构体 timer_oc_parameter_struct的定义:

/* channel output parameter structure definitions */
typedef struct {
    uint16_t outputstate;                       /*!< channel output state */
    uint16_t outputnstate;                      /*!< channel complementary output state */
    uint16_t ocpolarity;                        /*!< channel output polarity */
    uint16_t ocnpolarity;                       /*!< channel complementary output polarity */
    uint16_t ocidlestate;                       /*!< idle state of channel output */
    uint16_t ocnidlestate;                      /*!< idle state of channel complementary output */
} timer_oc_parameter_struct;

该结构体主要配置通道的状态,极性等,还需要设置占空比等配置,不同的通道需要分别设置。

/* PWM Mode configuration: Channel0 */
timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_oc_init_struct);

/* 通道2占空比设置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, CH0CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_0,TIMER_OC_MODE_PWM0);
/* 不使用输出比较影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);

4) 使能 TIM3。

我们需要使能 TIMER2。使能 TIMER2的方法前面已经讲解过:

timer_enable(TIMER2);

最后看下主函数代码:

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    //systick init
    sysTick_init();

    /* configure the TIMER peripheral */
    pwm_init ();

    while(1) 
    {
     
    }
}

是不是很简单,这里进行了PWM初始化,最核心的就是timer2_init()函数,其代码如下:

/*
    brief      configure the TIMER peripheral
    param[in]  none
    param[out] none
    retval     none
  */
void pwm_init(void)
{
    /* TIMER2 configuration: generate PWM signals with different duty cycles*/
    /* 定义一个定时器初始化结构体 */
    timer_parameter_struct timer_init_struct;
    /* 定义一个定时器输出比较参数结构体*/
    timer_oc_parameter_struct timer_oc_init_struct;

    /* PWM信号电平跳变值 */
    uint16_t CH0CV_Val = 500;
    uint16_t CH1CV_Val = 375;
    uint16_t CH2CV_Val = 250;
    uint16_t CH3CV_Val = 125;

    /* ----------------------------------------------------------------------- 
    TIMER2 Channel0 duty cycle = (TIMER2_CH0CV/ TIMER2_CAR+1)* 100% = 50%
    TIMER2 Channel1 duty cycle = (TIMER2_CH1CV/ TIMER2_CAR+1)* 100% = 37.5%
    TIMER2 Channel2 duty cycle = (TIMER2_CH2CV/ TIMER2_CAR+1)* 100% = 25%
    TIMER2 Channel3 duty cycle = (TIMER2_CH3CV/ TIMER2_CAR+1)* 100% = 12.5%
    ----------------------------------------------------------------------- */
    // gpio init
    timer_gpio_init();

    //Enable TIMER2 clock
    rcu_periph_clock_enable(RCU_TIMER2);
    /* 开启复用功能时钟 */
    rcu_periph_clock_enable(RCU_AF);

    timer_deinit(TIMER2);

    /* TIMER2 configuration */
    timer_init_struct.prescaler         = 0;
    timer_init_struct.alignedmode       = TIMER_COUNTER_EDGE;
    timer_init_struct.counterdirection  = TIMER_COUNTER_UP;
    timer_init_struct.period            = 999;
    timer_init_struct.clockdivision     = TIMER_CKDIV_DIV1;
    timer_init_struct.repetitioncounter = 0;
    timer_init(TIMER2, &timer_init_struct);

    /* PWM初始化 */
    timer_oc_init_struct.outputstate  = TIMER_CCX_ENABLE;		/* 通道使能 */
    timer_oc_init_struct.outputnstate = TIMER_CCXN_DISABLE;		/* 通道互补输出使能(定时器2无效) */
    timer_oc_init_struct.ocpolarity   = TIMER_OC_POLARITY_HIGH;	/* 通道极性 */
    timer_oc_init_struct.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;/* 互补通道极性(定时器2无效)*/
    timer_oc_init_struct.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;/* 通道空闲状态输出(定时器2无效)*/
    timer_oc_init_struct.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;/*互补通道空闲状态输出(定时器2无效) */

    /* PWM Mode configuration: Channel0 */
    timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_oc_init_struct);

    /* 通道2占空比设置 */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, CH0CV_Val);
    /* PWM模式0 */
    timer_channel_output_mode_config(TIMER2,TIMER_CH_0,TIMER_OC_MODE_PWM0);
    /* 不使用输出比较影子寄存器 */
    timer_channel_output_shadow_config(TIMER2,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);	

    /* PWM Mode configuration: Channel1 */
    timer_channel_output_config(TIMER2, TIMER_CH_1, &timer_oc_init_struct);

    /* 通道2占空比设置 */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, CH1CV_Val);
    /* PWM模式0 */
    timer_channel_output_mode_config(TIMER2,TIMER_CH_1,TIMER_OC_MODE_PWM0);
    /* 不使用输出比较影子寄存器 */
    timer_channel_output_shadow_config(TIMER2,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);	

    /* PWM Mode configuration: Channel2 */
    timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_oc_init_struct);

    /* 通道2占空比设置 */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, CH2CV_Val);
    /* PWM模式0 */
    timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);
    /* 不使用输出比较影子寄存器 */
    timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);	

    /* PWM Mode configuration: Channel3 */
    timer_channel_output_config(TIMER2, TIMER_CH_3, &timer_oc_init_struct);

    /* 通道2占空比设置 */
    timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_3, CH3CV_Val);
    /* PWM模式0 */
    timer_channel_output_mode_config(TIMER2,TIMER_CH_3,TIMER_OC_MODE_PWM0);
    /* 不使用输出比较影子寄存器 */
    timer_channel_output_shadow_config(TIMER2,TIMER_CH_3,TIMER_OC_SHADOW_DISABLE);	

    /* 自动重装载影子比较器使能 */
    timer_auto_reload_shadow_enable(TIMER2);	

    /* TIMER2 enable */
    timer_enable(TIMER2);
}

3.2 PWM周期、占空比分析

根据前面的参数配置,我们可以算出PWM的输出周期:

PWM=1/(Tclk/(psc+1))*(arr+1)

这里我们 arr=999 psc=0 Tclk=120Mhz ,

PWM=1/(120Mhz/(1))*(999+1)=1/120ms

因此PWM的输出频率120KHz,周期是8.3us。

PWM的占空比为:

Dutycycle=(CHxCV/CAR+1)* 100%

PWM自动重装值为999,四个通道的跳变值分别为500,375,250,125。因此,TIMER2的四个通道的占空比分别为50%,37.5%,25%,12.5%。

4 PWM输出的实验现象

在前面我们输出了TIMER2的通道0(PA6)、1(PA7)、2(PB0)、3(PB1)不同占空比的 PWM 信号。接下来就看看PWM的输出,PWM 信号可以通过示波器看到,下面笔者就是用逻辑分析仪查看波形。

首先笔者使用的逻辑分析仪是Kingst LA5016,当然啦,其他的也可以,关于逻辑分析仪的相关使用笔者这里就不介绍了,可以查看官方资料

首先将通道 0(PA6)、1(PA7)、2(PB0)、3(PB1)分别接到逻辑分析仪的CH0 – CH3,然后下载程序到板子中,打开Kingst VIS,然后进行采样。

1683894464486l2japa0ykw

我们就可以看到不同通道的实际周期,占空比等信息

C:\\Users\\BruceOu\\Desktop\\3434.png

从上图可以看到,实际测量的频率和占空比和理论是相符的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • PWM
    PWM
    +关注

    关注

    114

    文章

    4905

    浏览量

    209970
  • 定时器
    +关注

    关注

    23

    文章

    3148

    浏览量

    112060
  • Cortex-M
    +关注

    关注

    2

    文章

    224

    浏览量

    29574
  • GD32
    +关注

    关注

    7

    文章

    336

    浏览量

    23748
收藏 人收藏

    评论

    相关推荐

    GD32对Timer定时器原理的详细讲解

    GD32 Timr定时器看起来比较复杂啊。一看GD32E10x 的SPEC:一个高级定时器,三个通用定时器,还有一个基本
    的头像 发表于 04-22 17:02 1.5w次阅读
    <b class='flag-5'>GD32</b>对Timer<b class='flag-5'>定时器</b>原理的详细讲解

    GD32开发实战指南(基础篇) 第7章 定时器

    系统滴答定时器一般用来提供“心跳”作用,而GD32定时器最基本功能也是定时,可以设置不同时间长度的定时
    的头像 发表于 05-11 09:00 9251次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础篇) 第7章 <b class='flag-5'>定时器</b>

    GD32开发实战指南(基础篇) 第16章 RTC

    运行的定时器。从定时器的角度来说,相对于通用定时器 TIMER 外设,它十分简单,只有很纯粹的计时功能(当然,可以触发中断);但从掉电还继续运行的角度来说,它却是 GD32中唯一一个具
    的头像 发表于 05-18 22:14 5759次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础篇) 第16章 RTC

    【图书分享】《STM32库开发实战指南

    库函数开发小结 10 DMA——为CPU减负 11 ADC实验(DMA方式) 12
    发表于 03-13 17:01

    好书分享】入门经典:STM32库开发实战指南

    减负 11 ADC实验(DMA方式) 12 SysTick(系统滴答定时器13
    发表于 06-24 20:53

    Arduino开发实战指南 AVR

    模块的应用第三应用8打造自己的遥控履带车9
    发表于 08-03 16:14

    《嵌入式-STM32开发指南》第二部分 基础 - 5 PWM(HAL库)精选资料分享

    标准库3.5实现:《嵌入式-STM32开发指南》第二部分 基础 - 5 PWM5.1 PWM_输出5.1.1 PWM输出的工作原理脉冲宽度调制(PWM),是英文“ Pulse W
    发表于 08-10 08:05

    什么是GD32

    一、前言什么GD32GD32是国内开发的一款单片机,据说开发的人员是来自ST公司的,GD32也是以STM32作为模板做出来的。所以
    发表于 08-12 07:46

    STM32定时器中断实验

    1、参考资料《STM32F1开发指南-库函数版本》-13 定时器中断时实验《STM32中文参考手册V10》-14
    发表于 08-19 09:16

    定时器的工作原理

    标准库3.5实现:《嵌入式-STM32开发指南》第二部分 基础 - 4 定时器4.1定时器
    发表于 08-19 07:49

    GD32 MCU原理及固件库开发指南》 + 初读感悟

    开发能够避免每次操作寄存时去查芯片的应用手册,这样能很好地提高开发效率。GD32 MCU原理固件库开发指南这本书它详细描述了基本所有外设
    发表于 03-31 22:11

    GD32 MCU原理及固件库开发指南》+读后感

    2介绍GD32 MCU快速入门与开发平台搭建的方法,包括对软硬件开发平台、调试工具、GD32
    发表于 06-06 21:52

    GD32-Colibri-F207实验板定时器1停止

    GD32-Colibri-F207实验板定时器1停止,很好的GD32资料,快来学习吧。
    发表于 04-21 14:24 11次下载

    GD32开发实战指南(基础篇) 第19章 程序加密

    GD32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭。96位的产品唯一身份标识所提供的参考号码对任意一个GD32微控制器
    的头像 发表于 05-20 09:10 3397次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础篇) 第19章 程序加密

    GD32 Timer定时器周期时间计算公式

    有小伙伴反馈GD32 Timer定时器的周期时间不知如何计算,今天就来安排。
    的头像 发表于 01-22 09:53 745次阅读
    <b class='flag-5'>GD32</b> Timer<b class='flag-5'>定时器</b>周期时间计算公式