返回列表 发新帖我要提问本帖赏金: 100.00元(功能说明)

[研电赛技术支持] 【GD32定时器】定时器的花样用法

[复制链接]
14983|13
 楼主| 呐咯密密 发表于 2022-2-24 15:53 | 显示全部楼层 |阅读模式
本帖最后由 呐咯密密 于 2022-2-24 15:59 编辑

#申请原创# @21小跑堂
前言:

今天又是解决问题贴的一天,现在跑堂不熬汤,不给压力了,只能从论坛抽个问题发帖解决了,同时也欢迎各位有问题在论坛多多提问,这里大佬多多,你的问题会最大程度给予解决,提问传送门ARM论坛,版主工作日均在线。

本文主要着重于定时器的应用侧,在原理方面不会进行详细介绍,如果在简介原理时出现错误,还望各位见谅指正。硬件平台:GD32E230C8T6

SysTick 定时器

SysTick 是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器中的使能位清除, 就永不停息。 该定时器的介绍在MCU的手册中一般不会介绍,因为这是内核中的定时器,可查找相关内核手册来获取相关信息。该定时器一般用作于延时函数,对于一般的延时还是很方便的。

TIMER定时器

TIME定时器属于MCU的外设定时器,使用频繁且作用很大。在笔者之前的项目中TIMER定时器一般就用于计时,或者添加一个定时器的中断,用于在一定的时间后进入中断去执行相应的指令,这只是TIMER的基础用法。但是基于定时器的计数原理,可拓展的功能太多,这里就整理一下最近使用的定时器的深度用法,可大大提升定时器的应用场景。

从最常用的PWM说起。

定时器的PWM输出应该算是比较常用的功能,在LED的调光和电机控制中较为常用,下面介绍一下最基础的输出固定评率和占空比的PWM代码:

  1. void timer_config(void)
  2. {
  3.    
  4.     /* 使能 GPIOA 时钟 */
  5.     rcu_periph_clock_enable(RCU_GPIOA);
  6.     /* 使能 GPIOAB 时钟 */
  7.     rcu_periph_clock_enable(RCU_GPIOB);

  8.     /*初始化PWM输出引脚 PB0(TIMER2 CH2) */
  9.     gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
  10.     gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0);
  11.     gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_0);
  12.    
  13.     /*初始化PWM输出引脚 PA7(TIMER2 CH1) */
  14.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
  15.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_7);
  16.     gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_7);

  17.     /*初始化PWM输出引脚 PA6(TIMER2 CH0) */
  18.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
  19.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
  20.     gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);
  21.         
  22.         /* 结构体 */
  23.     timer_oc_parameter_struct timer_ocinitpara;
  24.     timer_parameter_struct timer_initpara;

  25.     /* 使能定时器时钟 */
  26.     rcu_periph_clock_enable(RCU_TIMER2);

  27.     timer_deinit(TIMER2);
  28.     /* 初始化TIMER相关结构体参数 */
  29.     timer_struct_para_init(&timer_initpara);
  30.     /* TIMER2 初始化 */
  31.     timer_initpara.prescaler         = 71;                //预分频值
  32.     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;//对其模式
  33.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;  //计数方向
  34.     timer_initpara.period            = 15999;             //周期
  35.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;  //时钟分频因子
  36.     timer_initpara.repetitioncounter = 0;                 //重复计数值
  37.     timer_init(TIMER2, &timer_initpara);

  38.     /* 初始化定时器通道输出参数结构 */
  39.     timer_channel_output_struct_para_init(&timer_ocinitpara);
  40.     /* 配置定时器通道输出功能 */
  41.     timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;                   //通道输出状态
  42.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;                 //互补通道输出状态
  43.     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;             //通道输出极性
  44.     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;                        //互补通道输出极性
  45.     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;                        //空闲状态下通道输出
  46.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;           //空闲状态先互补通道输出极性
  47.     timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_ocinitpara);
  48.     timer_channel_output_config(TIMER2, TIMER_CH_1, &timer_ocinitpara);
  49.     timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocinitpara);

  50.     /* CH0 configuration in PWM mode0, duty cycle 25% */
  51.     timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 4000);//设置通道比较值
  52.     timer_channel_output_mode_config(TIMER2, TIMER_CH_0, TIMER_OC_MODE_PWM0);//设置通道输出比较模式
  53.     timer_channel_output_shadow_config(TIMER2, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);//配置TIMER通道输出比较影子寄存器功能

  54.     /* CH1 configuration in PWM mode0, duty cycle 50% */
  55.     timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, 8000);
  56.     timer_channel_output_mode_config(TIMER2, TIMER_CH_1, TIMER_OC_MODE_PWM0);
  57.     timer_channel_output_shadow_config(TIMER2, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);

  58.     /* CH2 configuration in PWM mode0, duty cycle 75% */
  59.     timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 12000);
  60.     timer_channel_output_mode_config(TIMER2, TIMER_CH_2, TIMER_OC_MODE_PWM0);
  61.     timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);

  62.     timer_auto_reload_shadow_enable(TIMER2);//自动重载影子使能
  63.      /* TIMER2 使能 */
  64.     timer_enable(TIMER2);
  65.    
  66. }

