一、SysTick定时器简介
SysTick,即系统滴答定时器,是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,SysTick 的时钟源自 HCLK。当计数值减到 0 时,将从 RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。
二、SysTick工作原理
每次 VAL 减到 0 时,VAL 自动从 LOAD 重载,开始新的一轮递减计数;
三、SysTick寄存器介绍
SysTick 有4个寄存器。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
3.1、SysTick控制及状态寄存器(CTRL)
3.2、SysTick重装载数值寄存器(LOAD)
3.3、SysTick当前数值寄存器(VAL)
3.4、SysTick校准数值寄存器(CALIB)
四、SysTick定时时间的计算
SysTick 的时钟源自 HCLK ,它的计数器是向下递减计数的,计数一次的时间 \(T_{DEC}\)=1/HCLK,,假设配置系统时钟为 72MHZ,经过分频器 8 分频后,那么 SysTick 的时钟即为 9Mhz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。
SysTick 延迟初始化函数:
uint16_t g_frequency_us = 0; // us延时倍乘数
/*******************************************************************************
* @brief : 延迟初始化函数
* @param : clock: 系统时钟频率,单位为MHz
* @return : None
* @author : Sakura
* @date : 2023/7/11
*******************************************************************************
* @description : None
******************************************************************************/
void Delay_Init(uint8_t clock)
{
SysTick->CTRL = 0; // 清Systick状态
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); // Systick使用内核时钟源8分频,因Systick的计数器最大值只有2^24
g_frequency_us = clock / 8; // 1us作为基础时基
}
微秒级延迟函数:
/*******************************************************************************
* @brief : 微秒级延迟函数
* @param : us: 要延迟的微秒数
* @return : None
* @author : Sakura
* @date : 2023/7/11
*******************************************************************************
* @description : 系统时钟频率为72MHz时,最大延迟时间为1.86s
系统时钟频率超频为128MHz时,最大延迟时间为1.04s
******************************************************************************/
void Delay_us(uint32_t us)
{
uint32_t temp;
SysTick->LOAD = us * g_frequency_us; // 延迟时间加载
SysTick->VAL = 0x00; // 清空计数器
SysTick->CTRL |= 1<<0; // 开始倒数
do {
temp = SysTick->CTRL;
}while ((temp & 0x01) && !(temp & (1<<16))); // CTRL.ENABLE位必须为1,并等待时间到达
SysTick->CTRL &= ~(1<<0); // 关闭Systick
SysTick->VAL = 0x00; // 清空计数器
}
毫秒级延迟函数:
/*******************************************************************************
* @brief : 毫秒级延迟函数
* @param : ms: 要延迟的毫秒数
* @return : None
* @author : Sakura
* @date : 2023/7/11
*******************************************************************************
* @description : None
******************************************************************************/
void Delay_ms(uint32_t ms)
{
uint32_t repeat = ms / 1000; // 考虑到超频情况,delay_us()最大延迟时间为1.04s
uint32_t remain = ms % 1000;
while (repeat)
{
Delay_us(1000 * 1000); // 利用Delay_us()实现1000ms延时
repeat--;
}
if (remain)
{
Delay_us(remain * 1000); // 利用Delay_us(),实现尾数延迟(remain ms)
}
}
五、HAL库延时函数HAL_Delay
HAL 库实现延时功能非常简单,首先定义了一个 32 位全局变量 uwTick,在 Systick 中断服务函数 SysTick_Handler 中通过调用 HAL_IncTick 实现 uwTick 值不断增加,也就是每隔 1ms 增加 uwTickFreq,而 uwTickFreq 默认是 1。而 HAL_Delay 函数在进入函数之后先记录当前 uwTick 的值,然后不断在循环中读取 uwTick 当前值,进行减运算,得出的就是延时的毫秒数。
/**
* @brief This function provides minimum delay (in milliseconds) based
* on variable incremented.
* @note In the default implementation , SysTick timer is the source of time base.
* It is used to generate interrupts at regular time intervals where uwTick
* is incremented.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @param Delay specifies the delay time length, in milliseconds.
* @retval None
*/
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
/**
* @brief Provides a tick value in millisecond.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @retval tick value
*/
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/**
* @brief This function is called to increment a global variable "uwTick"
* used as application time base.
* @note In the default implementation, this variable is incremented each 1ms
* in SysTick ISR.
* @note This function is declared as __weak to be overwritten in case of other
* implementations in user file.
* @retval None
*/
__weak void HAL_IncTick(void)
{
uwTick += uwTickFreq;
}
但是,HAL 库的延时函数在中断服务函数中使用 HAL_Delay 会引起混乱(虽然一般禁止在中断中使用延时函数),因为它是通过中断方式实现,而 Systick 的中断优先级是最低的,所以在中断中运行 HAL_Delay 会导致延时出现严重误差。
HAL 库的 ms 级别的延时函数 __weak void HAL_Delay(uint32_t Delay);它是弱定义函数,所以用户可以自己重新定义该函数。
标签:定时器,HAL,滴答,us,Delay,void,SysTick,06,uint32 From: https://www.cnblogs.com/kurome/p/17580883.html