单片机开发中常见的 BUG 总结(
很多 MCU 工程表面跑得挺好,一上线就出问题。原因通常不是硬件坏,而是代码里埋了“容易犯错”的地雷。本文整理了单片机开发中最常见的 Bug 类型、典型例子以及规避建议,适合裸机、RTOS 和各种嵌入式场景。一、中断相关问题(最容易出 BUG)[*]中断和主程序共享变量未同步
[*]问题:ISR 和主循环读写同一变量,没有 volatile 或原子保护。
[*]后果:数据错乱、状态丢失、偶发死循环。
[*]建议:
[*]使用 volatile 修饰共享变量;
[*]尽量只用中断写、主程序读(环形缓冲);
[*]若读写都有,需关中断保护临界区。
[*]中断优先级错误
[*]问题:多个中断抢占关系不明,嵌套或优先级冲突导致异常。
[*]建议:
[*]明确 NVIC 优先级数值越小优先级越高;
[*]避免 ISR 嵌套调用低优先级中断;
[*]保持 ISR 短小,不调用复杂逻辑。
[*]中断未清标志
[*]问题:ISR 中未清除中断标志,导致反复触发。
[*]建议:
[*]判断中断源后第一时间清除标志;
[*]ISR 尾部不能依赖“自然清除”。
示例:void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 一定要先清标志
// 处理中断事件...
}
}
二、内存和指针问题
[*]指针非法访问 / 越界
[*]问题:数组越界、空指针解引用、类型错误。
[*]建议:
[*]所有数组操作都加边界检查;
[*]禁止使用未经初始化的指针;
[*]用调试器查看访问地址是否合法。
[*]栈溢出
[*]问题:局部变量太大、递归、ISR 堆栈深度太大。
[*]后果:程序跑飞或重启。
[*]建议:
[*]减少 ISR 内局部变量;
[*]使用全局缓冲区替代大数组;
[*]注意链接文件栈空间配置。
[*]DMA 与 CPU 访问冲突
[*]问题:DMA 正在搬运数据时,CPU 同时读写导致冲突。
[*]建议:
[*]DMA 双缓冲设计(ping-pong);
[*]CPU 处理数据前判断 DMA 传输完成。
三、外设配置问题
[*]GPIO 模式配置错误
[*]问题:GPIO 复用未配置,导致串口/UART不工作或 IO 无效。
[*]建议:
[*]GPIO 先设置为复用功能再初始化外设;
[*]对照芯片手册确认管脚功能。
[*]时钟设置错误
[*]问题:PLL 未锁定就切换主时钟;HSE、HSI 配置顺序错误。
[*]建议:
[*]等待 RCC 状态稳定再切换;
[*]调试时打印系统时钟频率核对。
四、状态机与逻辑控制
[*]if/else 过多,流程混乱
[*]问题:逻辑条件堆叠,维护困难。
[*]建议:
[*]用状态机组织主流程;
[*]每个状态职责单一、逻辑清晰。
[*]状态变量未初始化
[*]问题:未初始化的状态进入未知分支。
[*]建议:
[*]进入主循环前初始化所有状态变量;
[*]状态机中加入默认处理。
五、volatile 使用不当
[*]忘记 volatile
[*]问题:中断修改变量但主循环没加 volatile,导致编译器优化出错。
[*]建议:
[*]所有 ISR 与主程序共享变量必须加 volatile;
[*]保证主循环每次都从内存读取最新值。
六、时序问题
[*]初始化顺序错误
[*]问题:串口发送失败,是因为先开 USART 后设 GPIO,或时钟没准备好。
[*]建议:
[*]正确顺序应是:GPIO 配置 → 外设初始化 → 启动。
[*]接口协议时序不满足(如 SPI/I2C)
[*]问题:时钟/片选/延时配合不对,导致通信失败。
[*]建议:
[*]加必要的等待或握手确认;
[*]用逻辑分析仪抓波形验证。
七、看门狗相关
[*]忘记喂狗 / 错误喂狗
[*]问题:系统运行中被错误复位。
[*]建议:
[*]喂狗应在主任务执行成功后再进行;
[*]不能在死循环或异常分支内喂狗。
八、编译优化与链接问题
[*]DEBUG 能跑,RELEASE 崩溃
[*]问题:release 模式开启优化,volatile 缺失或数组越界被放大。
[*]建议:
[*]优先在 release 模式下测试;
[*]所有共享变量确保 volatile 和边界检查。
[*]ISR 名字拼错,启动文件未链接
[*]后果:中断触发直接跑飞。
[*]建议:
[*]检查启动文件中断向量表定义;
[*]建议使用统一 ISR 重定向表。
九、边界条件与偶发异常
[*]缓冲区满、队列溢出、越界访问
[*]问题:数据量超过处理能力未及时判断,导致错乱。
[*]建议:
[*]加入队列满判断、缓冲区环绕判断;
[*]使用 assert 或日志记录关键边界。
[*]高负载时偶发问题
[*]问题:ISR 占用过长,任务调度被打断,或者数据丢失。
[*]建议:
[*]测试系统在极限负载下的行为;
[*]使用 trace 或日志分析堆栈执行情况。
十、RTOS 混用错误(如使用 FreeRTOS)
[*]ISR 中调用任务 API
[*]错误用法:ISR 内使用 vTaskDelay()、xQueueSend() 非 FromISR 版本。
[*]建议:
[*]中断内只能用 xxxFromISR() 接口;
[*]设置标志位,由任务响应执行。
[*]优先级配置错误
[*]NVIC 中断优先级未对齐 FreeRTOS 要求,导致 hard fault。
[*]建议:
[*]检查 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 配置;
[*]所有用 RTOS API 的中断优先级必须低于这个值。
总结:单片机开发中常见的高风险点汇总如下【中断同步】 共享变量未保护
【指针内存】 数组越界 / 栈溢出
【外设配置】 GPIO、时钟、USART 时序错误
【状态逻辑】 没有状态机、流程混乱
【volatile】 缺失或误用
【DMA冲突】 DMA 与 CPU 同时操作
【看门狗】 忘记喂狗或错误喂狗
【编译优化】 DEBUG 能跑,RELEASE 崩溃
【RTOS】 中断中误用任务 API、优先级错误建议:
[*]不止让程序“能跑起来”,更要在极限、干扰、异常场景下依然稳定;
[*]多用断言、日志、LED 闪烁等手段排查“偶发”问题;
[*]编写通用中断通信框架、状态机模板,减少重复踩坑。
如有需要,我可以分享一份中断+缓冲+状态控制的高可靠通信模板(裸机/RTOS皆可)。
欢迎补充与交流。希望本文能帮你提前避免那些“我也不知道为啥炸了”的问题。
页:
[1]