首先要明白定时器的输出的引脚并不是随便定义的,具体可参照手册来确定:

66796214884c4abc0.png

其次在使用PWM功能时我们主要关注的是输出波形的频率和占空比,那么这里我们对定时器的设置主要就是设置预分频值(prescaler)和周期(period)以及通道比较值。在上述例程中我使用TIMER2,预分频值为72-1=71,周期为16000-1=15999。系统时钟72M,那么得以计算出:

定时器的时钟频率为TIMER2CLK=systemcoreclock/prescaler = 72MHz/72=1MHz

PWM的频率为TIMER2CLK/period=1MHz/16000 = 62.5Hz.

    TIMER2 通道0占空比 = (4000/ 16000)* 100  = 25%

    TIMER2 通道0占空比 = (8000/ 16000)* 100  = 50%

    TIMER2 通道0占空比 = (12000/ 16000)* 100 = 75%

示波器捕捉如下:
533796215a630e9be9.png

如果我们需要动态调节频率和占空比只需调用以下函数:
设置预分频值:
timer_prescaler_config(uint32_t timer_periph, uint16_t prescaler, uint8_t pscreload)
timer_periph:TIMERx(x=0,2,5,13..16)
prescaler:预分频值。
pscreload:生效时间。(TIMER_PSC_RELOAD_NOW:立即生效   TIMER_PSC_RELOAD_UPDATE:下次更新事件到来生效 )
设置通道输出脉冲值(占空比)
timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse)

timer_periph:TIMERx(x=0,2,5,13..16)

channel:通道值

pulse:通道输出脉冲值


至此,一个简单的PWM输出便完成了,但是当我们想平滑的控制一个灯的亮灭,总不能一直通过函数来进行不停地改变占空比,于是这里可以启用TIMER的DMA功能。

定义TIMER0通道1的地址:#define TIMER2_CH0CV                    ((uint32_t)0x40000434)

定义一个需要的数组变量:uint16_t buffer[3] = {4000, 8000, 12000};

添加DMA初始化:

  1. void timer_dma_config(void)
  2. {
  3.     dma_parameter_struct dma_init_struct;

  4.     /* enable DMA clock */
  5.     rcu_periph_clock_enable(RCU_DMA);

  6.     /* initialize DMA channel4 */
  7.     dma_deinit(DMA_CH2);
  8.     /* DMA channel4 initialize */
  9.     dma_init_struct.periph_addr  = (uint32_t)TIMER2_CH0CV;
  10.     dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
  11.     dma_init_struct.memory_addr  = (uint32_t)buffer;
  12.     dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
  13.     dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  14.     dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
  15.     dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
  16.     dma_init_struct.number       = 3;
  17.     dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
  18.     dma_init( DMA_CH2, &dma_init_struct);
  19.    
  20.     /* enable DMA circulation mode */
  21.     dma_circulation_enable(DMA_CH2);
  22.     /* enable DMA channel4 */
  23.     dma_channel_enable(DMA_CH2);
  24. }

将之前的通道0的输出脉冲值修改成buffer数组:

timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, buffer[0]);

TIMER0更新DMA请求启用

timer_dma_enable(TIMER2, TIMER_DMA_UPD);

TIMER2_CH0CV为定时器2通道0的地址,该地址可通过手册查询:

先找到TIMER2的基地址:

479216215b52db4aa8.png

再找到通道0的偏移地址:

793596215b5674b229.png

两个地址相加得到TIMER2_CH0CV

buffer数组为需要设置的脉冲宽度,根据需要扩充大小,数据越多,波形变化越平滑,这里为了试验只取了三个数组。

对于DMA通道的选择可以注意一下:
740486215b6c9461e3.png

选择的是TIMER2_UP,而不是TIMER_CH0!!!
示波器采样如下:
640176215b63300011.png




