当前位置: 首页 > news >正文

基于GD32的定时器不完全详解--定时、级联

SysTick 定时器

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

TIMER定时器

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

从最常用的PWM说起。

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

void timer_config(void)
{/* 使能 GPIOA 时钟 */rcu_periph_clock_enable(RCU_GPIOA);/* 使能 GPIOAB 时钟 */rcu_periph_clock_enable(RCU_GPIOB);/*初始化PWM输出引脚 PB0(TIMER2 CH2) */gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0);gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_0);/*初始化PWM输出引脚 PA7(TIMER2 CH1) */gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_7);gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_7);/*初始化PWM输出引脚 PA6(TIMER2 CH0) */gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);/* 结构体 */timer_oc_parameter_struct timer_ocinitpara;timer_parameter_struct timer_initpara;/* 使能定时器时钟 */rcu_periph_clock_enable(RCU_TIMER2);timer_deinit(TIMER2);/* 初始化TIMER相关结构体参数 */timer_struct_para_init(&timer_initpara);/* TIMER2 初始化 */timer_initpara.prescaler         = 71;                //预分频值timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;//对其模式timer_initpara.counterdirection  = TIMER_COUNTER_UP;  //计数方向timer_initpara.period            = 15999;             //周期timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;  //时钟分频因子timer_initpara.repetitioncounter = 0;                 //重复计数值timer_init(TIMER2, &timer_initpara);/* 初始化定时器通道输出参数结构 */timer_channel_output_struct_para_init(&timer_ocinitpara);/* 配置定时器通道输出功能 */timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;                   //通道输出状态timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;                 //互补通道输出状态timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;             //通道输出极性timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;                        //互补通道输出极性timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;                        //空闲状态下通道输出timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;           //空闲状态先互补通道输出极性timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_ocinitpara);timer_channel_output_config(TIMER2, TIMER_CH_1, &timer_ocinitpara);timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocinitpara);/* CH0 configuration in PWM mode0, duty cycle 25% */timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 4000);//设置通道比较值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);//配置TIMER通道输出比较影子寄存器功能/* CH1 configuration in PWM mode0, duty cycle 50% */timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, 8000);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);/* CH2 configuration in PWM mode0, duty cycle 75% */timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 12000);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);timer_auto_reload_shadow_enable(TIMER2);//自动重载影子使能/* TIMER2 使能 */timer_enable(TIMER2);}

首先要明白定时器的输出的引脚并不是随便定义的,具体可参照手册来确定:
在这里插入图片描述
其次在使用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%**
在这里插入图片描述
如果我们需要动态调节频率和占空比只需调用以下函数:
设置预分频值:
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初始化:

void timer_dma_config(void)
{dma_parameter_struct dma_init_struct;/* enable DMA clock */rcu_periph_clock_enable(RCU_DMA);/* initialize DMA channel4 */dma_deinit(DMA_CH2);/* DMA channel4 initialize */dma_init_struct.periph_addr  = (uint32_t)TIMER2_CH0CV;dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.memory_addr  = (uint32_t)buffer;dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;dma_init_struct.number       = 3;dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;dma_init( DMA_CH2, &dma_init_struct);/* enable DMA circulation mode */dma_circulation_enable(DMA_CH2);/* enable DMA channel4 */dma_channel_enable(DMA_CH2);
}

将之前的通道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的基地址:
在这里插入图片描述
再找到通道0的偏移地址:
在这里插入图片描述
两个地址相加得到TIMER2_CH0CV。

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

对于DMA通道的选择可以注意一下:
在这里插入图片描述
选择的是TIMER2_UP,而不是TIMER_CH0!!!
示波器采样如下:
在这里插入图片描述

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

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

三个相互级联的PWM输出:

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

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

代码实现:

