三、精准计时:滴答定时器探秘与应用
文章目录
- 滴答定时器简介
- 滴答定时器(SysTick)是Cortex - M4内核中的一个24位的倒计数定时器。它的主要作用是为操作系统或其他需要定时功能的应用提供一个简单的定时基准。它具有以下特点:
- 简单易用,集成在处理器内核中,不需要额外的外部硬件。
- 可以产生周期性的中断,方便用于任务调度等操作。
- 其时钟源可以是处理器的内部时钟或者外部时钟,通过配置可以灵活选择。
- 滴答定时器(SysTick)是Cortex - M4内核中的一个24位的倒计数定时器。它的主要作用是为操作系统或其他需要定时功能的应用提供一个简单的定时基准。它具有以下特点:
- 滴答定时器寄存器
- 控制和状态寄存器(SysTick_CTRL)
- 位0(ENABLE):用于使能或禁用SysTick定时器。当该位为1时,定时器开始计数;为0时,定时器停止计数。
- 位1(TICKINT):用于使能SysTick定时器中断。当该位为1时,定时器计数到0时会产生中断请求;为0时,不会产生中断。
- 位2(CLKSOURCE):用于选择时钟源。为0时,选择外部时钟源(一般是处理器的AHB时钟除以8);为1时,选择处理器的核心时钟。
- 位16(COUNTFLAG):这是一个状态位,当定时器计数到0时,该位会被置1,读取该位后会自动清零。
- 重装载值寄存器(SysTick_LOAD):这是一个24位的寄存器,用于设置定时器的初始计数值。当定时器启动后,会从这个设置的值开始向下计数。例如,如果要设置定时器每1000个时钟周期产生一次中断(假设时钟频率为1kHz,即1ms的定时周期),则可以将此寄存器的值设置为999(因为是从设置的值开始计数,到0结束,总共是1000个周期)。
- 当前值寄存器(SysTick_VAL):这是一个24位的寄存器,用于读取当前定时器的计数值。在定时器运行过程中,可以通过读取这个寄存器来获取定时器的当前状态。不过,读取这个寄存器可能会影响定时器的正常计数,因为它是一个硬件计数器。
- 控制和状态寄存器(SysTick_CTRL)
- 滴答定时器函数(以GD32F450ZG标准库为例)
- 通常会有初始化函数来设置定时器的参数,如
SysTick_Config()
函数。这个函数会自动完成SysTick定时器的基本配置,包括设置重装载值、选择时钟源、使能定时器和中断等。其函数原型大概如下:
static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ SysTick->VAL = 0; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */ }
- 中断服务函数,当SysTick定时器计数到0并且使能了中断时,会进入中断服务函数。用户需要在中断服务函数中编写自己的定时任务代码。在GD32F450ZG中,中断服务函数的名称通常是
SysTick_Handler()
。
- 通常会有初始化函数来设置定时器的参数,如
- 配置滴答定时器
- 步骤如下:
- 首先确定定时周期。假设系统时钟频率为
SystemCoreClock
,要实现1s的定时周期,需要计算出SysTick定时器的重装载值。如果选择内部时钟源(即SysTick_CTRL_CLKSOURCE_Msk位为1),计算公式为:重装载值=(SystemCoreClock/1000)-1(这里假设要实现1ms的定时周期,1s需要1000个这样的周期)。 - 然后调用
SysTick_Config()
函数来配置定时器。例如:
#define TICK_PER_SECOND 1000 uint32_t sysTickCounter = 0; void SysTick_Configuration(void) { if (SysTick_Config(SystemCoreClock/TICK_PER_SECOND - 1)) { // 配置出错处理 while (1); } }
- 首先确定定时周期。假设系统时钟频率为
- 步骤如下:
- 使用滴答定时器
- GD32 官方也给我们写好了延时 1ms 的函数,void delay_1ms(uint32_t count),有一个参数 count,这个就是我们要延时的时间的 ms 数,延时 1ms 就是 delay_1ms(1),延时 1s 就是 delay_1ms(1000)。需要注意的是这个延时是阻塞延时,在延时的时间里一直在等待,比较浪费系统资源,慎用。
/*
\brief delay a time in milliseconds
\param[in] count: count in milliseconds
\param[out] none
\retval none
*/
void delay_1ms(uint32_t count)
{
delay = count;
while(0U != delay) {
}
}
- 完整的实验代码示例(包含必要的头文件和初始化部分)
- 首先包含必要的头文件:
#include "gd32f4xx.h"
#include "systick.h"
#include "gd32f4xx_gpio.h"
#include "gd32f4xx_rcu.h"
- 然后进行GPIO和SysTick的初始化:
void GPIO_Configuration(void)
{
rcu_periph_clock_enable(RCU_GPIOD);
gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
}
int main(void)
{
GPIO_Configuration();
SysTick_Config();
while (1)
{
gpio_bit_toggle(LED2,GPIO_PIN_7);
delay_1ms(1000);//间隔1s
}
}
上述代码初始化了PD7引脚为输出模式,在main
函数中,通过一个无限循环来等待定时器中断的发生并执行相应的操作。注意,实际应用中可能需要根据具体的开发环境和芯片库版本进行适当的调整。