调试与可靠性问答总集

📚 总集 📅 2026-09-04
#来源/立芯星球 #类型/问答总集

调试与可靠性问答总集

本篇将本目录全部问答帖按知识相关性合并重组,共收录 60 余个知识点(含 3 篇收录于 02 目录的调试主题帖)。同类问题多来源已合并为一条,条目末尾给出原帖与作者。仅图片、无回答帖已在原笔记标记 review: 建议隔离,未收录。

知识地图

  • 一、调试方法论:checklist / 分层排查 / 不可复现问题 / 疑难杂症协作 / 环境一致性
  • 二、HardFault 与异常定位:异常升级机制 / 栈回溯 / 异常返回与屏障指令 / 断点硬件单元 / 多线程调试
  • 三、日志与工具链:RTT/SystemView/Trace / SWD-JTAG / 逻辑分析仪 / GPIO 插桩 / 调试器可见性
  • 四、看门狗与稳定性:喂狗监测 / 外部看门狗 / 向量产验证 / 产测架构
  • 五、测试验证:测试金字塔 / Unity 框架 / 回归测试集 / 测试思维
  • 六、典型案例复盘:Flash 写入 / 屏幕驱动 / 传感器标定 / 预取指死机 / 下载连接故障

一、调试方法论

Bug 调试 checklist(精华)。 逐项检查:1. 栈大小够不够;2. 优化等级(开发阶段建议 0);3. 是否进入死循环(全速运行后暂停,看是否停在 HardFault,再做栈回溯或用 cmbacktrace);4. 打印每个相关函数的返回值;5. 打印链路上的指针,断点处看是否为 0x00000000;6. FreeRTOS 优先用其原生 API(queue.h、task.h)而非 CMSIS-OS 封装;7. polling 类快速代码不便打断点,就在各状态/分支里散布 printf 标记。
bug调试的checklist(Jack)

排查软件问题先分层,再"让问题可观测"。 顺序:先确认供电、时钟等底座,再看外设波形与通信,最后看协议解析与业务状态机;工具用示波器、逻分、串口抓包。
Q3 平常如何排查软件问题?遇到不可复现的问题怎么办(Jack)

不可复现问题的三板斧。 第一,加现场日志,把关键状态、错误码、最后一帧协议在异常时落盘;第二,保存复位原因与异常栈,至少知道死在哪;第三,按概率排序做假设验证(竞态、缓冲区越界、超时未处理是高频嫌疑),用加保护、加断言、加水位监控把问题"逼出来",把偶现变成可定位。
Q3 平常如何排查软件问题?遇到不可复现的问题怎么办(Jack)

偶发性问题定位的三个层级。 1. CmBacktrace:进 HardFault 时把寄存器与调用栈打印保存到内部 Flash,复位或接调试器后再读出分析;2. 野指针类偶现问题用 MPU:在任务切换钩子里为每个线程设置可访问空间,越界访问直接被拦截进 HardFault,实现野指针捕获;3. Trace 捕获器全程监听寄存器变化,最靠谱但成本高,需导出 Trace 接口。
偶发性问题定位,有更详细一点的(Jack,收录于 02 目录)

大厂疑难杂症的提问与组织方法(精华)。 求助前讲清 7 件事:问题对项目的影响定调(是否卡量产);出现次数/复现手法/复现场景(本地、产线还是售后);是否疑似硬件或特定机器;是否与一供二供物料差异相关;deadline 与失败后果;当前分析状态与有无 workround;有无劣化手段加速复现。执行上:抽调人手与机器做劣化实验和 patch 验证,每天固定点检导出 log 与 coredump,多轮 DOE 排除变量,逐日更新进展;实验机器宝贵,debug log 一次加全,避免反复返工。
很多工程师因为英文不好或者没有基础,读不懂外设的数据手册和 Soc 的芯片手册(Jason)