主从定时器(定时器互联)

在使用定时器的过程中,有时一个定时器并不能满足我们的需求,此时可以尝试使用定时器的级联,将某个定时器作为主定时器,另一个作为从定时器,从而达到自己的目的。

三个相互级联的PWM输出:

需求:一路固定以250HZ的频率输出PWM,二路以62.5Hz频率输出,三路以15.625Hz频率输出。

可以看出来他们之间使4倍的关系,可以以一路为主定时器,二路为从定时器,捕获一路4个更新事件输出一个周期信号,同时二路作为三路的主定时器,三路捕获二路四个更新事件输出一个周期信号。

代码实现:
  1. void timer_config(void)
  2. {
  3.     rcu_periph_clock_enable(RCU_GPIOA);

  4.     /*PA2(TIMER14 CH0)*/
  5.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
  6.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
  7.     gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_2);
  8.    
  9.     /*PA6(TIMER2 CH0)*/
  10.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
  11.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
  12.     gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);

  13.     /*PA8(TIMER0 CH0)*/
  14.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
  15.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8);
  16.     gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_8);
  17.         
  18.         
  19.     timer_oc_parameter_struct timer_ocinitpara;
  20.     timer_parameter_struct timer_initpara;


  21.     rcu_periph_clock_enable(RCU_TIMER0);
  22.     rcu_periph_clock_enable(RCU_TIMER14);
  23.     rcu_periph_clock_enable(RCU_TIMER2);

  24.     timer_deinit(TIMER14);
  25.     timer_struct_para_init(&timer_initpara);
  26.     timer_initpara.prescaler         = 71;
  27.     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
  28.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
  29.     timer_initpara.period            = 3999;
  30.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
  31.     timer_initpara.repetitioncounter = 0;
  32.     timer_init(TIMER14, &timer_initpara);

  33.     timer_channel_output_struct_para_init(&timer_ocinitpara);

  34.     timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;
  35.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
  36.     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
  37.     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
  38.     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
  39.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
  40.     timer_channel_output_config(TIMER14, TIMER_CH_0, &timer_ocinitpara);

  41.     timer_channel_output_pulse_value_config(TIMER14, TIMER_CH_0, 2000);
  42.    
  43.     timer_channel_output_mode_config(TIMER14, TIMER_CH_0, TIMER_OC_MODE_PWM0);
  44.   
  45.     timer_channel_output_shadow_config(TIMER14, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);


  46.     timer_auto_reload_shadow_enable(TIMER14);
  47.     /* 选择主从模式 */
  48.     timer_master_slave_mode_config(TIMER14, TIMER_MASTER_SLAVE_MODE_ENABLE);
  49.     /* 触发器输出使用TIMER14更新事件 */
  50.     timer_master_output_trigger_source_select(TIMER14, TIMER_TRI_OUT_SRC_UPDATE);

  51.     timer_primary_output_config(TIMER14, ENABLE);

  52.     timer_deinit(TIMER2);

  53.     timer_struct_para_init(&timer_initpara);
  54.     timer_initpara.prescaler         = 0;
  55.     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
  56.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
  57.     timer_initpara.period            = 3;
  58.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
  59.     timer_initpara.repetitioncounter = 0;
  60.     timer_init(TIMER2, &timer_initpara);

  61.     timer_channel_output_struct_para_init(&timer_ocinitpara);

  62.     timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;
  63.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
  64.     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
  65.     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
  66.     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
  67.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
  68.     timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_ocinitpara);

  69.     timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 2);

  70.     timer_channel_output_mode_config(TIMER2, TIMER_CH_0, TIMER_OC_MODE_PWM0);

  71.     timer_channel_output_shadow_config(TIMER2, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);

  72.     timer_auto_reload_shadow_enable(TIMER2);
  73.     /* 从模式选择:外部时钟模式0 */
  74.     timer_slave_mode_select(TIMER2, TIMER_SLAVE_MODE_EXTERNAL0);
  75.     /* 选择定时器输入触发源:内部触发2(ITI2) */
  76.     timer_input_trigger_source_select(TIMER2, TIMER_SMCFG_TRGSEL_ITI2);
  77.     /* 选择主从模式 */
  78.     timer_master_slave_mode_config(TIMER2, TIMER_MASTER_SLAVE_MODE_ENABLE);
  79.     /* 使用TIMER2更新事件作为触发器输出 */
  80.     timer_master_output_trigger_source_select(TIMER2, TIMER_TRI_OUT_SRC_UPDATE);


  81.     timer_deinit(TIMER0);

  82.     timer_struct_para_init(&timer_initpara);
  83.     timer_initpara.prescaler         = 0;
  84.     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
  85.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
  86.     timer_initpara.period            = 3;
  87.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
  88.     timer_initpara.repetitioncounter = 0;
  89.     timer_init(TIMER0, &timer_initpara);

  90.     timer_channel_output_struct_para_init(&timer_ocinitpara);
  91.    
  92.     timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;
  93.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
  94.     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
  95.     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
  96.     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
  97.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
  98.     timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);

  99.     timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 2);

  100.     timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);

  101.     timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);

  102.     timer_auto_reload_shadow_enable(TIMER0);

  103.     timer_primary_output_config(TIMER0, ENABLE);
  104.     /* 从模式选择:外部时钟模式0 */
  105.     timer_slave_mode_select(TIMER0, TIMER_SLAVE_MODE_EXTERNAL0);
  106.     /* 选择定时器输入触发源:内部触发2(ITI2) */
  107.     timer_input_trigger_source_select(TIMER0, TIMER_SMCFG_TRGSEL_ITI2);

  108.     /* TIMER 使能 */
  109.     timer_enable(TIMER14);
  110.     timer_enable(TIMER2);
  111.     timer_enable(TIMER0);
  112. }
先看现象:
393706215c028b85df.png

黄色为主定时器14,以250Hz持续输出,蓝色为定时器14的从定时器2,紫色为主定时器2的从定时器0.

TIMER14只是普通的PWM输出,不赘述,只是在初始化时要设置为主从模式,另外要设置触发器使用TIMER14的更新事件,这样TIMER2才可以在TIMER14的每四次更新事件触发输出,TIMER对TIMER0同理。

其次,作为从定时器,是以主定时器作为参考,所以在设置预分频值和周期时要参考主定时器的时钟。

722446215c1955c3bb.png

以上图为例,这里预分频值为0,即不对时钟进行分频,并将周期设为3(4-1),这样每四个TIMER14的更新事件便可触发一次TIMER的输出。如果从定时器想改变频率和占空比,修改对应参数即可。这里只是以最简单的方式展示基础的定时器级联。但是需要注意的是定时器的内部级联是有限制的,并不是任意两个定时器都可实现级联,需要参考手册来确定。


定时器的功能太多太多,仅凭一篇帖子肯定说不完,这里仅调出两个进行描述,下面挑出实例进行案例讲解:

恰好论坛有兄弟求助,这里正好借这个问题进行一次实际操作:


39212621719d9d647a.png

这是一个典型的边沿不对齐的4路波形输出,每个单独的波形都可使用定时器的PWM模式或者比较输出模式来实现,但是同时输出就比较麻烦。此时可以考虑使用定时器的多路比较+DMA来实现。


对于比较输出切换模式,简单的理解就是将比较的值和当前计数器的值进行比较,根据比较结果来输出电平的高低,当比较的值和当前计数的值相等,做出电平的切换。



例如发生匹配之前是高电平,我们设置比较值为500,当定时器计数到500时,匹配成功,将输出改为低电平,反之亦然。


回到案例:


我们从这张图的0点开始计算,一格代表100个计数点,每8个点为一个周期,那么我们定时器的周期就设置为800,计数到800后重0开始再次计数。


那么对于A路,刚开始是高电平,计数到200变成低电平,再计数到700为高电平,然后继续计数到800。完成一个周期。那我们就设置该通道的比较事件触发DMA,初始计数值为200,记到200后触发匹配和DMA,将电平拉低,DMA将计数值改为700。循环下去即可。


那么同理对于/A而言,当CCR=300和 CCR=600时发生输出切换。同样开启该通道的比较事件触发DMA传输,实现CCR寄存器的数据循环更新。


对于B路,原理同上,当CCR=100和 CCR=400时发生输出切换。


对于/B,也可采用相同的方式,但是这里为了展示更多的用法,/B可以不采用DMA,因该波形的起始点刚反生电平翻转,这里可以采用PWM1的输出模式,将CCR设置为500。


基于上述分析,我们用代码实现效果:


首先通过宏定义设置TIMER0的通道外设地址(查手册,上文有介绍):


  1. #define TIMER0_CH0CV                    ((uint32_t)0x40012C34)
  2. #define TIMER0_CH1CV                    ((uint32_t)0x40012C38)
  3. #define TIMER0_CH2CV                    ((uint32_t)0x40012C3C)
