4、总线调试:I2C总线卡死与仲裁、SPI时序违规、UART波特率偏差、CAN总线错误帧分析

总线调试,说白了就是跟波形打交道。我见过不少工程师,代码写得飞起,一上逻辑分析仪就懵了。今天咱们就把四种最常用的总线——I2C、SPI、UART、CAN——挨个捋一遍,看看它们卡死的时候长什么样,怎么快速定位。

核心思路:先看波形对不对,再看协议规不规范,最后查电气参数。别一上来就怀疑芯片坏了。

总线调试知识体系 I2C 总线 SPI 总线 UART 总线 CAN 总线 SCL 卡死 仲裁丢失 时序违规 相位/极性 波特率偏差 帧格式错 错误帧 位填充 调试三板斧 ① 看波形:电平、边沿、毛刺 ② 对协议:地址、数据、校验 ③ 查参数:上拉、电容、分频

4.1 I2C 总线卡死与仲裁

I2C 卡死,十有八九是 SCL 或 SDA 被拉低了。我遇到过最典型的情况:从机没释放总线,主机还在傻等。你拿示波器一看,SCL 时钟还在跑,SDA 一直低——嗯,这就是典型的「总线锁死」。

常见卡死原因:

  • 从机未响应 ACK,主机继续发时钟——SDA 被从机拉低不放
  • 多主机同时发起传输,仲裁失败后未释放总线
  • 上拉电阻太大,上升沿太慢,被误判为低电平

怎么快速定位?我个人习惯先看 SCL 是否还在跳。如果 SCL 停了,大概率是主机卡在某个状态机里。如果 SCL 还在跑,SDA 不动,那就是从机没释放。这时候可以试试发 9 个额外的时钟脉冲——很多从机收到后会自动释放总线。

避坑指南:我曾经在一个项目里调了两天,最后发现是上拉电阻用了 10kΩ,总线电容太大,上升沿超过 1μs。换成 4.7kΩ 就好了。I2C 的上拉电阻不是随便选的,400kHz 模式下一般用 2.2kΩ~4.7kΩ。

仲裁丢失怎么查?多主机场景下,仲裁丢失是正常现象。但如果你发现某个主机老是仲裁失败,那就要看它的 SDA 输出驱动能力是不是偏弱。我一般会在逻辑分析仪上同时抓两个主机的波形,对比它们拉低 SDA 的时刻——谁先拉低谁赢,输的那个就得重试。

现象 可能原因 快速检查方法
SCL 停振 主机状态机卡死 复位主机,看波形恢复
SDA 一直低 从机未释放总线 发 9 个额外时钟
数据错位 上拉电阻不合适 测上升沿时间
频繁仲裁失败 驱动能力不匹配 对比各主机 SDA 波形

4.2 SPI 时序违规

SPI 的问题,说白了就是「对不上暗号」。主从机的 CPOL(时钟极性)和 CPHA(时钟相位)必须一致,否则数据全错。我见过有人把模式 0 和模式 3 混着用,数据看起来有模有样,但就是不对——你想想看,采样沿都反了,能对吗?

SPI 四种模式速查:

  • 模式 0:CPOL=0, CPHA=0 — 空闲低电平,上升沿采样
  • 模式 1:CPOL=0, CPHA=1 — 空闲低电平,下降沿采样
  • 模式 2:CPOL=1, CPHA=0 — 空闲高电平,下降沿采样
  • 模式 3:CPOL=1, CPHA=1 — 空闲高电平,上升沿采样

时序违规的典型症状:数据偶尔对、偶尔错,或者第一个字节对、后面全错。我遇到过一个案例,从机要求 CS 拉低后至少等 5μs 才能发时钟,主机没等,结果前两个 bit 总是错的。用示波器一量,CS 下降沿到第一个时钟上升沿只有 1μs——嗯,这就是 setup time 违规。

