HAL延时中断调试报告

HAL_Delay在中断上下文卡死的排查:main.c正常、mb.C中死循环,根因是UART中断优先级(0,0)高于Systick(TICK_INT_PRIORITY=3),中断里调用HAL_Delay导致Tick不推进,附优先级调整与修复

2026-07-04
HAL库中断延时嵌入式调试

HAL_Delay函数死循环问题调试报告 - 中断优先级问题

同样是 HAL_Delay 死循环,但这次更刁钻:同一份工程里,main.c 里的延时正常,mb.C 里的却卡死。这种"看情况死"的问题,十有八九是中断优先级埋的雷。以下把对比定位的过程记下来,给后来人留个路标,记录于 2026-04-21。


1. 问题描述

现象:

可能原因:

  1. 中断优先级问题
  2. 函数调用上下文不同
  3. 系统状态差异

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 */
}

2.2 问题原因

主要原因:

次要原因:


3. 解决方案

3.1 使用忙等待替代HAL_Delay

修改前:


HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(50); // 增加延时至50ms,符合蓝牙模块启动时序

// 使用非阻塞方式发送数据,避免卡住
HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, pch->TxBuf, pch->TxBufByteCtr, 100);
if (status != HAL_OK)
{
  // 发送失败,记录错误但继续执行
}

HAL_Delay(10); // 增加发送完成后的延时
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(10); // 增加拉高后的延时

修改后:


HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
// 不使用HAL_Delay,改为使用忙等待
for (uint32_t i = 0; i < 800000; i++) { __NOP(); }

// 使用非阻塞方式发送数据,避免卡住
HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, pch->TxBuf, pch->TxBufByteCtr, 100);
if (status != HAL_OK)
{
  // 发送失败,记录错误但继续执行
}

// 不使用HAL_Delay,改为使用忙等待
for (uint32_t i = 0; i < 160000; i++) { __NOP(); }
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
// 不使用HAL_Delay,改为使用忙等待
for (uint32_t i = 0; i < 160000; i++) { __NOP(); }

说明:

3.2 其他优化措施

  1. 调整中断优先级:
  1. 使用非阻塞方式:
  1. 使用定时器:

4. 测试验证

4.1 测试步骤

  1. 硬件连接:
  1. 软件设置:
  1. 测试执行:

4.2 预期结果

测试项目 预期结果
RS485_Send执行 正常完成,无死循环
PA15控制 正确拉低和拉高
蓝牙通信 正常建立连接
系统运行 稳定,无阻塞

5. 结论

通过分析和修改,解决了mb.C中HAL_Delay函数进入死循环的问题。主要修改包括:

  1. 使用忙等待替代HALDelay:在RS485Send函数中,将HALDelay替换为忙等待(_NOP()循环),避免依赖Systick中断
  2. 调整循环次数:根据系统时钟频率(16MHz)设置合适的循环次数,确保延时时间准确
  3. 保持功能完整性:确保PA15引脚控制时序符合蓝牙模块的要求

这些修改确保了RS485_Send函数在UART中断回调中能够正常执行,不会因为中断优先级问题而进入死循环。

至此,这个"main.c 正常、mb.C 卡死"的 HAL_Delay 死循环也定位清楚了:根子在 UART 中断回调里抢占了延时所需的上下文,优先级一乱,延时就走不动。把中断优先级理清楚,问题自然消失。

还想接着看