定义DMA发送的数组
  1. uint16_t buffer[2] = {200,700};
  2. uint16_t buffer1[2] = {600,300};
  3. uint16_t buffer2[2] = {400,100};
DMA初始化(DMA通道对应定时器通道可参考下表):
  1. /************************************************
  2. 函数名称 : timer_dma_config
  3. 功    能 : 初始化DMA
  4. 参    数 : 无
  5. 返 回 值 : 无
  6. 作    者 : 呐咯密密
  7. *************************************************/
  8. void timer_dma_config(void)
  9. {
  10.     dma_parameter_struct dma_init_struct;

  11.     rcu_periph_clock_enable(RCU_DMA);


  12.     dma_deinit(DMA_CH1);

  13.     dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH0CV;//外设地址
  14.     dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
  15.     dma_init_struct.memory_addr  = (uint32_t)buffer;//内存地址
  16.     dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
  17.     dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  18.     dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
  19.     dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
  20.     dma_init_struct.number       = 2;
  21.     dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
  22.     dma_init( DMA_CH1, &dma_init_struct);
  23.     dma_circulation_enable(DMA_CH1);
  24.     dma_channel_enable(DMA_CH1);
  25.         

  26.     dma_deinit(DMA_CH2);
  27.     dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH1CV;
  28.     dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
  29.     dma_init_struct.memory_addr  = (uint32_t)buffer1;
  30.     dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
  31.     dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  32.     dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
  33.     dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
  34.     dma_init_struct.number       = 2;
  35.     dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
  36.     dma_init( DMA_CH2, &dma_init_struct);
  37.     dma_circulation_enable(DMA_CH2);
  38.     dma_channel_enable(DMA_CH2);

  39.         

  40.     dma_deinit(DMA_CH4);
  41.     dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH2CV;
  42.     dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
  43.     dma_init_struct.memory_addr  = (uint32_t)buffer2;
  44.     dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
  45.     dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  46.     dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
  47.     dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
  48.     dma_init_struct.number       = 2;
  49.     dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
  50.     dma_init( DMA_CH4, &dma_init_struct);
  51.     dma_circulation_enable(DMA_CH4);
  52.     dma_channel_enable(DMA_CH4);
  53. }


98506217207cb2f6d.png

初始化定时器0,开启通道和DMA:


  1. /************************************************
  2. 函数名称 : timer_config
  3. 功    能 : 初始化TIMER0
  4. 参    数 : 无
  5. 返 回 值 : 无
  6. 作    者 : 呐咯密密
  7. *************************************************/
  8. void timer_config(void)
  9. {
  10.     rcu_periph_clock_enable(RCU_GPIOA);

  11.     /*configure PA8(TIMER0 CH0) as alternate function*/
  12.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
  13.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8);
  14.     gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_8);

  15.     /*configure PA9(TIMER0 CH1) as alternate function*/
  16.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
  17.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);
  18.     gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_9);

  19.     /*configure PA10(TIMER0 CH2) as alternate function*/
  20.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
  21.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_10);
  22.     gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_10);

  23.     /*configure PA11(TIMER0 CH3) as alternate function*/
  24.     gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
  25.     gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_11);
  26.     gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_11);        
  27. /**********************************************************************************/        
  28.      /* 结构体 */
  29.     timer_oc_parameter_struct timer_ocinitpara;
  30.     timer_parameter_struct timer_initpara;
  31.   
  32.     rcu_periph_clock_enable(RCU_TIMER0);

  33.     timer_deinit(TIMER0);

  34.     timer_struct_para_init(&timer_initpara);

  35.     timer_initpara.prescaler         = 71;
  36.     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
  37.     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
  38.     timer_initpara.period            = 799;
  39.     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
  40.     timer_initpara.repetitioncounter = 1;
  41.     timer_init(TIMER0, &timer_initpara);


  42.     timer_channel_output_struct_para_init(&timer_ocinitpara);

  43.     timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;
  44.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
  45.     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
  46.     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
  47.     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_HIGH;
  48.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
  49.     timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);
  50.     timer_channel_output_config(TIMER0, TIMER_CH_1, &timer_ocinitpara);
  51.     timer_channel_output_config(TIMER0, TIMER_CH_2, &timer_ocinitpara);
  52.     timer_channel_output_config(TIMER0, TIMER_CH_3, &timer_ocinitpara);

  53.     /* 通道0 */
  54.     timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, buffer[0]);
  55.     /* 设置为匹配时翻转 */
  56.     timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_TOGGLE);
  57.   
  58.     timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);

  59.     /* 通道1 */
  60.     timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, buffer1[0]);
  61.     /* 设置为匹配时翻转 */
  62.     timer_channel_output_mode_config(TIMER0, TIMER_CH_1, TIMER_OC_MODE_TOGGLE);
  63.    
  64.     timer_channel_output_shadow_config(TIMER0, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);

  65.         /* 通道2 */
  66.     timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_2, buffer2[0]);
  67.     /* 设置为匹配时翻转 */
  68.     timer_channel_output_mode_config(TIMER0, TIMER_CH_2, TIMER_OC_MODE_TOGGLE);
  69.    
  70.     timer_channel_output_shadow_config(TIMER0, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);

  71.     /* 通道3 */
  72.     timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_3, 500);
  73.     /* 设置为PWM1输出 */
  74.     timer_channel_output_mode_config(TIMER0, TIMER_CH_3, TIMER_OC_MODE_PWM1);

  75.     timer_channel_output_shadow_config(TIMER0, TIMER_CH_3, TIMER_OC_SHADOW_DISABLE);
  76.    
  77.    /* TIMER0主输出使能 */
  78.     timer_primary_output_config(TIMER0, ENABLE);
  79.         
  80.     /* TIMER0更新DMA请求启用 */
  81.     timer_dma_enable(TIMER0, TIMER_DMA_CH0D);
  82.     timer_dma_enable(TIMER0, TIMER_DMA_CH1D);
  83.     timer_dma_enable(TIMER0, TIMER_DMA_CH2D);
  84. //    timer_dma_enable(TIMER0, TIMER_DMA_CH3D);        
  85.     /* 使能自动重装载 */
  86.     timer_auto_reload_shadow_enable(TIMER0);

  87.     timer_enable(TIMER0);
  88. }
