概述
在GD32中定时器是非常重要的外设,它可以帮我们精准的控制程序的调度,就如之前讲过的SysTick就是一个定时器,我们可以通过设置这个定时器的寄存器实现延时函数。
GD32的定时器可大致分为3种——基本定时器、通用定时器、高级定时器。
它们之间的区别如下图所示:
这一节先介绍当中最简单的基本定时器。
简介
GD32中的定时器外设都是使用16位计时器;计数模式只有向上计数;支持单脉冲模式;支持DMA传输请求;在内部有触发线连接至DAC,这样可以使用定时器定时触发DAC的数据转换,这个在后面会讲到。
时钟设置
要想定时器按预期工作,那么对时钟进行正确的设置是十分重要的。先找到对应的时钟树框图。
首先,基本定时器挂载在APB1总线上,APB1总线的最高频率为54MHz(GD32F103C8T6),那么是不是进入定时器的频率就是这个呢?不是!不是!不是!(重要事情说3遍)
由时钟树中可看到,从AHB总线上过来的频率经过APB1分频器变成CK_APB1,但还需要经过一个分频器,才能变成定时器的时钟(CK_TIMER);这个分频器非常关键,因为它是带条件的,还是用户不可修改的。根据注释,当APB1分频器的分频系数为1时,该分频器的分频系数为1(不分频),否则倍频2倍。因为CK_APB1的频率为54MHz,所以APB1分频器的分频系数自然为2。当然我们也可以在固件库中找到答案,在system_gd32f10x.c文件中,我们可以找到以下这几行代码。
从247行看到,在系统时钟初始化时,对APB1分频器进行了2分频的设置。
因为APB1分频器的分频系数为2,所以定时器信号分频器会倍频2倍,所以频率最终会跟AHB总线是一样的108MHz。
对于挂载到APB2的定时器,它们的分频器也是这个规则。
基本定时器只有在高密度或以上的产品才搭载
很明显,从APB1总线上进来的时钟频率较快,因此定时器外设给了一个预分频器,预分频器寄存器是16位的,因此可以实现1-65536分频,分频后的时钟频率才是定时器计数的频率。
预分频器的工作其实比较好理解,例如我们的预分频是2分频,那么定时器等待2个CK_TIMER时钟周期才让定时器计数1次,以此类推。
下图是预分频系数从1变为2的时序图。
其中TIMER_CK是定时器的频率,PSC_CLK是预分频之后的频率。
计数模式
基本定时器只支持向上计数这一种模式。计数器从0开始向上连续计数到自动加载值,一旦计数器计数到自动加载值,会重新从0开始向上计数并产生上溢事件。
下面是一个向上计数的时序图。
上面的PSC值代表不同的预分频系数,PSC等于0和1分别表示1和2分频。
由上图可以看出当寄存器里的值达到了自动加载值后,会产生更新事件标志位和更新中断标志位会置1(若我们开启了对应的中断),不同的是更新事件标志位在置1后会很快被硬件置0,但更新中断标志位则需要用户进行软件清除,这在代码中是要注意的。
例程
现象:LED灯以1秒的间隔闪烁。
timer.c文件
#include "timer.h"
static __IO uint32_t timer_val = 0;
void TIM_Init(void)
{
timer_parameter_struct tim_struct = {0};
rcu_periph_clock_enable(RCU_TIMER1);
tim_struct.counterdirection = TIMER_COUNTER_UP; // 向上计数
tim_struct.prescaler = (108 - 1); // 预分频:108MHz / 108 = 1MHz
tim_struct.period = (1000 - 1); // 周期:1000 / 1MHz = 1ms
timer_init(TIMER1, &tim_struct);
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
nvic_irq_enable(TIMER1_IRQn, 1, 1);
timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_UP);
timer_interrupt_enable(TIMER1, TIMER_INT_UP);
timer_enable(TIMER1);
}
void TIM_DelayMs(__IO uint32_t ms)
{
timer_val = ms;
while(timer_val);
}
void TIM_ValDecrese(void)
{
if(timer_val) --timer_val;
}
gd32f10x_it.c文件文章来源:https://www.toymoban.com/news/detail-797434.html
void TIMER1_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER1, TIMER_INT_FLAG_UP)){
timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_UP);
TIM_ValDecrese();
}
}
总体的设计跟SysTick是差不多的,但SysTick没有预分频器,定时的配置没有定时器要灵活。我们用定时器实现的是一个1ms的延时,预分频系数为107,把频率定成1MHz,这样重装载值为999就可以实现1ms的定时。文章来源地址https://www.toymoban.com/news/detail-797434.html
到了这里,关于【GD32】从0开始学GD32单片机(10)—— TIMER基本定时器详解+1毫秒延时例程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!