熟悉陌生代码不要逐行读,去调试中熟悉功能。 先了解产品功能,再在调试中观察该功能经过哪些模块、被哪些条件触发。
大佬们,有个问题想请教一下,就是我目前在公司实习,然后领导让看公司的代码(Jack)

环境一致性是排查问题的前提。 版本不一致的软件全部卸载、统一安装指定版本;嵌入式环境问题极其消耗时间,"等你发现是环境问题,你会怀疑自己"。
工作中,学习中,如果电脑上之前安装了对应的软件了怎么办(Jack)

自己写的东西自己测不出是必然的。 这正是测试需要独立视角、需要他人评审与测试介入的底层原因。
老师,我之前有看到别人说开发自己写的东西,自己是测不出来的,真这样:(Jack)

质量是设计出来的,不是测试堆出来的。 事后测试只是兜底:项目初期需求清晰、架构合理,执行期版本控制、代码审查、自动化构建与测试,才是质量主力;架构混乱的项目测试再努力也只是补丁摞补丁。
过去有一种流行的观点:只要不断测试和调试,最终一定能把产品做得好(山海无名)

嵌入式敏捷指开发过程敏捷。 即开发中实时调整需求,而非交付后再敏捷;芯片+模组行业例外——SDK 常带着坑首发,等客户踩坑后"下个版本修复"。
嵌入式软件怎么实现敏捷开发呢,发布一版新的软件不像互联网软件那样可以灵活更新(Jack)

没有人只做自己小组的工作。 项目前期全员参与规格与可行性评估,中期固件帮系统、系统帮 AP,后期所有人都要参与测试与 debug。
几乎没有人只会或只需要做某个小组的工作。举例来说,项目刚开始时,项目管理组会比较忙(山海无名)

基于 OS 的调试比裸机难。 常规裸机手段(打 log、在线调试)在 OS 下都会失真:log 会丢、断点影响时序。束手无策时回到"可运行的基准版本",一点一点加代码边加边分析。
关于基于OS的调试: OS本(Jack)

设计要高内聚低耦合,日志连接可以合并。 两个主体之间若存在多条连接,日志这类连接应当合并收口,否则耦合过多。
为什么这样的设计不合适,违背了高内聚,低耦合(Jack)

二、HardFault 与异常定位

