「器件无响应」这一行
一句话总结
I2C 只能把线拉低。拉高的工作由电阻完成,所以一半的故障出现在上升沿。
为什么需要它
一个温度传感器直到昨天都还能读到,今天却读不到了。驱动只会说“设备无响应”。这句话把多种原因混为一谈——可能是地址错了,可能是传感器坏了,可能是没有供电,也可能是整条总线被锁死。从波形上看,这四种情况的样子截然不同。
学会读 I2C,就能把驱动里那一行错误,变成哪一位发生了什么事。这就是本模块的目的。
工作原理
共有两根线:SCL 是时钟,SDA 是数据,两者都是开漏的。设备只能用晶体管把线接到地,一旦松开,上拉电阻就会把它拉到电源电压。所以下降很快,上升则很慢,速度由电阻和寄生电容决定。TI 的 I2C 指南先用图示讲解了这种不对称——电阻小则通信快,但耗电更多,电阻大则变慢(SBAA565、SLVA704)。
规则可以概括为两条。数据只在 SCL 为低时才会改变。并且,SCL 为高期间如果 SDA 在变动,那就不是数据,而是一种条件。下降是 START,上升是 STOP。NXP 的规范原文就是这样定义的(UM10204)。
START STOP
SDA ‾‾\____ A6 A5 A4 A3 A2 A1 A0 R/W ACK ____/‾‾
SCL ‾‾‾‾\__/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\__/‾‾‾‾
START 之后是 7 位地址和一位读写位,第九位是 ACK。在这个位置,主设备松开 SDA,由地址匹配的设备代为拉低。如果没有人拉低,由于上拉它会保持高电平,这就是 NACK。也就是说,NACK 不是设备发送的信号,而是什么也没有发生这一事实。这就是为什么地址写错和传感器没有供电时,波形完全相同。
读取传输通常要开始两次。先用写地址传出寄存器编号,不发 STOP,而是发出重复 START,再用读地址重新指定地址。如果中间发出 STOP,其间别的主设备就可能插进来,所以重复 START 相当于宣布“还没结束”。此外,主设备会对最后一个要接收的字节故意发出 NACK。这不是错误,而是“别再发了”的意思。仅凭波形就把最后那个 NACK 报告为故障,是常有的事。
时钟拉伸是从设备不松开 SCL,一直拉住它。意思是还没准备好,请等待,主设备必须等到 SCL 被释放。在波形上,它表现为与主设备产生的其他低电平区间长度明显不同的一个区间。在本课程的采集中,低电平区间的中位数是 5 µs,其中只有一个是 47 µs。查找原因时,关键是问“这个区间是谁造成的”——主设备无法解释自己没有造成的区间。
最后是弱上拉。电阻太大或布线电容太大,上升时间就会变长,在一个位时间内升不到 VIH。在本课程的采集中,SDA 从 10% 到 90% 的上升用了 5.25 µs,而 SCL 是 1.00 µs。在 400 kHz 下,SCL 的高电平区间比这更短,所以 SDA 没有时间去承载 1。用一半电压(1.65 V)来读,数据看起来完好无损,但用 0.7 × 3.3 = 2.31 V 来读,18 个区间里只有一个会被读成 1。同一个文件,不同的阈值,不同的结论。
在现场相遇的样子
有一种总线被锁死的事故。从设备正在发送字节的过程中,如果主设备重新启动,从设备就会停在自己输出 0 的位置,不再松开 SDA。主设备想产生 STOP,但 STOP 需要把 SDA 拉高才能成立,所以无法产生。波形上有 START 却没有 STOP,SDA 长时间保持低电平。重启也解不开,必须晃动几下 SCL,让从设备把剩下的位全部发出去,才能解开。
也会遇到把上拉电阻分散焊在多块板上,合并之后阻值过小的情况。这时上升变快,通信是正常的,但耗电增加,要到电池寿命报告里才暴露出来。上升时间并不是只会往一个方向变坏的值。
下一项测验要确认什么
会问到 START 和 STOP 的定义、ACK 位置上实际发生了什么、为什么最后一个字节的 NACK 是正常的、如何从波形上识别时钟拉伸,以及弱上拉出现在哪个边沿。