4、I2C控制总线调试:地址、上拉、时序与常见故障

I2C 总线,说白了就是 Codec 芯片的“神经”。你音频通路配得再漂亮,I2C 不通,Codec 就是个哑巴。我调试过的板子,少说有六成音频问题,根因都在 I2C 上。不是地址写错了,就是上拉电阻没选对,再不然就是时序踩坑。

这一章,咱们就把 I2C 调试的四个核心点掰开揉碎讲清楚:地址怎么配、上拉电阻怎么选、时序怎么量、以及那几个最让人头疼的故障——总线锁死、ACK 丢失、时钟拉伸——到底怎么排查。

核心原则:I2C 调试,先看硬件,再抓波形。不要上来就怀疑驱动代码,八成问题是物理层。

I2C 控制总线调试核心知识体系 I2C 总线调试 ① 地址配置 ② 上拉电阻 ③ 时序测量 ④ 常见故障 7位地址 vs 8位地址 ADDR引脚电平配置 阻值计算 上升时间匹配 SCL/SDA 波形 建立/保持时间 总线锁死 ACK丢失/时钟拉伸 调试铁律:先量波形,再改代码。示波器是I2C调试的第一工具。 地址错 → 无ACK;上拉弱 → 上升沿慢;时钟拉伸 → 从设备忙

4.1 I2C 地址配置:第一个坑

拿到一块新的 Codec 板子,我第一件事就是查数据手册的 I2C 地址。你猜怎么着?至少有三回,我发现硬件工程师把 ADDR 引脚拉高拉反了。

Codec 芯片的 I2C 地址,通常由两部分组成:

  • 固定地址位:芯片厂商分配的,比如 0x1A 开头
  • 可配置地址位:通过 ADDR0、ADDR1 引脚的电平决定

举个例子,一颗常见的 Codec,数据手册写着:

7位地址 = 0x1A + (ADDR0 << 0) + (ADDR1 << 1)
ADDR0=0, ADDR1=0 → 0x1A
ADDR0=1, ADDR1=0 → 0x1B
ADDR0=0, ADDR1=1 → 0x1C
ADDR0=1, ADDR1=1 → 0x1D

注意:数据手册里给的地址,绝大多数是 7 位地址。但 Linux 内核和很多 MCU 驱动里,用的是 8 位地址(左移一位)。如果你在驱动里写 0x1A,实际发出去的是 0x34。搞混了,就等着收 NACK 吧。

我个人习惯,拿到原理图先做三件事:

  1. 找到 Codec 的 ADDR 引脚,看是拉高还是拉低
  2. 对照数据手册,算出 7 位地址
  3. 再算出 8 位地址,写在原理图边上

这样后面写驱动、抓波形,心里有底。

4.2 上拉电阻选择:不是随便焊个 4.7k 就完事

很多工程师习惯性在 SDA 和 SCL 上各放一个 4.7kΩ 上拉电阻。说实话,大部分情况下能用。但你要是遇到高速模式(400kHz 以上)或者总线电容比较大的情况,4.7k 可能就不够看了。

上拉电阻的选择,核心就两个约束:

约束条件 计算公式 说明
最小阻值(Rmin) Rmin = (VDD - VOL_max) / IOL_max VOL_max 通常 0.4V,IOL_max 通常 3mA
最大阻值(Rmax) Rmax = Trise_max / (0.8473 × Cbus) Trise_max 由 I2C 标准规定,Cbus 是总线总电容

举个例子,3.3V 供电,标准模式 100kHz,总线电容估算 200pF:

  • Rmin = (3.3 - 0.4) / 0.003 ≈ 967Ω
  • Rmax = 1000ns / (0.8473 × 200pF) ≈ 5.9kΩ

所以 4.7kΩ 在这个例子里是合理的。但如果你把频率提到 400kHz,Trise_max 变成 300ns,Rmax 就掉到 1.77kΩ 了。这时候 4.7k 就不行,得换成 1.5k 左右。

我的经验:量产板子上,我习惯留两个电阻位——一个主上拉,一个备用。万一调试发现上升沿太慢,直接焊小一点的电阻,不用改 PCB。

4.3 时序测量:示波器是照妖镜

