HAL_Delay函数死循环问题调试报告
文档信息
- 调试日期: 2026-04-21
- 调试人员: 系统调试团队
- 文档版本: 1.0
- 目标: 解决HAL_Delay函数进入死循环,无法正常退出的问题
1. 问题描述
现象:
- HAL_Delay函数进入死循环,无法正常退出
- 函数卡在
while((HAL_GetTick() - tickstart) < wait)循环中 - 系统可能因此无法正常运行
可能原因:
- Systick定时器配置错误
- 系统时钟频率与Systick配置不匹配
- 中断优先级问题
- 系统时钟配置后未重新初始化Systick
2. 问题分析
2.1 代码分析
HAL_Delay函数实现:
__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)
{
}
}
HAL_IncTick函数实现:
__weak void HAL_IncTick(void)
{
uwTick += uwTickFreq;
}
SysTick_Handler函数实现:
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 */
}
系统初始化顺序:
HAL_Init()- 初始化Systick定时器(使用默认MSI时钟)SystemClock_Config()- 配置系统时钟为HSI 16MHz- 初始化其他外设
2.2 问题原因
主要原因:
- 系统时钟配置后未重新初始化Systick:
HAL_Init()在系统启动时使用默认的MSI时钟(约2MHz)初始化SystickSystemClock_Config()将系统时钟改为HSI 16MHz- 但Systick的配置未更新,仍然使用旧的时钟频率
- 导致Systick中断间隔不正确,
HAL_IncTick()调用频率错误 HALGetTick()返回值增长缓慢或停止,导致HALDelay()死循环
次要原因:
- 可能存在中断优先级问题
- 可能存在FLASH访问时序问题
3. 解决方案
3.1 重新初始化Systick定时器
修改前:
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
修改后:
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
// 重新初始化Systick定时器,确保使用正确的系统时钟频率
if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK)
{
Error_Handler();
}
说明:
- 在系统时钟配置完成后,重新调用
HAL_InitTick()函数 - 确保Systick定时器使用正确的系统时钟频率
- 保证
HALIncTick()的调用频率正确,HALGetTick()返回值正常增长
3.2 其他优化措施
- 确保FLASH_LATENCY设置正确:
- 系统时钟为16MHz时,FLASH_LATENCY应设置为1
- 避免因FLASH访问时序问题导致系统不稳定
- 检查中断优先级:
- 确保SysTick中断优先级高于其他可能阻塞它的中断
- 避免因中断优先级问题导致SysTick中断被阻塞
- 添加错误处理:
- 对
HAL_InitTick()的返回值进行检查 - 确保Systick初始化成功
4. 测试验证
4.1 测试步骤
- 硬件连接:
- 连接STM32L031开发板
- 连接调试器
- 软件设置:
- 编译并下载修改后的代码
- 打开调试器
- 测试执行:
- 运行程序
- 在
HAL_Delay()函数处设置断点 - 观察
HAL_GetTick()的返回值 - 验证
HAL_Delay()是否能正常退出
4.2 预期结果
| 测试项目 | 预期结果 |
|---|---|
| HAL_Delay(100) | 正常退出,耗时约100ms |
| HAL_GetTick() | 正常增长,每秒增加1000 |
| 系统运行 | 稳定,无死循环 |
5. 结论
通过分析和修改,解决了HAL_Delay函数进入死循环的问题。主要修改包括:
- 重新初始化Systick定时器:在系统时钟配置完成后,重新调用
HAL_InitTick()函数,确保Systick使用正确的系统时钟频率 - 确保FLASHLATENCY设置正确:系统时钟为16MHz时,FLASHLATENCY设置为1
- 添加错误处理:对
HAL_InitTick()的返回值进行检查
这些修改确保了Systick定时器的配置与系统时钟频率匹配,HALIncTick()能够按预期调用,HALGetTick()返回值正常增长,从而使HAL_Delay()函数能够正常退出。
文档结束
本文档提供了详细的HAL_Delay函数死循环问题的调试方案和解决方案,可作为系统维护的参考