试验效果:
28385621722184b312.png

这里波形有点瑕疵,是因为我没有裸板,在之前的板子上测试的,PA8,PA9,PA10外接了RS485电路,该板子还在使用,电路不好破坏,不过不影响试验结果。可以看出来是符合我们的需求的。



此次试验未使用到定时器的级联,解决问题的关键在于对问题本质的分析,对定时器各个功能的熟练掌握,介于时间和篇幅关系,这里就不介绍定时器的所有功能。如有问题,可发帖或跟帖提问,看到会尽量处理,如不好处理我会酌情继续发帖解决。


关于级联的实际应用可参考上一篇**:开工没有回头箭--记开工的问题与解决





结语:
该**为本人学习总结,文中所述知识点可能会出现错误和遗漏,欢迎大家批评指出。




打赏榜单

21小跑堂 打赏了 100.00 元 2022-03-09
理由:恭喜通过原创奖文章审核!请多多加油哦!

评论

学习了,写的很具体  发表于 2022-3-7 12:32

评分

参与人数 1威望 +1 收起 理由
moticsoft + 1

查看全部评分

可怜的小弗朗士 发表于 2022-3-2 09:39 | 显示全部楼层
定时器的用法确实很多,用法也是灵活多变,感谢楼主分享经验。
shiyimei 发表于 2022-3-2 14:38 | 显示全部楼层
写的很具体,感谢分享
match007 发表于 2022-3-2 14:40 | 显示全部楼层
大神a,有些分析别说做了,看都有点费劲
主要是我的应用简单
lzbf 发表于 2022-3-4 14:03 | 显示全部楼层
做个一个精准延时。
soodesyt 发表于 2022-3-4 14:08 | 显示全部楼层
      
eefas 发表于 2022-3-4 14:12 | 显示全部楼层
GD32定时器有几个?
努力反击 发表于 2022-3-9 10:25 | 显示全部楼层
感谢大佬分享
凡人8000 发表于 2022-3-14 20:38 | 显示全部楼层
非常清楚,非常好!
天命风流 发表于 2022-3-14 21:09 | 显示全部楼层
非常好                              
cisco777 发表于 2023-4-3 10:28 | 显示全部楼层
为什么我用GDF427试了案例代码,反转不了呢?
1048151145 发表于 2024-10-29 14:47 | 显示全部楼层
最后一个实例,如果想要实现占空比的改变该如何做呢?我现在是在定时器中断中去更新数组数据,但是实际波形出来并不对
yangjiaxu 发表于 2024-10-29 15:23 | 显示全部楼层
GD 小密玩的确实太专业了奥,不过GD的芯片之前优势还在,现在的优势好像不算是性价比的了吧
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

568

主题

4085

帖子

56

粉丝
快速回复 在线客服 返回列表 返回顶部