void timer_config(void)
{rcu_periph_clock_enable(RCU_GPIOA);/*PA2(TIMER14 CH0)*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_2);/*PA6(TIMER2 CH0)*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);/*PA8(TIMER0 CH0)*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8);gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_8);timer_oc_parameter_struct timer_ocinitpara;timer_parameter_struct timer_initpara;rcu_periph_clock_enable(RCU_TIMER0);rcu_periph_clock_enable(RCU_TIMER14);rcu_periph_clock_enable(RCU_TIMER2);timer_deinit(TIMER14);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 71;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 3999;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER14, &timer_initpara);timer_channel_output_struct_para_init(&timer_ocinitpara);timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;timer_channel_output_config(TIMER14, TIMER_CH_0, &timer_ocinitpara);timer_channel_output_pulse_value_config(TIMER14, TIMER_CH_0, 2000);timer_channel_output_mode_config(TIMER14, TIMER_CH_0, TIMER_OC_MODE_PWM0);timer_channel_output_shadow_config(TIMER14, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);timer_auto_reload_shadow_enable(TIMER14);/* 选择主从模式 */timer_master_slave_mode_config(TIMER14, TIMER_MASTER_SLAVE_MODE_ENABLE);/* 触发器输出使用TIMER14更新事件 */timer_master_output_trigger_source_select(TIMER14, TIMER_TRI_OUT_SRC_UPDATE);timer_primary_output_config(TIMER14, ENABLE);timer_deinit(TIMER2);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 0;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 3;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER2, &timer_initpara);timer_channel_output_struct_para_init(&timer_ocinitpara);timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_ocinitpara);timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 2);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);timer_auto_reload_shadow_enable(TIMER2);/* 从模式选择:外部时钟模式0 */timer_slave_mode_select(TIMER2, TIMER_SLAVE_MODE_EXTERNAL0);/* 选择定时器输入触发源:内部触发2(ITI2) */timer_input_trigger_source_select(TIMER2, TIMER_SMCFG_TRGSEL_ITI2);/* 选择主从模式 */timer_master_slave_mode_config(TIMER2, TIMER_MASTER_SLAVE_MODE_ENABLE);/* 使用TIMER2更新事件作为触发器输出 */timer_master_output_trigger_source_select(TIMER2, TIMER_TRI_OUT_SRC_UPDATE);timer_deinit(TIMER0);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 0;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 3;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER0, &timer_initpara);timer_channel_output_struct_para_init(&timer_ocinitpara);timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 2);timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);timer_auto_reload_shadow_enable(TIMER0);timer_primary_output_config(TIMER0, ENABLE);/* 从模式选择:外部时钟模式0 */timer_slave_mode_select(TIMER0, TIMER_SLAVE_MODE_EXTERNAL0);/* 选择定时器输入触发源:内部触发2(ITI2) */timer_input_trigger_source_select(TIMER0, TIMER_SMCFG_TRGSEL_ITI2);/* TIMER 使能 */timer_enable(TIMER14);timer_enable(TIMER2);timer_enable(TIMER0);
}

先看现象:
在这里插入图片描述
黄色为主定时器14,以250Hz持续输出,蓝色为定时器14的从定时器2,紫色为主定时器2的从定时器0.

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

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

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

恰好有兄弟求助,这里正好借这个问题进行一次实际操作:
在这里插入图片描述
这是一个典型的边沿不对齐的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的通道外设地址(查手册,上文有介绍):

#define TIMER0_CH0CV                    ((uint32_t)0x40012C34)
#define TIMER0_CH1CV                    ((uint32_t)0x40012C38)
#define TIMER0_CH2CV                    ((uint32_t)0x40012C3C)

定义DMA发送的数组

uint16_t buffer[2] = {200,700};
uint16_t buffer1[2] = {600,300};
uint16_t buffer2[2] = {400,100};

DMA初始化(DMA通道对应定时器通道可参考下表):

/************************************************
函数名称 : timer_dma_config
功    能 : 初始化DMA
参    数 : 无
返 回 值 : 无
作    者 : 呐咯密密
*************************************************/
void timer_dma_config(void)
{dma_parameter_struct dma_init_struct;rcu_periph_clock_enable(RCU_DMA);dma_deinit(DMA_CH1);dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH0CV;//外设地址dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.memory_addr  = (uint32_t)buffer;//内存地址dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;dma_init_struct.number       = 2;dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;dma_init( DMA_CH1, &dma_init_struct);dma_circulation_enable(DMA_CH1);dma_channel_enable(DMA_CH1);dma_deinit(DMA_CH2);dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH1CV;dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.memory_addr  = (uint32_t)buffer1;dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;dma_init_struct.number       = 2;dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;dma_init( DMA_CH2, &dma_init_struct);dma_circulation_enable(DMA_CH2);dma_channel_enable(DMA_CH2);dma_deinit(DMA_CH4);dma_init_struct.periph_addr  = (uint32_t)TIMER0_CH2CV;dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.memory_addr  = (uint32_t)buffer2;dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;dma_init_struct.number       = 2;dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;dma_init( DMA_CH4, &dma_init_struct);dma_circulation_enable(DMA_CH4);dma_channel_enable(DMA_CH4);
}

