一句话: 按全功能测试清单从头测,一天排掉三个此前从未暴露的隐藏bug——命令编码位序错、纯读命令被状态机吃坏、判稳逻辑被使能标志门控拉回。三条方法论:分层排查、边界测试、测试清单驱动。
适合谁读:调试中"现象看着像硬件、其实是软件"经历不够多的嵌入式工程师。
背景:一块激光驱动板的全量测试
一块激光光源驱动板(电流DAC + 多通道ADC + 双路温控),功能:恒流驱动、恒功率控制、双路温控、串口协议 + 串口屏交互。
按全功能测试清单从头测,一天内排掉三个此前"从未暴露"的 bug。
方法论一:现象像硬件问题,先分层排查,别急着拆板
现象:监控读数完全不跟手——设电流 0~300mA 阶梯,读数几乎不动;切参考电压/电源轨这些已知电压通道,读数全收敛到同一值;万用表测监控引脚恒 3.29V。
第一反应:15kΩ 输出阻抗被 ADC 采样拉偏?连线断了?换了块板子,现象一样。
正确做法:分层排查,逐层坐实——
第1层 通路: 发两条相同指令, 第二条回读第一条内容 → 回读内容对上, 芯片活着, SPI 通 第2层 接收: 命令确实被芯片接收 第3层 语义: 全码扫描, 所有控制码发一遍, 记录每个码的读数 → 两个不同的码读数完全相同 → 芯片只认数据字节低 5 位 → 我的编码把 MUX 码放到了帧高位 (数据手册位序图读歪了)根因:MUX 码放错位——发出去的"参考电压选择"实际选中了电源通道(3.3V),所以万用表恒测 3.29V。芯片和硬件从头到尾都是好的。
经验:
- "芯片没响应"分三层排查:通路 → 接收 → 语义,逐层坐实再动硬件
- 手册位序拿不准,全码扫描反推最可靠——用"两个不同码解析到同一通道 → 读数相同"这类特征反推真实位序,零猜测纯实证
方法论二:边界条件永远值得单独测
坑 A:纯读命令静默无应答
协议帧:帧头 长度 命令 读写标志 数据 校验。接收状态机有个优化:纯读标志的某个值(表示无数据)时跳过数据段直接等校验。
读版本/序列号这些命令按文档发另一个标志值——结果整帧被状态机吃坏,静默无应答。因为那个值走了"数据接收"路径,把校验字节当数据收了。
读标志=0x02 + 无数据 → 跳数据段 → 正常 ← 状态机只认这个 读标志=0x00 + 无数据 → 进数据段 → 帧被吃坏 ← 文档写的是这个!修法:状态机兼容两种读标志值且无数据 → 跳数据段。文档不用改,上位机不用改,固件兼容两种值。
坑 B:判稳逻辑被使能标志门控拉回
发光序列状态机:预热 → 判稳 → 运行 → 待机。上板测试发现序列永远卡在第一步——进入预热态 0.6 秒后状态被拉回空闲。
根因:判稳函数用"使能标志"做门控(标志为 0 就置空闲),而状态机只拉了 GPIO 使能脚、忘了同步设使能标志。判稳函数每 100ms 把状态清回空闲,序列卡死超时。
状态机: 预热态 (GPIO拉高) + 状态=加热中 判稳函数: if (使能标志 != 1) 状态=空闲 ← 标志没设, 每周期被拉回!修法:状态机使能时同步设使能标志,回空闲时清 0。这个 bug 让发光序列功能从上线起就没真正工作过,直到有完整测试环境才暴露。
经验:边界值(读标志的两种值、长度最小/最大、非法值、使能标志联动)必须进测试清单。正常路径跑通 ≠ 功能可用。
方法论三:测试清单驱动,改动后必须用最新固件测
这次全量测试的价值不在于"测了多少",而在于系统化:
| 做法 | 价值 |
|---|---|
| 测试指令独立编号(50 号以后)与正式协议隔离 | 测试命令不会污染给上位机的协议 |
| 全码扫描/电流阶梯/温度曲线脚本化 | 可重复、可回归 |
| 破坏性测试(Flash 擦写)先备份后测、测完恢复 | 板子测完还能用 |
| 测试项每项都有依据和预期 | 结果可判断 |
一个反例:中途有个改动(超时参数)提交了但没烧录,后续测试跑在旧固件上——虽然测的项目不受影响,但流程上不对。测试必须用与代码仓库同步的最新固件,否则测试结果没有意义。
沉淀
| 教训 | 一句话 |
|---|---|
| 分层排查 | 通路 → 接收 → 语义,逐层坐实再怀疑硬件 |
| 全码扫描反推 | 手册位序不确定时,用实证反推替代猜测 |
| 边界测试 | 标志值/长度边界/标志联动,单独列测试项 |
| 测试清单驱动 | 指令编号隔离、脚本化、可回归 |
| 最新固件测试 | 提交→烧录→测试,顺序不能乱 |
实测对比:正常路径测试:三个隐藏bug从未暴露 | 全量测试清单:一天排掉三个,5批次66/67项通过
有用的话点个收藏,下次调试直接用。有问题欢迎评论区交流,看到了都会回。
下一篇:脚本明明写对了,为什么就是匹配不到?\r\n 和 \n 的坑——跨平台脚本的行尾陷阱