SPI 调试要点:

  • 先确认 CPOL/CPHA 是否匹配——看数据手册,别猜
  • 检查 CS 到第一个时钟的建立时间
  • 最后一个时钟到 CS 释放的保持时间
  • 时钟频率别超过从机上限——尤其是长走线时

我个人习惯用逻辑分析仪抓一整帧,然后放大看每个边沿。如果 MISO 上的数据在采样沿附近跳变,那就是时序裕量不够。可以试着降低时钟频率,或者调整相位。

4.3 UART 波特率偏差

UART 的问题,八成跟波特率有关。两个设备波特率差 2% 以内一般能工作,超过 3% 就开始丢字节了。为什么会这样?因为 UART 是靠起始位同步的,后面每个 bit 都在累积误差。到第 8 个数据位的时候,采样点可能已经偏到 bit 边界上了。

怎么算误差?比如你设了 115200,实际测出来是 113000,误差就是 (115200-113000)/115200 ≈ 1.9%。这个值在安全范围内。但如果实际是 110000,误差 4.5%,那肯定要丢数据。

避坑指南:我曾经用了一个内部 RC 振荡器的 MCU,标称 16MHz,实际只有 15.2MHz。UART 波特率设 115200,结果丢包率 30%。换成外部晶振就好了。所以,对波特率精度要求高的场合,别用内部 RC。

快速定位方法:用示波器量 TX 引脚,看一个 bit 的宽度。比如 115200 的 bit 宽度应该是 8.68μs。如果量出来是 9.0μs,那实际波特率就是 111111,误差 3.6%——已经到临界了。

标称波特率 bit 宽度(理论) 误差 3% 时实际宽度 建议
9600 104.17μs 107.29μs 内部 RC 可用
115200 8.68μs 8.94μs 建议外部晶振
921600 1.085μs 1.118μs 必须外部晶振

另外,别忘了检查数据格式:数据位、停止位、校验位。我见过有人设了 8N1,从机设的是 7E1——能通才怪。

4.4 CAN 总线错误帧分析

CAN 总线调试,最头疼的就是错误帧。CAN 协议本身有很强的错误检测机制,但这也意味着——一旦出错,节点会不停重发,直到总线被「错误被动」甚至「总线关闭」。

错误帧长什么样?正常 CAN 帧后面跟着 ACK 槽和 EOF。错误帧会在任意位置插入 6 个显性位(主动错误标志),然后总线进入间歇场。你在示波器上看到的就是——好好的数据帧突然被一堆显性位打断。

常见错误类型:

  • 位错误:发送节点监控到总线电平与自己发送的不一致
  • 填充错误:连续 5 个相同电平后未出现反码
  • CRC 错误:接收节点计算的 CRC 与发送的不匹配
  • 格式错误:帧格式不符合规范
  • ACK 错误:没有节点在 ACK 槽拉低总线

怎么分析?我个人习惯用 CAN 分析仪抓总线日志,看错误计数器的变化。如果某个节点的发送错误计数器(TEC)一直在涨,那问题大概率出在这个节点上。如果接收错误计数器(REC)涨,那可能是总线干扰或者别的节点在发坏数据。

注意:CAN 总线两端必须加 120Ω 终端电阻。我遇到过好几次,错误帧满天飞,最后发现是终端电阻没焊。没有终端电阻,信号反射会把波形搞得一塌糊涂。

位填充错误怎么查?CAN 协议规定,连续 5 个相同电平后必须插入一个反码。如果发送节点没插,或者接收节点误判了填充位,就会报填充错误。这种问题通常跟时钟精度有关——CAN 要求每个节点的时钟误差在 ±1.5% 以内。你想想看,如果两个节点时钟差太多,采样点偏移,就可能把数据位误判成填充位。

嗯,总线调试说到底就是「看波形、对协议、查参数」。I2C 卡死看 SCL/SDA,SPI 违规看边沿对齐,UART 丢数据量 bit 宽度,CAN 错误帧看错误计数器。把这些基本功练熟了,大部分总线问题都能在半小时内定位。