在这里插入图片描述
初始化定时器0,开启通道和DMA:

/************************************************
函数名称 : timer_config
功    能 : 初始化TIMER0
参    数 : 无
返 回 值 : 无
作    者 : 呐咯密密
*************************************************/
void timer_config(void)
{rcu_periph_clock_enable(RCU_GPIOA);/*configure PA8(TIMER0 CH0) as alternate function*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8);gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_8);/*configure PA9(TIMER0 CH1) as alternate function*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_9);/*configure PA10(TIMER0 CH2) as alternate function*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_10);gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_10);/*configure PA11(TIMER0 CH3) as alternate function*/gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_11);gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_11);        
/**********************************************************************************/        /* 结构体 */timer_oc_parameter_struct timer_ocinitpara;timer_parameter_struct timer_initpara;rcu_periph_clock_enable(RCU_TIMER0);timer_deinit(TIMER0);timer_struct_para_init(&timer_initpara);timer_initpara.prescaler         = 71;timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period            = 799;timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 1;timer_init(TIMER0, &timer_initpara);timer_channel_output_struct_para_init(&timer_ocinitpara);timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_HIGH;timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);timer_channel_output_config(TIMER0, TIMER_CH_1, &timer_ocinitpara);timer_channel_output_config(TIMER0, TIMER_CH_2, &timer_ocinitpara);timer_channel_output_config(TIMER0, TIMER_CH_3, &timer_ocinitpara);/* 通道0 */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, buffer[0]);/* 设置为匹配时翻转 */timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_TOGGLE);timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);/* 通道1 */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, buffer1[0]);/* 设置为匹配时翻转 */timer_channel_output_mode_config(TIMER0, TIMER_CH_1, TIMER_OC_MODE_TOGGLE);timer_channel_output_shadow_config(TIMER0, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);/* 通道2 */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_2, buffer2[0]);/* 设置为匹配时翻转 */timer_channel_output_mode_config(TIMER0, TIMER_CH_2, TIMER_OC_MODE_TOGGLE);timer_channel_output_shadow_config(TIMER0, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);/* 通道3 */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_3, 500);/* 设置为PWM1输出 */timer_channel_output_mode_config(TIMER0, TIMER_CH_3, TIMER_OC_MODE_PWM1);timer_channel_output_shadow_config(TIMER0, TIMER_CH_3, TIMER_OC_SHADOW_DISABLE);/* TIMER0主输出使能 */timer_primary_output_config(TIMER0, ENABLE);/* TIMER0更新DMA请求启用 */timer_dma_enable(TIMER0, TIMER_DMA_CH0D);timer_dma_enable(TIMER0, TIMER_DMA_CH1D);timer_dma_enable(TIMER0, TIMER_DMA_CH2D);
//    timer_dma_enable(TIMER0, TIMER_DMA_CH3D);        /* 使能自动重装载 */timer_auto_reload_shadow_enable(TIMER0);timer_enable(TIMER0);
}

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

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

相关文章:

基于GD32的定时器不完全详解--定时、级联

SysTick 定时器 SysTick 是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器中的使能位清除, 就永不停息。 该定时器的介绍在MCU的手册中一般不会介绍,因为…...

Clion开发STM32之ESP8266系列(四)

前言 上一篇: Clion开发STM32之ESP8266系列(三) 本篇主要内容 实现esp8266需要实现的函数串口3中断函数的自定义(这里没有使用HAL提供的)封装esp8266服务端的代码和测试 正文 主要修改部分 核心配置头文件(添加一些宏定义) sys_core_conf.h文件中…...

降本增效,StarRocks 在同程旅行的实践

