GD32F4(7):GD32F4定时器使用

GD32F4定时器:利用溢出中断实现定时

1. 系统环境

  • 系统:win10
  • IDE: Keil5
  • 开发板:GD32官方开发板GD32F427R-START
  • GD库版本:GD32F4xx_Firmware_Library_V3.0.0

2. 定时器描述

对于任何的外设,在使用的时候看一下时钟树都是重要的,因此对于定时器的使用,先看gd32f4的时钟树对定时器的描述如下:
在这里插入图片描述

从时钟树我们可知,定时器1、2、3、4、5、6、11、12、13是在APB1总线下面,定时器0、7、8、9、10是在APB2总线下面。

同时在时钟树下面有这样一段关于time的描述:

TIMER时钟由AHB时钟分频获得,它的频率可以等于CK_APBx、CK_APBx的两倍或CK_APBx的四倍。
详细信息请参考RCU_CFG1寄存器的TIMERSEL位。

既然如此,我们就看看RCU_CFG1寄存器的TIMERSEL位的描述:
在这里插入图片描述
在这里插入图片描述

可能你看到这一下就懵了,没关系我们先写定时器程序,边写边过来分析,就会恍然大悟。

3. 定时器程序编写

下面我们实现一个定时器溢出中断,定时器虽然有高级、普通之分,但是那是其它功能(如PWM),对于定时器溢出中断可以说没区别。因此我们就以定时器1为例来写一个定时器最简单的初始化。

void timer_init(void)
{
   
	timer_parameter_struct timer_initpara; //定时器结构体 
	rcu_periph_clock_enable(RCU_TIMER1); //开启定时器时钟
    
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);//配置定时器时钟,这个后面重点说
    
	timer_struct_para_init(&timer_initpara);//将定时器结构体内参数配置成默认参数
    timer_deinit(TIMER1); //复位定时器
    
	//配置TIMER1,时钟为200M/200/1000 = 1k
    timer_initpara.prescaler         = 200-1;//预分频
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE; //边缘对齐
    timer_initpara.counterdirection  = TIMER_COUNTER_UP; //向上计数方式
    timer_initpara.period            = 1000-1; //计数值
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0; //设置重复计数器值,0表示不重复计数,每次溢出都产生更新事件
    timer_init(TIMER1,&timer_initpara);
    
    timer_auto_reload_shadow_enable(TIMER1);//使能自动重加载
    timer_enable(TIMER1);//使能定时器    
}


下面我们分析一下函数rcu_timer_clock_prescaler_config,因为它决定了定时器的时钟究竟是多少:

rcu_timer_clock_prescaler_config有一个参数,值为RCU_TIMER_PSC_MUL2或RCU_TIMER_PSC_MUL4,我们对照时钟树详细来说一下参数的意思:
在这里插入图片描述

这个图里面有三部分被红框标识,我们暂时称之上图1、上图2和上图3,后面分析的时候会用到。

定时器的TIMERSEL寄存器:
在这里插入图片描述

看上面时钟树局部图,对于GD32F425来讲,CK_AHB最高可以配置到200M,方法请参考我的另一篇文章

因为APBx的分频器(上图1)和定时器的时钟倍频器(上图2)是耦合在一起的,定时器的时钟倍频器不可以单独设置,因此有如下情况,

当函数rcu_timer_clock_prescaler_config参数为RCU_TIMER_PSC_MUL2的时候TIMERSEL的值将被设置为0,此时:

  • 若APBx的分频系数为不分频(上图1),那么定时器倍频器(上图2)将不倍频,此时CK_TIMERx = CK_AHB,即定时器时钟就是AHB总线时钟。
  • 若APBx的分频系数为2分频(上图1),那么定时器倍频器(上图2)将进行2倍频,此时依旧CK_TIMERx = CK_AHB,即定时器时钟就是AHB总线时钟。
  • 若APBx设置的分频系数是除不分频、2分频外的其它分频(上图1),那么定时器倍频器(上图2)将进行2倍频,此时CK_TIMERx = 2 x CK_APBx,即定时器时钟就是APBx总线时钟的2倍,具体多少要看APBx的时钟。

当函数rcu_timer_clock_prescaler_config参数为RCU_TIMER_PSC_MUL4的时候TIMERSEL的值将被设置为1,此时:

  • 若APBx的分频系数为不分频(上图1),那么定时器倍频器(上图2)将不倍频,此时CK_TIMERx = CK_AHB,即定时器时钟就是AHB总线时钟。
  • 若APBx的分频系数为2分频(上图1),那么定时器倍频器(上图2)将进行2倍频,此时依旧CK_TIMERx = CK_AHB,即定时器时钟就是AHB总线时钟。
  • 若APBx的分频系数为4分频(上图1),那么定时器倍频器(上图2)将进行4倍频,此时依旧CK_TIMERx = CK_AHB,即定时器时钟就是AHB总线时钟。
  • 若APBx设置的分频系数是除不分频、2分频、4分频外的其它分频(上图1),那么定时器倍频器(上图2)将进行4倍频,此时CK_TIMERx = 4 x CK_APBx,即定时器时钟就是APBx总线时钟的4倍,具体多少要看APBx的时钟。

现在我们指导,这个定时器的时钟和APBx的分频系数相关,那么我们就看一下在时钟初始化的时候,设置情况:

system_gd32f4xx.csystem_clock_200m_25m_hxtal(void)函数里面(因为我用的是25m晶振,所以是这个函数),里面有段代码如下:

/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB/2 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;
/* APB1 = AHB/4 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;

现在我们知道,对于我的工程APB1进行了4分频,APB2进行了2分频,那么为了保证挂在APB1总线上的timer1的输入时钟为200M,那么就要选择 rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);

基于其它的设置,就和STM32的定时器基本没有区别了,下面代码prescaler是将定时器的时钟先进行200分频,period是计数周期值为1000一个循环,如下:

timer_initpara.prescaler         = 200-1;//预分频
timer_initpara.period            = 1000-1; //计数值

4. 一个完整的代码例子

下面我们将完成一个定时器1毫秒溢出中断的程序,注意我这个的前提的APB1 = AHB/4, AHB = 200M,具体配置请看上面的描述;

//定时器初始化
void time1_init(void)
{
	
	timer_parameter_struct timer_initpara;//定时器结构体
	
	rcu_periph_clock_enable(RCU_TIMER1);//开启定时器时钟
	//因为APB1是AHB的4分频,因此定时器时钟CK_TIMERx = CK_AHB = 200m
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);//配置定时器时钟等于CK_AHB
	
	timer_struct_para_init(&timer_initpara);//将定时器结构体内参数配置成默认参数
    timer_deinit(TIMER1); //复位定时器
	
	/* TIMER1 configuration */
	//200M/200/1000 = 1k
    timer_initpara.prescaler         = 200-1;//预分频
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE; //边缘对齐
    timer_initpara.counterdirection  = TIMER_COUNTER_UP; //向上计数方式
    timer_initpara.period            = 1000-1; //计数值
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0; //每次溢出都产生更新是件
    timer_init(TIMER1,&timer_initpara);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);//使能自动重加载
	
	timer_interrupt_enable(TIMER1,TIMER_INT_UP);//使能溢出中断
	
	nvic_irq_enable(TIMER1_IRQn, 0, 1);//配置中断优先级
	
    /* TIMER1 enable */
    timer_enable(TIMER1);//使能定时器

}

//main函数
int main(void)
{
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);//配置优先级分组	
	timer1_init();
}

//中断函数
void TIMER1_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(TIMER1,TIMER_INT_UP)){
        //用户代码
		/* clear TIMER interrupt flag */
		timer_interrupt_flag_clear(TIMER1,TIMER_INT_UP);
    }      
}

代码简述:

利用rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4)将定时器时钟CK_TIMERx = CK_AHB = 200m

进入定时器配置,200M首先进行200分频,然后在计数1000次触发一次中断,此时200M/200/1000 = 1k,也就是说定时器中断周期为1k,即中断时间为1ms。

若需要1秒中断只需要将上面程序中的

timer_initpara.period = 1000-1; //计数值

改为

timer_initpara.period = 1000000-1; //计数值

注意:我们都知道STM32的timer1的溢出中断是有专门的中断函数的,而GD32的timer0对标的STM32的timer1,因此GD32的timer0也有专门的溢出中断函数,而对于GD32的timer1,所有的中断共用一个TIMER1_IRQHandler函数。也有些定时器好几个定时器共用一个中断函数,在编写的时候这点要注意。

你可以使用GD32系列微控制器中的定时器来实现脉冲计数功能。下面是一个简单的示例代码,演示如何使用GD32定时器2来进行脉冲计数: ```c #include "gd32f10x.h" void timer2_init(void) { /* 使能定时器2的时钟 */ rcu_periph_clock_enable(RCU_TIMER2); /* 配置定时器2的工作模式为边沿计数模式 */ timer_ic_parameter_struct timer_icinitpara; timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING; // 上升沿触发 timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; // 直接计数模式 timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1; // 输入捕获分频器预分频值 timer_icinitpara.icfilter = 0x0F; // 输入捕获滤波器 timer_input_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara); /* 配置定时器2的计数模式为上升沿计数 */ timer_counter_mode_config(TIMER2, TIMER_COUNTER_EDGE); /* 配置定时器2的触发源为TI0FP0 */ timer_external_trigger_source_config(TIMER2, TIMER_SMCFG_TRGSEL_CI0FE0); /* 配置定时器2的触发极性为上升沿触发 */ timer_external_trigger_polarity_config(TIMER2, TIMER_SMCFG_ETP); /* 配置定时器2的预分频值与计数值 */ timer_prescaler_config(TIMER2, 71, TIMER_PSC_RELOAD_UPDATE); timer_counter_value_config(TIMER2, 0); /* 使能定时器2的输入捕获功能 */ timer_channel_input_capture_config(TIMER2, TIMER_CH_0, TIMER_IC_SELECTION_DIRECTTI); timer_channel_enable(TIMER2, TIMER_CH_0); /* 使能定时器2 */ timer_enable(TIMER2); } uint32_t get_pulse_count(void) { /* 获取定时器2的当前计数值 */ return timer_channel_capture_value_register(TIMER2, TIMER_CH_0); } ``` 在使用之前,你需要确保已经正确初始化了GD32的系统时钟,并根据实际需求进行相应的配置。示例代码中的定时器2配置为边沿计数模式,上升沿触发,输入捕获滤波器为15个连续的采样结果,预分频值为71。可以根据实际情况进行调整。 使用`timer2_init()`函数初始化定时器2,然后使用`get_pulse_count()`函数可以获取当前的脉冲计数值。 请注意,示例代码中仅供参考,具体的配置和使用方法还需要根据你的实际需求进行调整。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈搭石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值