本文以 立创·天空星开发板-GD32F407VET6-青春版 作为学习的板子,记录学习笔记。
立创·天空星开发板-GD32F407VE-Timer
定时器
定时器是嵌入式系统中常用的一种外设,它可以产生一定的时间间隔、延时、定时等功能,广泛应用于定时、计数、脉冲宽度调制(PWM)等领域。
具体而言,定时器可以实现以下功能:
- 计时:定时器可以用来实现延时操作,例如等待外部设备的稳定、等待数据的接收等,也可以用来定时执行一些任务,例如周期性任务、定时采集数据等。
- 计数:定时器可以用来实现计数功能,例如计数外部事件的次数、计算脉冲信号的频率等。
- PWM输出:定时器可以用来产生PWM信号,通过调节占空比可以实现各种功能,例如LED调光、舵机控制等。
- 中断控制:定时器通常具有中断功能,可以在计数器计数到一定值时触发中断,以实现各种复杂的功能。
根据 GD32F407VE 的用户手册,定时器(TIMERx)分为五种类型,如下表所示:
可以看出所有的定时器都有预分频器,且都是 16位的,另外,除了定时器 1/4 支持 32位计数器外,其他的都是16位的计数器。
16位能表示的最大值为:2^16 - 1 = 65536 - 1 = 65535.
所以,在定时器配置的过程中,预分频器和计数器的配置都不建议超过 65535。
基本定时器示例
c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "ExtendedUSART.h"
#define PRESCALER 16800
#define PERIOD SystemCoreClock / PRESCALER
static void TIMER5_config() {
// 定时器 - 时钟配置
rcu_periph_clock_enable(RCU_TIMER5);
timer_deinit(TIMER5);
// 定时器 - 倍频配置
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
// 定时器 - 参数配置
timer_parameter_struct tps;
timer_struct_para_init(&tps);
tps.prescaler = PRESCALER - 1; // 分频系数
tps.period = PERIOD - 1; // 周期计数
timer_init(TIMER5, &tps);
// 定时器 - 中断配置
nvic_irq_enable(TIMER5_DAC_IRQn, 2, 2);
timer_interrupt_enable(TIMER5, TIMER_INT_UP);
timer_enable(TIMER5);
}
void TIMER5_DAC_IRQHandler(void) {
if(SET == timer_interrupt_flag_get(TIMER5, TIMER_INT_UP)) {
//清除中断标志位
timer_interrupt_flag_clear(TIMER5,TIMER_INT_FLAG_UP);
printf("timer interrupt triggered!");
}
}
int main(void) {
systick_config();
USART0_config();
TIMER5_config();
printf("USART0 in GD32 is running!\r\n");
while(1);
}
该段代码的执行效果是每隔 1s 向串口输出文本 timer interrupt triggered!
,可以通过串口助手看到输出,导入的 ExtendedUSART.h
可参考之前写的 立创·天空星开发板-GD32F407VE-USART。
我这里用的是基本定时器5,采用向上的计数模式,也就是自增计数,当计数值达到计数器最大值 65535 时,触发中断,自动自行中断处理函数 TIMER5_DAC_IRQHandler。
最为重要的两个参数是 PRESCALER
和 PERIOD
。这两个参数的目的是为了降频。因为 GD32F407VE 的主频是 168MHz,从计数的角度来说就是 MCU 能在 1s 内计数到 168M,从某种角度来说,可以理解为 168M ≈ 1s。那分频的意义在于,我希望能让 MCU 慢下来,同样是 1s,分频系数越大,数的数字就越少。
举例来说,如果按如下配置:
c
tps.prescaler = 16800; // 分频系数
tps.period = 10000; // 周期计数
不难发现,将 168MHz 按 16800 分频后,就使得原本能 1s 数数到 168M 现在 1s 只能数到 10000。
那我们就可以将数 10000 个数等价于 1s。数完就触发一次中断。如果希望半秒来一次中断,那就可以将计数周期设置为 5000。因为计数周期缩短一倍,触发时间自然缩短一半。
另外,还有一个比较重要的配置,那就是倍频。我在代码中采用的是四倍频,其函数的简介如下:
c
/*!
\brief configure the TIMER clock prescaler selection
\param[in] timer_clock_prescaler: TIMER clock selection
only one parameter can be selected which is shown as below:
\arg RCU_TIMER_PSC_MUL2: if APB1PSC/APB2PSC in RCU_CFG0 register is 0b0xx(CK_APBx = CK_AHB)
or 0b100(CK_APBx = CK_AHB/2), the TIMER clock is equal to CK_AHB(CK_TIMERx = CK_AHB).
or else, the TIMER clock is twice the corresponding APB clock (TIMER in APB1 domain: CK_TIMERx = 2 x CK_APB1;
TIMER in APB2 domain: CK_TIMERx = 2 x CK_APB2)
\arg RCU_TIMER_PSC_MUL4: if APB1PSC/APB2PSC in RCU_CFG0 register is 0b0xx(CK_APBx = CK_AHB),
0b100(CK_APBx = CK_AHB/2), or 0b101(CK_APBx = CK_AHB/4), the TIMER clock is equal to CK_AHB(CK_TIMERx = CK_AHB).
or else, the TIMER clock is four timers the corresponding APB clock (TIMER in APB1 domain: CK_TIMERx = 4 x CK_APB1;
TIMER in APB2 domain: CK_TIMERx = 4 x CK_APB2)
\param[out] none
\retval none
*/
void rcu_timer_clock_prescaler_config(uint32_t timer_clock_prescaler)
可见,GD32F407VE 提供了两种倍频的配置。置于选择哪种倍频,得取决于数据手册中的功能框图。如下所示:
由图所示,Timer5 在 APB1 总线上,其最高频率是42MHz,是 AHB 总线的四分之一。这就是需要进行四倍频的原因之一。另外,还可以通过数据手册中的时钟树来判断,如下所示: