1. I2C协议基础:起始条件、停止条件、数据帧格式
做逻辑分析仪调试这么多年,我碰到最多的案例就是I2C应答异常。不管你信不信,十个I2C问题里起码有七个出在应答上。但要想真正看懂应答,得先把基础协议啃透。
I2C协议呢,说白了就两根线:SCL(时钟)和SDA(数据)。主设备负责产生时钟,从设备在时钟的配合下收发数据。嗯,这里要注意,两根线都是开漏结构,需要上拉电阻。我个人习惯用4.7kΩ,不过具体阻值跟总线电容有关,这个后面实战再细聊。
1.1 起始条件(Start Condition)
起始条件长什么样?看波形:SCL保持高电平时,SDA从高电平跳变到低电平。
我记得刚入行那会儿,有次写I2C驱动,怎么调都调不通。拿示波器一看,发现起始条件SDA下降的时候SCL还没拉高。你想想看,SCL都没到高电平,从设备根本不认这个起始。后来在那段代码里加了个延迟,问题就解决了。所以啊,时序这玩意儿,差一点就是不行。
1.2 停止条件(Stop Condition)
跟起始条件反着来:SCL高电平时,SDA从低电平跳变到高电平。
这里有个容易踩的坑。我曾经修过一个案子,MCU发完停止条件后立刻释放总线,但SDA上拉电阻太大,上升沿慢吞吞的,SCL都已经变低了SDA还没到高。结果从设备认为总线还占用着,下一条起始条件发送失败。后来换小电阻就正常了。
| 条件 | SCL状态 | SDA跳变方向 | 作用 |
|---|---|---|---|
| 起始条件 | 高电平 | 高→低 | 通知所有从设备:总线要开始传输了 |
| 停止条件 | 高电平 | 低→高 | 通知所有从设备:传输结束,总线释放 |
1.3 数据帧格式
一个完整的I2C数据帧包含以下部分:
- 起始条件 —— 如上文所述
- 从设备地址(7位或10位) —— 7位地址最常见
- 读写位(R/W) —— 0表示写,1表示读
- 应答位(ACK/NACK) —— 从设备每收到一个字节都要回应
- 数据字节 —— 每次8位,MSB先传
- 停止条件 —— 或者重复起始条件
先看这个典型的7位地址写操作帧:
SCL: ▁▁▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▁▁
SDA: ▁▁▁▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▁▁
↑S ↓ A6 A5 A4 A3 A2 A1 A0 W ACK D7 D6 D5 D4 D3 D2 D1 D0 ACK ↑P
Start Addr[6:0] R/W 从应答 Data Byte(8位) 从应答 Stop
看不懂?没关系,我们拆开来讲。
1.3.1 地址字节
起始条件之后,主设备发送的第一个字节就是地址字节。高7位是从设备地址,最低位是读写方向。
- 地址 + 0(写)→ 主设备要发送数据给从设备
- 地址 + 1(读)→ 主设备要从设备读取数据
比如一个传感器的地址是0x48(7位二进制1001000),那么发写的地址字节就是0x90(1001000 0),发读就是0x91(1001000 1)。
1.3.2 应答位
每传完一个字节(8位),接收方都会在第9个SCL周期拉低SDA表示应答(ACK),或者保持高电平表示非应答(NACK)。这个机制非常重要——答不答应,直接决定了通信能不能继续。
1.3.3 数据字节
数据字节的格式和地址字节一样,只是内容变成了真正的数据。每个字节发送完后,接收方必须回应ACK。如果是读操作,最后一个字节主设备会回复NACK,然后发停止条件——告诉从设备“够了,不用再发了”。
1.4 一张图看懂I2C单次传输流程
我把上面讲的内容画成了这张流程图,方便你对照记忆。
看到这张图了吗?每次传输都是从起始条件开始,走地址→应答→数据→应答的循环。应答失败(NACK)直接跳到停止条件。这也是我们后面用逻辑分析仪排查的重点——NACK在哪个环节出现,基本就能锁定问题。
1.5 关于重复起始条件
还有一个常见形式叫重复起始条件(Repeated Start)。就是在没有发停止条件之前,再发一个起始条件。这样做的好处是:总线不会被释放,其他主设备不会插进来。典型应用场景是读某个寄存器的值:先写寄存器地址,然后重复起始+读操作。
好了,I2C协议基础就说到这儿。起始、停止、数据帧格式、应答机制,这些是整个课程的地基。后面的所有案例——应答异常、总线死锁、时钟拉伸——都会反复用到这些概念。建议你最好用逻辑分析仪实际抓几个I2C波形对照着看,比只看书理解深刻得多。