作者:周涛 同程旅行数据中心大数据研发工程师 同程旅行是中国在线旅游行业的创新者和市场领导者。作为一家一站式平台,同程旅行致力于满足用户旅游需求,秉持 "让旅行更简单、更快乐" 的使命,主要通过包括微信小程序、AP…...

INTP型人格适合选择哪些专业?

INTP人格内倾理性人格、具有强烈的好奇心、创造性和独立性的特点。他们善于独立思考和寻找问题的本质,并对抽象的想法和理论感兴趣。 INTP人格的人具有很强的逻辑思维和分析能力,他们的思维方式非常系统,追求完美和准确。因此他们适合选择需…...

【LeetCode热题100】打卡第16天:组合总和

文章目录 组合总和⛅前言🔒题目🔑题解 组合总和 ⛅前言 大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏! 精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数…...

tinkerCAD案例:1.戒子环

基本戒指 在本课中,您将学习使用圆柱形状制作戒指。来吧! 说明 将圆柱体拖动到工作平面上并使其成为孔。 圆柱体应缩放以适合其制造手指。 在本例中,我们将使用 17mm 作为直径,但请根据您的需要随意调整尺寸。 将“圆柱”形状拖…...

RPC接口测试技术-Tcp 协议的接口测试

【摘要】 首先明确 Tcp 的概念,针对 Tcp 协议进行接口测试,是指基于 Tcp 协议的上层协议比如 Http ,串口,网口, Socket 等。这些协议与 Http 测试方法类似(具体查看接口自动化测试章节)&#xf…...

MyBatis Plus基本用法-SpringBoot框架

依赖 使用 Mybatis Plus 框架时&#xff0c;需要添加以下依赖&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>latest-version</version> </dependency…...

指针--指针变量的定义和初始化

存放变量的地址需要一种特殊类型的变量&#xff0c;这种特殊的数据类型就是指针&#xff08;Pointer&#xff09;。 具有指针类型的变量&#xff0c;称为指针变量&#xff0c;它时专门用于存储变量的地址值和变量。 其定义形式如下&#xff1a; 类型关键字 * 指针变量名&#x…...

Web基本概念

一、前言 World Wide Web的简称&#xff0c;是一个由许多互相链接的超文本组成的系统&#xff0c;通过互联网访问 &#xff08;为用户提供信息&#xff09; 静态网页 仅适用于不能经常更改内容的网页&#xff1b; 动态网页 网络编程技术创建的页面&#xff1b;通过在传统的静态…...

Niagara—— Texture Sample 与 Particle Subuv 区别

目录 一&#xff0c;Texture Sample 二&#xff0c;Particle Subuv 一&#xff0c;Texture Sample 此节点是最基本的采样节点&#xff0c;依据UV坐标来采样Texture&#xff1b; MipValueMode&#xff0c;设置采样的Mipmap Level&#xff1b; None&#xff0c;根据当前Texture…...

如何在食品行业运用IPD?

食品是我国重要的民生产业之一&#xff0c;是保障和满足人民群众不断增长消费需求的重要支撑。食品指各种供人食用或者饮用的成品和原料以及按照传统既是食品又是药品的物品&#xff0c;包括加工食品&#xff0c;半成品和未加工食品&#xff0c;不包括烟草或只作药品用的物质。…...

如何用pandas进行条件分组计算?

Pandas提供了强大的分组聚合功能&#xff0c;可以轻松进行条件分组计算和统计。本文通过一个例子&#xff0c;展示如何使用Pandas的.groupby()和.agg()方法进行条件分组计算。 准备数据 假设有这样一个字典数据: dict { 姓名: [张三&#xff0c;李四&#xff0c;王五&#x…...

tomcat如何调优,涉及哪些参数?

Tomcat是一个流行的开源Java Servlet容器&#xff0c;用于部署和管理Java Web应用程序。调优Tomcat可以提高性能、并发处理能力和稳定性。以下是一些常见的Tomcat调优参数和技巧&#xff1a; 1.调整内存参数&#xff1a; -Xms&#xff1a;指定Tomcat启动时的初始堆内存大小。 -…...

java培训机构学校教学教务选课管理平台springboot+vue

