问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

GD32定时器编程详解:以L4类型定时器为例

创作时间:
作者:
@小白创作中心

GD32定时器编程详解:以L4类型定时器为例

引用
CSDN
1.
https://blog.csdn.net/m0_63235356/article/details/138400593

本文将详细介绍GD32微控制器中的定时器资源及其使用方法,重点讲解L4类型定时器的配置和编程。通过一个秒表示例,帮助读者掌握GD32定时器的基本应用。

GD32中的定时器

GD32E230芯片内置了七个定时器,分为六种类型,其中通用的L4版本有两个,其他类型的各一个。本文将以通用L4定时器为例进行讲解,其他类型的定时器配置流程大体相似。

通用L4定时器

虽然每种类型的定时器都有自己的结构框图,但基本原理相似,主要包括自动重装载寄存器、预分频器和输出比较功能。需要注意的是,通用L4定时器只有一个时钟源。

在使用GD32定时器时,需要查阅手册了解各个定时器的具体特性,因为GD32的定时器种类较多,细节上存在差异。

对于L4定时器,其时钟源来自AHB总线,频率为72MHz。这个时钟经过一个分频器后提供给Timer0、14、15、16。分频器的工作方式取决于APB2的分频设置,如果APB2是1分频,那么分频器也是1分频,否则是2分频。由于APB2默认是1分频,因此L4定时器的实际时钟频率就是72MHz。

定时器溢出频率的计算公式与STM32相同:
[ \text{溢出频率} = \frac{\text{时钟频率}}{(\text{自动重装载寄存器的值} + 1) \times (\text{预分频器的值} + 1)} ]

固件库函数

以下是与定时器相关的几个主要函数:

timer_init

初始化定时器,参数一选择是哪一个定时器。参数二传入一个结构体变量的指针。我们来看看这个结构体。

通过配置预分频值和周期来控制定时器溢出的频率,就用上面的公式,把这个周期看成是自动重装载计数器就行。对齐方式一般选择边缘对齐TIMER_COUNTER_EDGE。计数方式选择向上计数TIMER_COUNTER_UP,L4里也只能向上计数。时钟分频因子这个是输入捕获的时候才用的到的,我们可以不配。重复计数器值是只溢出多少次之后再溢出才进一次中断的,不过只有高级定时器才有,我们也不配。

timer_enable

使能定时器,当我们配置完定时器包括中断之后就可以使能了。

timer_disable

失能定时器。

timer_prescaler_config

配置预分频器的模式,我们选择立即加载。

timer_interrupt_enable

使能中断,中断源我们就选第一个,更新中断,这是不管哪一种定时器都能用的,其他参数可以参考上面表格的说法。

timer_interrupt_flag_get

获取中断标志位,一样是在中断处理函数中判断是哪个中断源触发的中断。

timer_interrupt_flag_clear

清除中断标志位,除了在中断处理函数中使用,我们在一开始配置定时器的时候就需要用一下,否则会马上触发一次中断。

上面这些函数就足够我们进行最基础的一个计时了,但是还有个问题,就是中断,中断都需要配置NVIC。

NVIC使能的函数我们在上一篇外部中断里说过了,这边就直接把代码贴出来了。

nvic_irq_enable(TIMER15_IRQn,1);

最后一个就是中断处理函数了,我们一样是从启动文件的汇编文件里去找。

秒表示例代码

下面是一个秒表示例代码,实现精度为10ms的计时功能:

#include "gd32e23x.h"
#include "systick.h"
#include "OLED.h"

uint16_t Z_Time_s = 0 ,Z_Time_10ms = 0;

void TIMER15_IRQHandler(void){
    if(timer_interrupt_flag_get(TIMER15,TIMER_INT_FLAG_UP) == SET){
        timer_interrupt_flag_clear(TIMER15,TIMER_INT_FLAG_UP); //清除中断标志位
        if(++Z_Time_10ms >= 100) {
            Z_Time_10ms = 0;
            ++Z_Time_s;
        }
    }
}

int main(void){
    systick_config();
    OLED_Init();
        
    rcu_periph_clock_enable(RCU_TIMER15);                   //开启定时器时钟  
    
    timer_parameter_struct timer_initpara;                  
    timer_initpara.prescaler = 200 -1;                      //预分频
    timer_initpara.alignedmode = TIMER_COUNTER_EDGE;        //边缘对齐
    timer_initpara.counterdirection = TIMER_COUNTER_UP;     //向上计数
    timer_initpara.period = 3600  - 1;                      //周期
    timer_init(TIMER15,&timer_initpara);                    //初始化定时器
    
    timer_prescaler_config(TIMER15, 1, TIMER_PSC_RELOAD_NOW);//设置预分频器为立即加载模式
    
    timer_interrupt_flag_clear(TIMER15, TIMER_INT_FLAG_UP); //清除中断标志位,否则一开始就会进一次中断
    nvic_irq_enable(TIMER15_IRQn, 1);                       //设置中断优先级
    timer_interrupt_enable(TIMER15, TIMER_INT_UP);          //使能更新中断 
    
    timer_enable(TIMER15);                                  //使能定时器
    
    OLED_ShowString(1,5,":");
    while(1){
        OLED_ShowNum(1,1,Z_Time_s,4);
        OLED_ShowNum(1,6,Z_Time_10ms,2);
    }
}

我这边做个秒表的效果,以精度在10ms,因此我们需要每秒定时器溢出中断100次。因为时钟源是72MHz,因此需要让72MHz去除以(周期+1)再除以(预分频+1)最终等于100即可。最后一点要注意的就是这俩寄存器是16位的,因此设置周期和预分频的值的时候要注意不要超过65535了。

大家可以配合上个文章的外部中断,在这个秒表的基础上加几个按钮,实现暂停继续,清零等功能。就当是课后作业了(bushi)

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号