STM32 官方 HAL 库的延时函数主要是 HAL_delay()
,其实现基于 SysTick 定时器的中断机制,属于 "阻塞式但依赖中断更新时基" 的方式,具体原理如下:
1. 核心实现原理
HAL_Delay()
函数通过一个全局的毫秒级时基变量(uwTick
)实现延时,该变量由 SysTick 定时器的周期性中断自动更新。
- 时基更新 :SysTick 定时器被配置为每 1ms 触发一次中断,在中断服务函数
SysTick_Handler()
中,uwTick
变量会自动加 1(记录系统运行的毫秒数)。 - 延时等待 :
HAL_Delay()
函数记录当前uwTick
值,然后循环等待,直到uwTick
的增量达到设定的延时毫秒数。
2. 关键代码解析
(1)SysTick 定时器初始化(时基配置)
在系统初始化阶段(如 HAL_Init()
函数中),HAL 库会自动配置 SysTick 定时器:
c
运行
// 初始化 SysTick 为 1ms 中断一次
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
// 配置 SysTick 时钟源为 HCLK 分频(通常为 HCLK/1,即 72MHz 或 168MHz 等)
if (SysTick_Config(SystemCoreClock / 1000U) == 0)
{
// 配置 SysTick 中断优先级
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
NVIC_SetPriority(SysTick_IRQn, TickPriority);
return HAL_OK;
}
else
{
return HAL_ERROR;
}
}
else
{
return HAL_ERROR;
}
}
SysTick_Config(SystemCoreClock / 1000U)
配置 SysTick 每 1ms 触发一次中断(如系统时钟为 72MHz 时,装载值为 72000)。
(2)SysTick 中断服务函数(更新时基)
c
运行
void SysTick_Handler(void)
{
HAL_IncTick(); // 调用 HAL 库的时基更新函数
}
__weak void HAL_IncTick(void)
{
uwTick++; // 全局时基变量自增(每 1ms 加 1)
}
uwTick
是一个全局变量,记录系统从启动到当前的毫秒数。
(3)HAL_Delay()
函数实现
c
运行
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick(); // 获取当前 uwTick 值
uint32_t wait = Delay;
// 确保至少等待 Delay 毫秒(补偿可能的计时误差)
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)uwTickFreq; // uwTickFreq 为 1(1ms 精度)
}
// 循环等待,直到 uwTick 增量达到设定的延时值
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
// 获取当前时基值(封装 uwTick)
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
- 函数先记录起始时基值
tickstart
,然后通过while
循环等待,直到当前时基值与tickstart
的差值大于等于Delay
。
江科大的延时函数:无中断的阻塞式延时
void Delay_us(uint32_t xus) { SysTick->LOAD = 72 * xus; //设置定时器重装值 SysTick->VAL = 0x00; //清空当前计数值 SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器 while(!(SysTick->CTRL & 0x00010000)); //等待计数到0 SysTick->CTRL = 0x00000004; //关闭定时器 }
"基于硬件定时器的延时"(因为它直接操作定时器寄存器,通过单次计数等待实现延时),但它使用的是 SysTick 定时器(内核定时器)而非外设定时器。
这种方式的优势是精度高(微秒级)、不依赖中断,适合对延时精度要求高的场景,但缺点是延时期间会阻塞 CPU,且 SysTick 若被其他模块(如 RTOS)占用时可能冲突。