说一千道一万,不如抓个波形看一眼。我调试 I2C 从来不看驱动日志,直接上示波器。

抓波形时,重点关注这几个参数:

  • SCL 时钟频率:是不是你设的那个值?很多 MCU 的 I2C 时钟分频器算出来有偏差
  • SDA 上升时间:太慢说明上拉电阻偏大,或者总线电容太大
  • 数据建立时间(tSU:DAT):SCL 低电平到 SDA 数据稳定的时间
  • 数据保持时间(tHD:DAT):SCL 下降沿后 SDA 保持的时间

我记得有一次,板子上的 Codec 偶尔不响应。抓波形发现 SDA 的上升沿足足有 1.2μs,标准模式要求最大 1μs。查了半天,发现是 PCB 走线太长,总线电容到了 300pF。把上拉电阻从 4.7k 换成 2.2k,问题解决。

调试技巧:抓 I2C 波形时,把示波器时基调到 2μs/div 左右,触发在 SDA 下降沿。这样能看到完整的一帧数据:起始条件、地址、ACK/NACK、数据、停止条件。

4.4 常见故障排查

4.4.1 总线锁死(Bus Lock)

这是最让人头疼的问题。现象是 SDA 一直被拉低,SCL 还在跳,但就是发不出数据。

原因:从设备没有释放 SDA 总线。通常是主设备发送了不完整的数据帧,比如中途复位了,或者从设备处理数据时卡住了。

排查方法:

  1. 用示波器看 SDA 是不是一直低
  2. 如果是,手动给 SCL 发 9 个时钟脉冲(很多 MCU 有 I2C 总线恢复功能)
  3. 如果还不行,断电重启

我曾经遇到过一次:Codec 的 I2C 从设备在初始化过程中被主设备复位了,但 Codec 内部状态机还在处理上一笔数据。结果 SDA 被锁死。解决方案是在驱动初始化前,先发一个 I2C 总线恢复序列——9 个 SCL 脉冲加一个 STOP 条件。

4.4.2 ACK 丢失(NACK)

主设备发完地址或数据后,第 9 个时钟周期 SDA 没有被拉低。说白了,从设备没理你。

常见原因:

  • 地址写错了(7位/8位搞混)
  • 从设备没上电或处于复位状态
  • 上拉电阻太小,SDA 拉不下来
  • 从设备不支持该操作(比如写只读寄存器)

排查步骤:

  1. 确认地址对不对(用示波器抓地址字节,自己数一下)
  2. 确认 Codec 供电正常
  3. 确认复位引脚拉高
  4. 换一颗芯片试试(我遇到过芯片本身坏的)

4.4.3 时钟拉伸(Clock Stretching)

有些从设备在准备数据时,会把 SCL 拉低,告诉主设备“你等等,我还没准备好”。这叫时钟拉伸。

大部分 Codec 不支持时钟拉伸。但如果你用的是一颗支持它的芯片,而主设备 I2C 控制器又不支持时钟拉伸,那就会出问题——主设备以为 SCL 已经释放了,实际上还被从设备拉着。

我的建议:选 Codec 时,如果主设备 I2C 控制器比较老(比如某些低端 MCU),尽量选不支持时钟拉伸的 Codec,省心。

快速判断:用示波器看 SCL 低电平时间。如果 SCL 低电平时间明显比主设备发出的长,那就是从设备在做时钟拉伸。这时候要么换主设备,要么在驱动里加超时处理。

4.5 调试流程总结

嗯,说了这么多,最后给个我自己的调试流程,你照着走一遍,八成问题能定位:

  1. 查原理图:确认 ADDR 引脚电平,算出 7 位和 8 位地址
  2. 量上拉:用万用表量 SDA 和 SCL 对地电阻,确认上拉存在且阻值合理
  3. 抓波形:示波器看起始条件、地址、ACK、数据、停止条件
  4. 看时序:检查上升时间、建立/保持时间是否满足规格
  5. 试恢复:如果总线锁死,发 9 个时钟恢复
  6. 换芯片:以上都查了还不行,换一颗 Codec 试试

I2C 调试,说白了就是耐心加细心。波形不会骗人,它告诉你的一切,都是真相。