HAL_Delay 为什么不能在中断里调?
前言很多 STM32 新手写过这样的代码void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { HAL_Delay(50); // 按键消抖 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); }下载后发现按键按下去程序卡死了。为什么HAL_Delay在 main 循环里用得好好的进中断就出问题这篇文章彻底讲清楚。一、HAL_Delay 是怎么实现的打开stm32f4xx_hal.c看看HAL_Delay的代码__weak void HAL_Delay(uint32_t Delay) { uint32_t tickstart HAL_GetTick(); uint32_t wait Delay; ​ /* 防止 tick 计数器溢出导致 HAL_Delay 判定异常 */ if (wait HAL_MAX_DELAY) { wait (uint32_t)(uwTickFreq); } ​ while ((HAL_GetTick() - tickstart) wait) { } }核心逻辑就一句话不断读当前 tick等时间到了才返回。而HAL_GetTick()读取的是uwTick这个全局变量__weak void HAL_IncTick(void) { uwTick uwTickFreq; }这个uwTick谁来更新答案是// stm32f4xx_it.c 中 void SysTick_Handler(void) { HAL_IncTick(); }SysTick 中断每 1ms 触发一次uwTick才加 1。二、中断优先级导致死锁问题就在这里。HAL_Delay是一个死等的函数——它在 while 循环里反复读uwTick直到值达到目标。但uwTick是由SysTick 中断更新的。如果发生这种情况main 中正常运行 ↓ 按键按下了 ← 外部中断触发EXTI ↓ CPU 跳进 EXTI 中断服务函数 ↓ 调用 HAL_Delay(50) ← 开始死等 uwTick ↓ SysTick 中断本该在 1ms 后更新 uwTick ↓ 但是SysTick 的中断优先级 EXTI 的中断优先级 ↓ SysTick 中断被 EXTI 挡住了进不去 ↓ uwTick 永远不增加 ↓ HAL_Delay 永远等下去 ↓ 卡死关键是中断优先级的关系中断默认抢占优先级备注EXTI较低数字高优先默认比 SysTick 高通常为 0 或 5取决于 CubeMX 配置SysTick较大数字低优先默认 15高优先级中断在执行时同优先级或更低优先级的中断无法打断它。所以当 EXTI 中断里调HAL_DelaySysTick 中断无法触发uwTick卡住死锁。三、验证方法在 EXTI 回调中试着直接读HAL_GetTick()void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { uint32_t t1 HAL_GetTick(); for (volatile int i 0; i 10000; i); // 磨蹭一会 uint32_t t2 HAL_GetTick(); ​ printf(t1%lu, t2%lu, diff%lu\r\n, t1, t2, t2 - t1); // t1 和 t2 永远相等——SysTick 中断没进来 }四、解决办法4.1 不要在中断里死等这是最根本的解决思路。中断应该快进快出// 错误在中断里等 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { HAL_Delay(50); // ❌ 不要这样做 } ​ // 正确中断里只置标志位 volatile uint8_t key_flag 0; ​ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { key_flag 1; // ✅ 标记事件主循环处理 } ​ // main 循环中 while (1) { if (key_flag) { key_flag 0; HAL_Delay(20); // 现在在 main 里等没问题 // ... 处理按键 } }中断只干两件事置标志位、接收数据。剩下的主循环做。4.2 提高 SysTick 优先级不推荐把 SysTick 的中断优先级设得比 EXTI 高这样即使 EXTI 里调HAL_DelaySysTick 也能打断它更新uwTick。HAL_InitTick(TICK_INT_PRIORITY); // 在 main 中调用值越小优先级越高比如设HAL_InitTick(0)SysTick 就是最高优先级。为什么不推荐这只解决了 HAL_Delay 的问题其他在中断中可能被调用的需要时间等待的函数也会遇到同样的坑。而且改变了系统的中断优先级设计可能会引入其他 bug。4.3 自定义非阻塞延时推荐学习写法// 非阻塞延时检查 uint8_t Check_Delay(uint32_t *start_tick, uint32_t delay_ms) { if ((HAL_GetTick() - *start_tick) delay_ms) return 1; // 时间到了 return 0; // 还没到 } // 使用方式 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { static uint32_t last_tick 0; if (Check_Delay(last_tick, 200)) // 距上次触发超过 200ms 才处理 { last_tick HAL_GetTick(); HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } }这段代码不阻塞、不进死等在任何上下文中都能安全调用。五、还有哪些函数不能在中断里调HAL 库中带有超时参数、内部有 while 死等的函数都不适合在中断里直接调函数原因HAL_Delay()死等 uwTick 更新HAL_UART_Transmit(..., timeout)等发送完成中断优先级可能导致死锁HAL_SPI_Transmit(..., timeout)同上HAL_I2C_Master_Transmit(..., timeout)同上所有PollFor...函数本质上都是轮询等待这些函数在中断优先级低于 SysTick的情况下调用不会死锁但这不是可靠的设计。中断里能安全调用的置标志位、读写简单变量往队列/缓冲区写入数据HAL_xxx_Transmit_IT()中断版——它只启动传输就返回六、如果必须要延时怎么办有些场景确实需要在中断里等一会儿比如跟特定时序的传感器通信。方案状态机 定时器// 定时器每 100μs 触发一次中断 // 状态机在定时器中断中推进 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint8_t step 0; switch (step) { case 0: // 拉低 CS CS_LOW(); step; break; case 1: // 等 1ms 后发命令 if (delay_check(1)) { // 自制的简易计时器 SPI_Transmit(cmd); step; } break; case 2: // 等等... // ... break; } }不用HAL_Delay用定时器驱动状态机逐步推进每个步骤只做一点点事。七、总结要点一句话为什么死锁高优先级中断里等 uwTick但 SysTick 进不来最佳实践中断只置标志主循环再处理耗时操作不要改 SysTick 优先级来绕过治标不治本非要延时用定时器状态机不用 HAL_Delay面试常问这就是考点——中断里的 HAL_Delay 会死锁原因解释清楚

相关新闻

【深度学习】OpenCV日常学习0629

【深度学习】OpenCV日常学习0629

定义 鲁棒性指模型在输入数据分布发生变化、包含随机噪声或遭受恶意扰动时,仍能维持原有预测性能的能力。数学上等价于模型输出函数对输入扰动的敏感性较低。 运用方法(工程实践) 对抗训练:训练时动态生成对抗样本(如F…

2026/7/6 14:46:37阅读更多 →
YOLOv10模型改进-特定领域应用-第96篇:YOLOv10改进策略【特定领域应用】| YOLOv10在无人机巡检中的应用

YOLOv10模型改进-特定领域应用-第96篇:YOLOv10改进策略【特定领域应用】| YOLOv10在无人机巡检中的应用

一、本文介绍 本文记录的是YOLOv10在无人机巡检领域的应用。无人机巡检需要检测电力设施、道路状况、建筑物损伤等目标,YOLOv10凭借其高效的推理速度和准确的检测能力,成为无人机巡检领域的首选目标检测算法。 二、无人机巡检场景分析 2.1 检测目标 无人…

2026/7/6 14:46:37阅读更多 →
路由模式:智能分发 + 垂直 Agent

路由模式:智能分发 + 垂直 Agent

路由模式:智能分发 垂直 Agent路由模式的核心是智能分发——根据任务类型、内容或上下文将请求路由到不同的垂直 Agent。读完你能设计出适配多种场景的路由层,理解分类器精度与兜底策略的关键 trade-off。一、概念速查 1.1 什么是路由模式 路由模式&…

2026/7/6 14:46:37阅读更多 →
rogauracore未来路线图:新功能规划与技术发展方向展望

rogauracore未来路线图:新功能规划与技术发展方向展望

rogauracore未来路线图:新功能规划与技术发展方向展望 【免费下载链接】rogauracore RGB keyboard control for Asus ROG laptops 项目地址: https://gitcode.com/gh_mirrors/ro/rogauracore rogauracore是一款专为华硕ROG笔记本打造的RGB键盘控制工具&#…

2026/7/6 15:56:49阅读更多 →
SmartMirror项目架构深度剖析:从硬件到软件的完整技术栈

SmartMirror项目架构深度剖析:从硬件到软件的完整技术栈

SmartMirror项目架构深度剖析:从硬件到软件的完整技术栈 【免费下载链接】SmartMirror Motion controlled SmartMirror 项目地址: https://gitcode.com/gh_mirrors/smar/SmartMirror 想要打造一款炫酷的智能魔镜吗?SmartMirror项目为您展示了如何…

2026/7/6 15:56:49阅读更多 →
Vue登录页面加载动画优化:如何实现优雅的登录状态切换

Vue登录页面加载动画优化:如何实现优雅的登录状态切换

Vue登录页面加载动画优化:如何实现优雅的登录状态切换 【免费下载链接】vue-example-login 🔥A login demo for Vue.js. 项目地址: https://gitcode.com/gh_mirrors/vu/vue-example-login 在Vue.js应用开发中,登录页面的用户体验至关重…

2026/7/6 15:56:49阅读更多 →
CodexGuide × HyperFrames:用代码生成动画视频的终极教程

CodexGuide × HyperFrames:用代码生成动画视频的终极教程

CodexGuide HyperFrames:用代码生成动画视频的终极教程 【免费下载链接】CodexGuide CodexGuide:面向全球初学者、创作者、开发者与团队的 Codex 实践指南 项目地址: https://gitcode.com/gh_mirrors/co/CodexGuide 想要用代码轻松制作专业动画视…

2026/7/6 15:56:49阅读更多 →
Syncthing-Fork自动化技巧:利用定时同步与脚本实现文件管理自动化

Syncthing-Fork自动化技巧:利用定时同步与脚本实现文件管理自动化

Syncthing-Fork自动化技巧:利用定时同步与脚本实现文件管理自动化 【免费下载链接】syncthing-android Syncthing-Fork - A Syncthing Wrapper for Android. 项目地址: https://gitcode.com/gh_mirrors/sync/syncthing-android Syncthing-Fork作为一款优秀的…

2026/7/6 15:56:49阅读更多 →
如何利用Steam挂刀行情站实现饰品交易收益最大化

如何利用Steam挂刀行情站实现饰品交易收益最大化

如何利用Steam挂刀行情站实现饰品交易收益最大化 【免费下载链接】SteamTradingSiteTracker Steam 挂刀行情站 —— 24小时更新的 BUFF & IGXE & C5 & UUYP & ECO 挂刀比例数据 | Track cheap Steam Community Market items on buff.163.com, igxe.cn, c5game.…

2026/7/6 15:51:48阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/6 4:26:20阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/6 2:48:33阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/6 0:10:35阅读更多 →
Seraphine:基于LCU API的英雄联盟智能游戏助手技术解析与应用指南

Seraphine:基于LCU API的英雄联盟智能游戏助手技术解析与应用指南

Seraphine:基于LCU API的英雄联盟智能游戏助手技术解析与应用指南 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 技术架构先行:官方接口的合规应用 你是否曾在BP阶段手忙脚乱&#x…

2026/7/6 0:03:39阅读更多 →
多协议远程连接管理工具mRemoteNG:告别混乱,统一你的远程桌面管理

多协议远程连接管理工具mRemoteNG:告别混乱,统一你的远程桌面管理

多协议远程连接管理工具mRemoteNG:告别混乱,统一你的远程桌面管理 【免费下载链接】mRemoteNG mRemoteNG is the next generation of mRemote, open source, tabbed, multi-protocol, remote connections manager. 项目地址: https://gitcode.com/gh_m…

2026/7/6 0:03:39阅读更多 →
COUNT(DISTINCT) 与 GROUP BY 去重统计:5 亿数据量下的性能实测与选型指南

COUNT(DISTINCT) 与 GROUP BY 去重统计:5 亿数据量下的性能实测与选型指南

COUNT(DISTINCT) 与 GROUP BY 去重统计:5 亿数据量下的性能实测与选型指南在数据分析和处理领域,去重统计是最基础也是最频繁使用的操作之一。当数据量达到亿级规模时,不同的去重统计方法在性能上可能产生天壤之别。本文将基于 5 亿行数据的实…

2026/7/6 0:03:39阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/6 4:45:01阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/6 4:45:01阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/6 4:45:03阅读更多 →