I2C协议时序详解:起始条件、停止条件、数据有效性、字节格式、应答机制
I2C协议,说白了就是两根线(SCL时钟线、SDA数据线)搞定多个设备之间的通信。很多刚入行的工程师觉得它简单,不就是拉高拉低嘛。但我在项目里见过太多因为时序踩坑的案例——从传感器读回来的数据全是0xFF,或者设备偶尔不响应,十有八九都是时序没搞对。
这一章,咱们把I2C协议的五个核心要素掰开揉碎讲清楚。你拿着逻辑分析仪去抓波形的时候,脑子里得有清晰的时序图做对照。
2.1 起始条件与停止条件
I2C总线空闲时,SCL和SDA都是高电平。这是规矩,谁都不能占线。
起始条件(START Condition):SCL为高电平时,SDA从高电平切换到低电平。
停止条件(STOP Condition):SCL为高电平时,SDA从低电平切换到高电平。
我刚开始用逻辑分析仪抓I2C波形时,总喜欢先找这两个跳变沿。为什么?因为它们是每一帧通信的“起跑线”和“终点线”。
这里有个容易忽略的细节:起始条件之后,总线被认为处于“忙”状态。停止条件之后,总线才恢复“空闲”。如果主设备在发送停止条件之前又发了一个起始条件,这叫“重复起始条件(Repeated START)”。我在调试一个OLED屏幕驱动时遇到过这种情况——主设备想连续读写多个寄存器,中间不释放总线,用的就是重复起始。
2.2 数据有效性
I2C协议对数据采样有严格规定:在SCL时钟的高电平期间,SDA上的数据必须保持稳定。只有在SCL低电平时,SDA才允许变化。
你想想看,如果数据在时钟高电平时跳变,从设备会把它误判成起始或停止条件。这就是为什么很多I2C通信异常,根源在于SDA信号在SCL高电平时出现了毛刺或抖动。
| SCL状态 | SDA允许变化? | 说明 |
|---|---|---|
| 低电平 | 允许 | 数据准备阶段,可以更新SDA电平 |
| 高电平 | 不允许 | 数据采样阶段,SDA必须稳定 |
用逻辑分析仪看波形时,我习惯把SCL的上升沿和下降沿标出来。在上升沿之前,SDA应该已经稳定了至少一个建立时间(tSU:DAT,标准模式最小250ns)。如果发现SDA在SCL上升沿附近还在跳变,那基本可以断定是时序违规。
2.3 字节格式与数据传输
I2C传输数据是以字节为单位的,每个字节8位,高位(MSB)在前。这一点和很多串行协议不一样,比如UART是低位在前。我第一次用逻辑分析仪解析I2C数据时,就因为这个方向搞反了,把地址0x50读成了0x0A,折腾了半天。
完整的字节传输流程是这样的:
- 主设备在SCL上产生8个时钟脉冲
- 在每个SCL脉冲的高电平期间,从设备(或主设备)在SDA上输出一位数据
- 第9个时钟脉冲用于应答位(ACK/NACK)
举个例子,主设备要发送0xA5(二进制1010_0101):
SCL: _-_-_-_-_-_-_-_-_-_
SDA: 1 0 1 0 0 1 0 1 ? (高位在前,?为应答位)
bit7 bit6 ... bit0 ACK
我在项目中习惯用逻辑分析仪的“协议解析”功能自动解码,但说实话,新手阶段我建议你手动数一遍时钟和SDA电平。数过两三个字节之后,你对I2C时序的感觉就完全不一样了。
2.4 应答机制(ACK/NACK)
这是I2C协议里最容易被忽视、却又最关键的部分。每个字节传输完成后,接收方必须在第9个时钟周期拉低SDA表示应答(ACK),或者保持高电平表示非应答(NACK)。
ACK:SDA在第9个时钟低电平期间被接收方拉低,表示“收到,请继续”。
NACK:SDA在第9个时钟保持高电平,表示“没收到”或“不想收”或“传输结束”。
实战经验: 我调试过一个温度传感器,读寄存器时总是返回0xFF。用逻辑分析仪抓波形发现,主设备发送完从设备地址后,从设备根本没有拉低SDA(即没有ACK)。后来查手册才知道,那个传感器的地址是7位,但我代码里写成了8位左移一位的格式,地址对不上,从设备自然不会应答。
NACK在正常通信中也会出现,比如主设备作为接收方时,读完最后一个字节后主动发送NACK,告诉从设备“别再发了,我要结束了”。这是合法的协议行为。
但如果是从设备该应答时没应答(比如地址错误、总线冲突、从设备忙),那就是故障了。逻辑分析仪上看到这种情况,优先检查:
- 从设备地址是否正确(7位还是10位?左移了没有?)
- 从设备是否上电且工作正常
- SCL时钟频率是否在从设备支持范围内
- SDA上拉电阻是否合适(我常用4.7kΩ,但总线电容大时要换小一点的)
2.5 完整通信帧结构
把上面几个要素串起来,一个完整的I2C写操作帧是这样的:
| START | 从设备地址(7位) | R/W(0) | ACK | 寄存器地址(8位) | ACK | 数据字节(8位) | ACK | STOP |
读操作帧稍微复杂一点,中间需要一次重复起始条件和方向切换:
| START | 从设备地址(7位) | R/W(0) | ACK | 寄存器地址(8位) | ACK |
| REPEATED START | 从设备地址(7位) | R/W(1) | ACK | 数据字节(8位) | NACK | STOP |
嗯,这里要注意:读操作时,主设备在最后一个字节要发NACK,而不是ACK。我见过有人在这里写错,导致从设备一直发数据,总线被锁死。
2.6 用逻辑分析仪抓I2C时的关注点
说了这么多理论,最后落地到实际操作。当你把逻辑分析仪的探头夹到SCL和SDA上,开始捕获波形时,我建议你按这个顺序检查:
- 看起始条件:SDA下降沿是否发生在SCL高电平期间?如果下降沿发生在SCL低电平期间,那不是START,只是普通数据变化。
- 数时钟个数:每个字节应该有9个SCL脉冲(8位数据+1位应答)。如果少了或多了,说明时钟同步有问题。
- 检查应答位:第9个时钟时,SDA是否被拉低?如果该应答的地方没应答,优先排查地址和从设备状态。
- 看停止条件:SDA上升沿是否发生在SCL高电平期间?如果上升沿发生在低电平,那不是STOP,只是数据变化。
- 检查总线空闲电平:停止条件之后,SCL和SDA都应该回到高电平。如果有一根线被拉低,说明总线被某个设备占着没释放。
我的调试习惯: 逻辑分析仪的采样率至少设到4倍于SCL频率。比如SCL是100kHz,采样率设400kHz以上。采样率太低,边沿位置测不准,建立时间和保持时间根本算不出来。我一般直接设1MHz,省心。