哪些错误会进入 HardFault(多源合并)。 BusFault:访问无效地址;MemoryManageFault:访问违例(MPU 拦截);UsageFault:除 0、非法指令等。关键机制:当这些错误未配置处理函数(或未拦截)时,自动升级为 HardFault——平台没定义 MemManage 处理函数,本属它的错误就直接变 HardFault;在错误处理里直接退出同样会升级。所以 HardFault 的真凶常在上一层,栈回溯不能只回溯一层,可能要好几层。
什么时候会进入Hardfault中断?(重点答案在最下面(Jack)、ARM中,对于平台未定义服务函数的异常,一般都是自动升级成hardfault(Jack)、为什么很多错误不及时处理会引起hardfault(Jack)、hello,想咨询一下,仿真进入hardfault(Jack)

HardFault 的触发通常是同步的,但有预取指例外。 非法操作一旦被发现,处理器立即停止当前指令跳转 HardFault,不会"再执行几条语句";但流水线已预取的指令可能先执行、直到非法操作被发现;BusFault 在部分芯片没有独立中断号/悬起位时也会直接升级 HardFault。
HardFault触发真的是同步的吗?举个例子(Jack)

栈回溯原理与操作(精华,收录于 02 目录)。 HardFault 类跑飞无法用"暂停看位置"定位,因为停下的总是 HardFault 处理函数而非故障点。函数调用时 LR 保存返回地址,深层调用会把返回地址压栈;因此:进 debug 看 Core 寄存器组找到 SP,再检查该栈帧内存——函数代码一般在 0x0800xxxx 段,在栈帧内存中找到 08 开头的连续 4 字节,即为其一层的函数返回地址,逐层回溯即可找到故障点。
栈回溯学会了,总结一下:栈回溯解决的是HardFault类问题(Lili_iliLX)

栈回溯以 LR 为主、其他寄存器为辅。 异常时栈中压存的 PC 其实直接指向触发指令,为什么看 LR?因为栈回溯的本质是通过栈帧推调用关系,LR 是串联各层的依据;PC、CPSR、fault status 寄存器仍可作辅助信息。
老师,LR存储的是函数的返回地址,即函数被调用完后的下一条指令地址(Jack)

栈回溯有局限性。 栈回溯只能拿到有限信息;HardFault 意味着严重非法访问、未定义指令或栈溢出,CPU 压栈保存的上下文(PC 等)只是起点,长文详解见原文链接。
老师,关于如何排查hard fault这个问题(山海无名)

进 HardFault 后的排查套路(存根)。 查看 LR-4 处(Thumb 下预取导致 LR 指向异常指令下一条);在 HardFault 处理函数中打印 R0-R12、SP、LR、PC、xPSR 及 CFSR/HFSR/DFSR 等错误状态寄存器。
进入hardfault后怎么排查问题?(立芯嵌入式)

cm_backtrace 与 Ozone 的场景区分。 cm_backtrace 可离线捕获:异常信息存入内部 Flash,重新上电或接调试器时读出分析;Ozone 属在线调试,需要调试器在场。
cm_backtrace也是可以捕获hardfault(Jack)

异常返回是"伪地址"编码。 复位/异常进入 Handler 模式后 LR 被写入 0xFFFFFFF9 之类特殊值,这不是内存地址而是异常返回行为编码(如返回 Thread 模式);启动代码 LDR R0,=__main + BX R0 跳去执行 __main,返回时凭 LR 的编码退出 Handler 进入 Thread 模式跑 main。
在复位异常后,CPU进入handle模式,LR被赋值0xFFFFFFF9(Jack)

SVC 为什么需要 BX R14 手动返回。 大部分内部中断由硬件自动做异常返回;SVC 是软中断,需要软件指令完成回退,所以汇编里会出现 BX R14
老师,这里的BX R14是指啥,通过异常返回值退出异常(Jack)

同步异常与异步异常。 同步异常:错误产生后 CPU 立刻进入处理函数,无论当时在执行代码还是另一个中断;异步异常:CPU 要等当前代码或中断结束后才进入处理函数。
同步异常:异常(错误)产生,CPU立刻进入错误处理函数(Jack)

DSB 与 ISB 屏障指令。 DSB(数据同步屏障):让之前的内存访问(如写外设/NVIC/SCB 寄存器)真正"落地"再往下走;ISB(指令同步屏障):清空流水线里对旧状态的"理解",改中断使能、特权级、向量表、MPU 后通常要跟一条。
ISB:DSB这些指令是干什么的,异常都是同步(Jack)

向 PC 写值必须是奇数(LSB=1)。 Cortex-M 只有 Thumb 状态,分支目标地址 LSB=1 表示 Thumb 执行;若写入 0 视为企图切换 ARM 模式,直接触发 fault。
老师,我不太能理解这里的含义(Jack)

NVIC_SetPendingIRQ 与 STIR 的区别。 前者是 CMSIS 函数,操作 NVIC 挂起位(除内部异常外都可挂起,可挂起可清除,时机由控制器决定);后者写软件触发中断寄存器,立即触发外部中断、用于模拟硬件中断,但不修改挂起位。
老师,NVIC_SetPendingIRQ(IRQn)设置悬起和NVIC-:STIR(Jack)

断点的硬件原理与数量。 调试器打断点靠 FPB(Flash Patch and Breakpoint Unit);注意与 DWT 区分——DWT 是数据观察点(监视某地址被访问即停机)。STM32F411:FPB 断点 6 个,DWT 观察点 4 个。
STM32F411接上调试器能够打断点的原理是什么?能打的断点数量是多少(Jack)

DWT 高级离线断点 vs BKPT。 DWT 离线断点不能在仿真中使用(调试器本身占用 DWT);BKPT 死板(执行到即停);DWT 可设条件断点,如"某变量大于某值就暂停",在高速协议分析、离线复杂问题诊断中非常有用。
老师,使用DWT离线断点的时候不能仿真吗,只能离线运行才能触发(Jack)

多线程下确认队列/事件被接收的三种手段。 1. 打 log;2. 在 vTaskSwitchContext() 里加打印队列信息的代码,精确看每个队列/事件接收情况;3. 非抢占模式下改写 vApplicationTickHook() 打印。
多个线程的时候怎么调试,app里面发事件,但是调试怎么确定任务线程已经收到事件(Jack)

三、日志与工具链

RTT 与 SystemView 可以共存。 默认 SystemView 用通道 1,RTT Viewer 扫描通道 0;只要两者不用同一通道即可。
rttlog 和systemview 是不能共存的把(Jack)

SystemView 有侵入性,ETM Trace 零占用。 SystemView 会占用一点 CPU,更轻的选择是插桩;ETM Trace/TPIU 是 0% CPU 占用,已用于高级系统诊断与电机 us 级采样(国内工具链尚空白,英飞凌有 Aurora Trace,可参考劳特巴赫 Trace 视频)。
sysetmview会破坏原本的任务时许吗@Jack(Jack)

Trace 调试 vs DAP 调试(精华)。 Trace 用 ETM 内核单元(TRACEDx 接口),DAP 调试用 DAP 单元(J-Link/SWD 接口)。单步调试只能跟踪一条路径、占用 AHB 总线拖慢系统、其他路径被触发时可能漏捕,属侵入式调试(经 AHB-AP 干预 CPU);Trace 可跟踪多条路径、不干预运行,属非侵入式调试,适合对已完成的程序做综合分析。
Trace调试和DAP调试(Jack)

SWD 为什么两根线却够用。 SWD 是 ARM 专为自家内核做的协议,去掉了兼容 8051、TriCore 等多内核的冗余,比 JTAG 简洁,单位时间净荷数据更多;JTAG 线多是为通用性,并非更快。
我想问一下,SWD调试是串口通行,可以输入可以输出,那他是半双工(Jack)

JTAG 上下拉电阻不是必须的,要按信号调。 阻值过大导致驱动不足,走线长时 TDO 建立时间不达标会造成连不上;应用示波器实测调整阻值,量产级联下载场景尤其要留心。
JATG的上下拉电阻是必须的吗(Jack)

Keil 仿真界面的汇编可以导出。 生成后在输出路径找到 .dis 后缀文件,用 notepad++ 打开查看。
keil仿真界面的汇编代码怎么导出来?(看图(Jack)

逻辑分析仪可能拉低被测电平。 有些逻辑分析仪内部阻抗低,接上拉按键会把电压拉到 1.1V 左右;接上后先复测 GPIO 电平心里有数,跌太多就外接上拉电阻增强信号。
老师我遇到个问题,我逻辑分析仪去测按键,按键是上拉(Jack)

测程序耗时最简单的办法:GPIO 翻转 + 逻辑分析仪。 在每段程序/节点翻转一个 GPIO,用逻辑分析仪抓波形,各段耗时一目了然。
有时候我们认为程序运行很慢,不知道哪里耗时很多,最简单的办法是什么(Jack)

GPIO 插桩看函数运行轨迹。 在每个函数后判断返回值,用 GPIO 输出高低电平反映运行结果,抓波形即可看到程序实际走向。
程序调试中的插桩: 在每个函(Jack)

调试器看不到变量地址,先把优化降到 O0。 高优化等级下变量被放进寄存器甚至消除,调试器自然找不到地址。
如果调试器中看不到变量地址,可以尝试优化等级关一下。O0(Jack)

局部变量在调试器和 map 文件里都看不到是正常的。 局部变量在栈上,进入其代码块才分配产生,退出即消失,map 文件不会有它的固定地址;仍看不到多半是优化等级的锅。
为什么调试器中看不到局部变量?map文件里也看不到(Jack)

不确定宏是否真的打开,看预编译文件。 IDE(尤其 Keil)对宏状态的判断未必准确;查看预编译输出中该宏是否生效。典型案例:勾选 GNU extensions 会改变 printf 重定向底层函数(fputc → _io_putchar)。
有的时候,若是不确定一个文件的某个宏是否真的打开(有时IDE无法准确判断(Jack)

Shell(人机交互调试器)的适用阶段。 产品试产和运行时测试阶段用得比较多;企业选型按此场景取舍。
产品试产和做一些运行时测试时比(zn 问 / Jack 答)

调试技巧汇总(资源)。 官方开源调试技巧视频汇总 PDF,含 RTT、easylogger 等例程指引。
调试技巧汇总(立芯嵌入式)

Docker 操作与 Debug 记录(资源)。 常用操作与踩坑记录,见附件笔记文档。
Docker使用时的常用操作和Debug记录(Jack)

四、看门狗与稳定性

调试阶段让软件看门狗辅助定位。 对有周期运行要求的轮询非阻塞任务,可在 while 循环放软件看门狗协助开发期定位问题;但看门狗可能干扰调试,需用宏控制开关。
关于操作系统中的看门狗: 轮(Jack)

看门狗复位后如何知道程序卡在哪。 方法一:在看门狗中断里打离线断点,停机后接 J-Link 加载符号表做栈回溯;方法二:移植"看门狗线程"监测哪个线程没喂狗,没喂狗直接停机再栈回溯。
老师,开了看门狗,有办法知道是程序卡在哪里导致看门狗复位(Jack)

外部看门狗的刷新序列必须整体落在超时窗口内。 多字节刷新序列中,第二字节若在超时后才写入,通常视为刷新失败触发复位;避免贴着超时极限刷新。
有大佬知道External watchdog的刷新机制吗?第一个字节在窗口中写入(Jack)

功能完成到向量产,要做的稳定性工作。 稳定性测试 + 不同工况环境测试(高低温、强弱磁),并输出文档与各功能模块工作状态记录。
老师,我这边仪器每个模块功能都实现了,现在在向量产方向走,这个阶段我需要做哪些事(Jack)

正式系统与产测系统的架构。 通常做两块板:一块保留 Trace IO 做 HIL 与低功耗测试,另一块 release 板做集成与发布测试;产测用单元测试供产线验证功能,产品跑正式版本——同一套代码,编译时选择。
请教一下各位 你们的正式系统和产测系统 是混合到一起(Jack)

通信可靠性测试的名字叫注入测试。 对已集成 Modbus/DeviceNet/CANopen 的设备做可靠性验证:硬件用高频电脉冲干扰,软件侧做故障注入测试(思路可参考工装开发板视频)。
你领导说的这个叫注入测试,你看(DemonWu 问 / Jack 答)

五、测试验证

测试体系的分层与意义。 编码规范是写代码时的基本检验;静态检查靠工具协助分析;单元测试与故障注入是测试阶段(发布前)暴露程序自身问题的手段。软件工程中人和环境都是变量,测试驱动开发能让各水平工程师在同一基准上对齐。
UnityTest有什么用?(Jack)

Unity 框架的意义与"框架 vs 自写测试"(多源合并)。 Unity 这类框架帮你节省基础工作,但接口、参数范围的用例覆盖仍要自己设计;进阶可参考 GoogleTest、Jenkins 集成测试(多由测试开发工程师负责)。框架还能复用现成测试 case(如 W25Q 等常见器件的测试集),先初筛基本问题;这是"自己写个函数测功能"做不到的。
Unity的意义? Unit老师,用单元测试框架和自己就写一个函数进行功能测试有什么不一样(Jack)

单元测试的本质是可反复执行的回归测试集。 切换平台或改完 IIC 驱动后跑一遍即可快速验证驱动仍正常工作;它的价值不是拦截 bug,而是环境变化(换芯片、换底层库和驱动)时迅速确认功能完好。
单元测试它后面就是个测试集,随时拿出来用,你比如你切换完平台后(Jack)

面试中单元测试的问法。 一般只问测试覆盖率等概念,不深究,但要求有这个意识。
单元测试这一块会问什么问题呀(Jack)

六、典型案例复盘

案例:内部 Flash 在线调试时写入失败。 现象:直接运行正常,单步/在线调试写 Flash 数据出错。排查:对比两次写入数据 → 确认前 4 字节首次写入失败 → 怀疑解锁延时或干扰。对策与通用结论:写入后立刻回读校验,不匹配则二次写入,再失败返回错误状态。
学生: 老师,你有没有遇到过操(Jack)

案例:屏幕初始化代码写完点不亮。 第一步一定是读屏幕 ID 和状态字,确认通讯链路正常后再写初始化;写入结果再用状态字验证,形成闭环。读命令和寄存器在数据手册里。
老师,我写了屏幕的初始化代码,但是屏幕无法点亮,我应该怎么去排查问题?供电正常(Jack)

案例:加速度传感器"昨天好好的今天不行"。 先在驱动里加复位指令清除传感器原有状态;做运动传感器要有标定测试台架——传感器随地理位置、环境温度变化会有偏差,先定指标分辨是采样不准还是算法问题。
老师们,kxtj3这个加速度传感器有人用过吗?我昨天写的代码可以运动检测(Jack)

案例:预取指导致死机(精华)。 Boot 跳转后零日志、Trace32 显示 Core Power Down,且问题随代码布局变化时隐时现。根因: veneer 的 ldr pc, [pc] 跳转中,Core 把字面量池里"下一个字"误当指令预取,而该伪指令恰好用了 R3(指向未初始化 DDR)作基址,访问非法地址总线卡死;改动代码后同一位置变成 R1(有效地址),就不再复现。交叉验证:把 R3 改有效地址不再卡死,把 R1 改成无效地址立即卡死。教训:预取指(Instruction Prefetch 与 Speculative Data Access)会让"没执行的代码"也能触发总线异常。
记录一个预取指导致死机的坑(海底)

案例:407 工程下载到 411 芯片后 J-Link 连不上。 处理:拨动 Boot0 电平 → 重新上电 → Keil 重新识别芯片 → 全片擦除。原理:错误工程把 AHB 时钟初始化成异常值(168MHz 配置跑在 100MHz 的 411 上),DAP 调试单元时钟紊乱无法通信;Boot0 从内部程序启动会把时钟初始化回合理值。
1.调整boot0电平(若为高,则变低,反之亦然(Jack)

案例:J-Link 下载报驱动签名问题。 按图示进入启动设置,禁用驱动程序签名强制即可。
如果jlink下载时遇到这个问(Jack)

案例:下载器搜不到芯片的系统排查。 0. 接线完整拍照(开发板→线束→电脑端,J-Link 指示灯状态);1. 确认 Type-C 已接线;2. 按 Boot0 再按 Reset、先松 Reset 再松 Boot0 再识别;3. 找 J-Link 商家要专用驱动并记录驱动版本;4. 设备管理器查看 J-Link 设备属性;5. 万用表量 VCC 与 3.3 引脚电压。
如果下载不了搜不到芯片怎么办?(Jack)


来源笔记

未收录说明:提问无回答帖(hardfault 方法求助、mcu 堆溢出检测、mcu 单元测试入门等)、仅图片帖(Debug 设计实验、优化等级与调试关系、unity 接口重定向、IAR build actions、keil 光标)、模板缺失帖(诊断流程模板)共 12 篇已标记 review: 建议隔离老师,位域该不该用?是否需要刻意考虑在不同的平台间或编译环境下所产生的问题 主题属 C 语言可移植性,收录于 C语言与编码规范问答总集