I2C协议基础:从波形看懂I2C通信
做嵌入式这些年,调试过的通信接口少说也有十几种。但要说哪个协议最常用、也最容易出幺蛾子,I2C排前三。你想想看,一根数据线一根时钟线,两根线就能挂一堆设备,多方便。但方便归方便,一旦出问题,波形乱跳、应答丢失、总线锁死…够你折腾半天。
今天咱们就从最基础的开始,把I2C协议的骨架捋一遍。后面所有实战案例的排查,都离不开这些底层知识。
1. 总线结构:两根线上的“君子协定”
I2C总线就两根信号线:SDA(数据线)和SCL(时钟线)。所有设备都挂在上面,谁想说话,谁就控制时钟和数据。
- SDA(Serial Data):数据线,双向传输。
- SCL(Serial Clock):时钟线,由主机产生。
画个简单的接线图。每个设备都有一个唯一的7位或10位地址。主机发地址,从机应答,谁应答谁就“认领”这次通信。
这里有个要点:空闲时SDA和SCL都是高电平。原因很简单,I2C是漏极开路结构,必须靠外部上拉电阻拉高。如果上拉电阻选不对,波形上升沿就变得很慢,通信就容易出错。我最早做STM32驱动温湿度传感器时,偷懒用了10kΩ上拉,结果在一米长的线缆上怎么都通信不上,换成4.7kΩ就好了。
2. 起始条件和停止条件:通信的“握手”信号
I2C通信靠两个特殊信号来控制数据流的开始和结束。这两个信号在示波器上特征非常明显:
- 起始条件(Start Condition):SCL为高电平时,SDA从高变低。
- 停止条件(Stop Condition):SCL为高电平时,SDA从低变高。
你看,关键都在SCL为高的时候发生跳变。平时数据传输时,SDA只能在SCL为低时改变。这个规则是为了区分“控制信号”和“数据信号”。
逻辑分析仪抓波形时,这两个条件就是通信的“书签”。波形一出来,你先找起始条件和停止条件,就能圈定一帧完整的数据。
3. 数据格式:字节与应答的“拉锯战”
I2C的数据是按字节传输的,但每个字节后面都跟一个应答位。我把它称为“拉锯战”:主机发一串,从机给个回应。
一个典型的I2C写操作时序是这样的(以7位地址为例):
起始条件 | 7位地址 + R/W位 | 应答位 | 8位数据字节 | 应答位 | ... | 停止条件
具体拆开看:
- 地址字节:7位地址 + 1位读写标志(0=写,1=读),实际上发送8位。
- 应答位:第9个时钟周期。SDA被从机拉低表示“应答”(ACK),保持高表示“不应答”(NACK)。
- 数据字节:每个字节也是8位,高位(MSB)先发,紧接着跟一个应答位。
这里我画个时序图,标出每个阶段的含义:
理解了结构,再来看数据格式的几种常见变体:
| 操作类型 | 格式说明 | 典型应用 |
|---|---|---|
| 写操作 | 起始 + 地址(写) + ACK + 数据 + ACK + ... + 停止 | 配置传感器寄存器、写EEPROM |
| 读操作 | 起始 + 地址(写) + ACK + 寄存器地址 + ACK + 再次起始 + 地址(读) + ACK + 数据 + NACK + 停止 | 读取传感器数据、读取存储器内容 |
| 组合操作 | 多次重复起始条件,切换读写方向 | 一次传输完成先写后读(如读寄存器值) |
读操作里有个常见陷阱:主机在最后一个字节后要发送NACK(不应答),告诉从机别再发了。如果主机发送ACK,从机会继续输出数据,导致总线冲突。我记得有次调试一颗加速度计,读出来的数据总多一个字节,分析仪上一看,就是主机在最后多发了ACK。
4. 时钟同步与仲裁:多主机怎么不打架
I2C允许总线上有多个主机。那问题来了,如果两个主机同时拉低SDA怎么办?
I2C的仲裁机制是这样工作的:多个主机可以同时发送数据,但谁先把SDA拉低,谁就“赢”了。因为SDA是线与结构,一旦有主机输出低电平,其他主机读到的SDA就是低,跟自己的输出不一致,就知道自己输了,自动停止发送。
时钟同步则靠SCL的线与特性。所有主机都在SCL上做“线与”,谁想拉低时钟就拉低,其他主机只能等着。这个机制让慢速从机可以拉低SCL来“伸延”时钟,让主机等它准备好。我们管这个叫时钟拉伸(Clock Stretching)。
5. 总结一下
这一章把I2C协议最核心的部分过了一遍。其实说白了就是三件事:
- 怎么开始和结束:起始条件、停止条件,特征是SCL高时SDA跳变。
- 怎么传数据:每个字节8位,高位先发,后面跟应答位。
- 怎么分主从:主机发地址,从机应答,发数据或收数据。
后面几章我们会用逻辑分析仪实际抓波形,把今天讲的东西在屏幕上对号入座。你到时候就会发现,纸上得来终觉浅,看波形才是最快的排查方式。