Bug 故事:两个 DAC 通道并联,我改了 5 个地方才修好(改了5个地方才修好)

发布时间:2026/10/9 8:33:24
Bug 故事:两个 DAC 通道并联,我改了 5 个地方才修好(改了5个地方才修好) tags: 嵌入式,调试,DAC,SPI,多通道,Bug故事 category: 嵌入式 type: original series: debug-troubleshootBug 故事两个 DAC 通道并联我改了 5 个地方才修好改了5个地方才修好一句话: 为了输出更大的电流把 DAC 的两个通道并联使用。写完通道 A 之后忘记同步写通道 B导致只有一半电流输出。这个 bug 藏在 5 个代码位置里——初始化、设定值、递增、递减、协议解析——花了三个小时排完。系列文章《嵌入式调试排错》 — 编译错误、看门狗、DAC踩坑、协议对齐——真实Bug故事排查方法论 一个宏搞定 全网独家解决方案 ✅ 改了5个地方才修好 15条指令找出7处错误背景为什么要并联电路里需要驱动一个负载最大 600mA。但单片 DAC 每个通道只能输出 300mA。硬件同事的办法把两个 DAC 通道的输出引脚焊在一起让它们并联供电。CH3 出 300mACH4 也出 300mA合起来 600mA。原理没问题但固件里埋了一个坑。坑写完通道 A忘了通道 B这个 DAC 是通过 SPI 总线控制的。设定电流值时写一个寄存器就完事// 单通道模式300mA 量程 void DAC_SetCurrent(uint8_t u8Channel, uint16_t u16Code) { SPI_CS_Low(); SPI_Write(u8Channel, u16Code); // 通道 A SPI_CS_High(); }当 CH3 和 CH4 并联使用时你脑子里知道它们是同一个东西。所以写代码的时候自然就写成// 并联模式 — 初始版本有 bug void SOA_SetCurrent(uint16_t u16Code) { SPI_CS_Low(); SPI_Write(CH3, u16Code); // 写完通道 A // 忘了写通道 B SPI_CS_High(); }结果只有通道 A 在输出电流通道 B 还在输出上次的值。负载只拿到一半电流。这个问题本身不难发现——如果代码只有一个地方写电流值的话。为什么修了 5 个地方一个真实的嵌入式项目里设定电流值不是只有一个入口的。我翻了一遍代码发现电流值可以从这几个地方被改变位置场景1初始化— 上电默认值2直接设定— 串口发命令设电流 XXX3递增— 串口发命令电流 104递减— 串口发命令电流 -105协议批量写入— 上位机一口气写多个参数这 5 个位置每个都只写了通道 A没写通道 B。修法是一样的——每个位置加一行写通道 B// 修好之后 SPI_Write(CH3, u16Code); // 通道 A SPI_Write(CH4, u16Code); // 通道 B — 一个字节都不能少修完第 1 个位置用串口直接设定电流对了。修完第 2 个递增也对。修到第 5 个——上位机批量写入——才发现还有一个遗漏。5 个位置全修完三个小时过去了。教训怎么避免这种 bug1. 封装不要撒胡椒如果一开始就把并联写入封装成一个函数void DAC_WriteDualChannel(uint16_t u16Code) { SPI_CS_Low(); SPI_Write(CH3, u16Code); SPI_Write(CH4, u16Code); SPI_CS_High(); }然后所有地方只调这个函数——bug 根本不存在。撒胡椒式地到处写 SPI_Write出了问题只能在每一处补。2. 不要靠脑子记硬件细节CH3 和 CH4 是并联的——这个信息只存在脑子里不在代码里。过三个月换个同事维护他看到SPI_Write(CH3, code)会自然认为这就够了。用宏定义把这个关系显式化#define DAC_CH_SOA_A CH3 #define DAC_CH_SOA_B CH4 #define DAC_SOA_IS_DUAL 1 // 标记这个通道是并联的3. 并联通道应该强制同步最彻底的修法在 DAC 驱动的初始化时标记哪些通道是并联的。写任意一个时自动同步另一个。这样上层代码完全不需要知道并联这件事// 驱动层 — 自动同步并联通道 void DAC_WriteChannel(uint8_t u8Ch, uint16_t u16Code) { SPI_CS_Low(); SPI_Write(u8Ch, u16Code); // 查同步表这通道有没有并联的兄弟 uint8_t u8Partner g_au8SyncPartner[u8Ch]; if (u8Partner ! 0xFF) { SPI_Write(u8Partner, u16Code); // 自动同步 } SPI_CS_High(); }硬件细节封装在驱动层上层代码完全不用关心。总结教训一句话封装并联写入应该是一个函数不是到处撒的 SPI_Write显式化硬件细节写在宏里不要只存脑子里同步机制并联关系应该在驱动层自动处理上层无知这个 bug 不是技术多难而是一个硬件细节泄露到了 5 个代码位置。封装做好的话它根本不会发生。实测对比封装前: 5处代码各自写SPI_Write | 封装后: 1个函数统一处理bug永久消灭有用的话点个收藏下次调试直接用。有问题欢迎评论区交流看到了都会回。下一篇《一个宏搞定》——编译错误、看门狗、DAC踩坑、协议对齐——真实Bug故事排查方法论

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询