系统滴答定时器是捆绑在芯片内核里面的资源,利用系统定时器可以实现跨越不同平台,不同型号,不同库函数的各种MCU来实现统一的精准的计时,只要基于同一内核就可以实现很好的移值,例如STM32F103C8T6是基于Arm的Cortex-M3的内核,在对应的"cm3_core.h"内核寄存器描述的头文中可以找到关于滴答定时器的相关寄存器配置,基于滴答定时器的操作只需要对内核中寄存器进行配置,不需要经过外设寄存器和库函数等途径,因此,无论是基于寄存器开发,库函数开发,还是HAL库开发都可以很好的移植相关程序,那么,我们该如何实现利用内核里面的滴答定时器资源来实现精准延时?
首先,我们需要了解关于滴答定时器的相关寄存器配置。
#define SysTick ((SysTick_Type *) SysTick_BASE)
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
} SysTick_Type;
上述代码中可以看出SysTick是结构体SysTick_Type类型的指针,通过->可以引出结构体内部的成员,进一步对相应寄存器进行配置。
①CTRL:控制与状态寄存器;该寄存器第0位使能定时器,第一位一般给0,不开启结束异常中断,第二位给1,选择内核时钟;通过对第16位的轮询读取可以判断定时器是否计数到0,该位作为定时器的状态控制位;
②LOAD:重装载数值寄存器;配合系统内核时钟来实现给定单位时间的周期性的定时。
#elif defined SYSCLK_FREQ_72MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */
extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
在system_stm32f10x.h系统时钟配置头文件中将SystemCoreClock声明成外部变量,不同型号的系统内核时钟不同,当我们可以通过宏定义的方式来实现跨型号的移植;
③VAL:当前数值寄存器;可读可写,写时可以使它清零,该位便于对滴答定时器进行初始化操作。
其次,我们要来了解滴答定时器的工作流程;
最后,我们就可以实现精准延时的模块化代码编写与移植了,下面代码是基于江协科技的延时函数的改编;
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = SystemCoreClock/1000000 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}