近年来&#xff0c;随着培训机构机构规模的逐渐增大&#xff0c;人工书写的方式已经不能满足如此庞大的数据。为了更好的适应信息时代的高效性&#xff0c;一个利用计算机来实现培训机构教务管理工作的系统将必然诞生。基于这一点&#xff0c;设计了一个培训机构教务管理系统&a…...

半导体(TSS)放电管的两大选购注意事项及选型小策略

固体放电管&#xff0c;是以半导体工艺制作而成的&#xff0c;因此我们也称为半导体&#xff08;TSS&#xff09;放电管&#xff0c;它常在电路中并联使用&#xff0c;具备伏安特性。 TSS放电管在电路中类似开关&#xff0c;在正常工作时不动作&#xff0c;但一般被保护电路受到…...

05-使用Vue3 + Vue CLI 实现前端模块的搭建

1、环境准备 流程:安装node得到npm,使用npm安装vue cli(脚手架),使用vue cli创建项目。 Vue CLI版本和Node版本有关,用Node V12只能下载到Vue CLI V4.X,必须用Node V18才能下载到Vue CLI V5.X IDEA支持配置多个版本的Node,类似配置多个JDK。 node.js安装 1、官网下载…...

3.1 增加多进程执行playwright

增加了多进程的方式执行测试代码&#xff0c;对代码改动比较大 1、case case目录依然是自动生成 2、config dir_collection.py新增了配置 mkdir_collections [case,log,img, ] del_collections [results,report ] del_regex temp3、data/img/log/resource/video data/im…...

关于单片机的时钟浅谈及STM32F103/F030单片机的内外时钟切换问题

绪论 本文主要讲解单片机的时钟系统的相关知识&#xff0c;并进行超频测试&#xff0c;同时介绍如何在STM32F0单片机上进行内外时钟的切换&#xff0c;在不使用外部晶振或者外部晶振不启动时自动切换内部时钟的方法。 一、杂谈 问题来源于群里的一次问答&#xff1a; 诚然&…...

centos6.10环境下安装php7.4(基于WLNMP包)

centos6系统已经被官网停止维护&#xff0c;要安装软件必须用第三方的RPM包&#xff0c;下面使用yum安装php7.4正式版&#xff0c;当前基于WLNMP提供的一键安装包来安装 1、添加epel源 yum install epel-release yum install epel-release 2、添加WLNMP一键安装包源 rpm -iv…...

Qt使用第三方库openssl进行RSA加密解密操作详解

一、openssl库的编译,可以参考文档: https://blog.csdn.net/liang19890820/article/details/51658574/ 因为我这里使用的是windows操作系统,可以直接下载exe格式的安装文件,直接安装即可,就包含了我们需要的头文件和库文件,省去了编译操作。exe安装文件下载地址: htt…...

激发数学思维:GPT-4实证研究探索挑战性数学问题

深度学习自然语言处理 原创作者&#xff1a;wkk 考虑到自然语言在许多科学和工程领域表达的数学问题的丰富性&#xff0c;使用大语言模型(LLM)来解决数学问题是一项有趣的研究工作。今天给大家介绍一篇微软研究院联合欧美高校关于如何使用GPT-4解决数学问题的研究论文。 之前的…...

如何配置IP地址

一.自动获取IP 1.dhclient 2.ifconfig 通过这个命令可以查看系统有几块网卡和网卡的IP。 如果您的Linux有多块网卡&#xff0c;那么在Linux中它会显示成eth1, eth2 依此类推 二.手动配置IP 如果您的虚拟机不能自动获取IP&#xff0c;那么只能手动配置&#xff0c;配置方法为&am…...

CentOS + Nginx 环境自动申请和部署Let‘s Encrypt免费SSL证书教程

文章目录 步骤 1&#xff1a;安装Certbot工具步骤 2&#xff1a;配置Nginx服务器步骤 3&#xff1a;生成SSL证书步骤 4&#xff1a;配置Nginx以使用SSL证书步骤 5&#xff1a;重新加载Nginx配置步骤 6&#xff1a;自动续期证书 本文介绍如何在 CentOS Nginx 环境下&#xff0c…...

浅谈对BI工具价值的看法

浅谈对BI工具价值的看法 BI的定义看法 百度百科的定义&#xff1a; 商业智能&#xff08;Business Intelligence&#xff0c;简称&#xff1a;BI&#xff09;&#xff0c;又称商业智慧或商务智能&#xff0c;指用现代数据仓库技术、线上分析处理技术、数据挖掘和数据展现技术…...

创建定时任务

import schedule import timedef task():print("Im working...")if __name__ __main__:schedule.every(10).seconds.do(task) # 每10秒一次schedule.every(10).minutes.do(task) # 10分钟一次schedule.every().hour.do(task) # 每小时schedule.every().day.at(&q…...

MyBatis的使用、Spring AOP、Spring事务

一、MyBatis 的使用 1、环境配置 1.1、建库建表 -- 创建数据库 drop database if exists mycnblog; create database mycnblog DEFAULT CHARACTER SET utf8mb4;-- 使⽤数据数据 use mycnblog;-- 创建表[⽤户表] drop table if exists userinfo; create table userinfo(id in…...

Apache Doris 冷热分层技术如何实现存储成本降低 70%?

在数据分析的实际场景中&#xff0c;冷热数据往往面临着不同的查询频次及响应速度要求。例如在电商订单场景中&#xff0c;用户经常访问近 6 个月的订单&#xff0c;时间较久远的订单访问次数非常少&#xff1b;在行为分析场景中&#xff0c;需支持近期流量数据的高频查询且时效…...

MySQL 两个备机同时挂掉故障分析

来源&#xff1a; 接报线上出现两个5.7.38的备库同时crash&#xff0c;crash堆栈相同&#xff0c;内容如下&#xff1a; stack_bottom 7fd7700b0d30 thread_stack 0x40000 /home/service/app/mysql33066/bin/mysqld(my_print_stacktrace0x2c)[0xf1062c] /home/service/app/m…...

序列化与反序列化深入理解

序列化与反序列化深入理解 1 介绍1.1 概述1.2 序列化实现的需求 2 常用序列化实现函数序列化语言内置开源序列化实现 3 各序列化实现比较4 各序列化实现概述XMLJSONProtobufJava 内置TLVVLE&#xff08;Variable Length Encoding&#xff09; 5 flex & bison5.1 介绍应用解…...

dede网站地图不显示文章列表/好看的网页设计作品

为什么80%的码农都做不了架构师&#xff1f;>>> 通常在按完mysql时&#xff0c;root密码默认为空&#xff0c;而且只有localhost的登录权限 1、修改root密码 mysqladmin -uroot -p password mylove! 这了会提示输入原始密码&#xff0c;由于原始密码为空&#xf…...

信息技术做网站/郑州网站营销推广公司

...

南和企业做网站/站长综合查询工具

ISIS将网络层又分了两个层&#xff1a; 子网独立子层&#xff08;subnetwork independent sublayer&#xff09;&#xff08;为传输层提供统一服务&#xff09;&#xff1b; 子网依赖子层&#xff08;subnetwork dependent sublayer&#xff09;&#xff08;存取数据链路层提供…...

c 做网站流程/盐酸达泊西汀片是治疗什么的药物

摘要这个POC用于在不知道明文密码的情况下对启用了密码安全认证插件(默认开启插件&#xff1a;mysql_native_password)的MYSQL数据库进行登录。前提条件为&#xff1a;1.为了获取到已知用户的hash&#xff0c;我们需要读取到目标数据库中的mysql.user表。2.能够拦截到上述已知用…...

南京响应式网站制作/网络营销推广论文

if True: x 15 print(x)print(x) # 可见 if 语句&#xff0c;不是一个代码块&#xff0c;因为代码块有独立的作用域&#xff0c;代码块结束时&#xff0c;会释放变量l1 [1,2,3,4]print(id(l1))l2 [1]print(id(l2))def func4(li): return li[:2] # 如果能够切片…...

网站开发流程的认识/百度售后客服电话24小时

来自&#xff1a;程序员书库(ID&#xff1a;CodingBook)毋庸置疑&#xff0c;Python是2019年最流行的编程语言之一&#xff0c;前几日&#xff0c;HackerRank 发布了 2020 年《开发者技能报告》&#xff0c;报告调查了来自全球 162 个国家的 116000 多名软件开发者。根据调查显…...