2. I2C协议时序:起始条件、停止条件、数据有效性、字节传输格式、应答机制(ACK/NACK)
I2C协议说白了就是一套“谁说话、谁听话、什么时候说、说完怎么确认”的规矩。刚开始学的时候,我觉得这东西不就是两根线嘛,能有多复杂?结果第一次调EEPROM,示波器抓出来的波形乱七八糟,才发现时序细节一个都不能错。
这一章咱们就把I2C最核心的五个时序要素掰开揉碎讲清楚。你把这些搞明白了,后面写驱动、调bug心里就有底了。
2.1 起始条件(START Condition)
起始条件标志着一次I2C通信的开始。它由主机产生,所有从机都会检测到这个信号并准备接收后续数据。
时序定义:
- SCL保持高电平
- SDA从高电平切换到低电平
- 切换完成后SCL变为低电平
这里有个关键点:SDA的下降沿必须在SCL高电平时发生。我刚开始做的时候,有次代码里SCL和SDA的时序没配合好,SDA下降沿落在了SCL低电平区间,结果从机根本不响应。查了半天才发现是起始条件没满足。
2.2 停止条件(STOP Condition)
停止条件标志着通信结束。主机告诉所有从机:“我说完了,你们可以休息了。”
时序定义:
- SCL先变为高电平
- SDA从低电平切换到高电平
- SDA上升沿必须在SCL高电平时完成
你想想看,起始和停止其实是对称的——一个是高到低的跳变,一个是低到高的跳变,而且都发生在SCL高电平期间。
2.3 数据有效性(Data Validity)
I2C协议规定:SDA上的数据只有在SCL低电平时才能变化,在SCL高电平时必须保持稳定。从机就是在SCL的上升沿采样SDA的。
这个规则其实很直观——SCL高电平是“采样窗口”,SCL低电平是“数据准备时间”。
| SCL状态 | SDA行为 | 说明 |
|---|---|---|
| 高电平 | 必须保持稳定 | 从机在此阶段采样数据 |
| 低电平 | 允许变化 | 主机在此阶段更新数据 |
我记得有次调试一个温湿度传感器,读出来的数据偶尔会跳变。用示波器一看,发现是SDA在SCL高电平时有个毛刺,导致从机采样到了错误值。后来在硬件上加了个小电容滤波,软件上也做了多次读取取平均,才算稳定下来。
2.4 字节传输格式
I2C传输数据的基本单位是字节(8位),而且是从最高位(MSB)开始传。每传完一个字节,紧接着就是一个应答位。
传输顺序:
- 主机驱动SCL产生8个时钟脉冲
- 在每个SCL低电平期间,主机(或从机)更新SDA
- 在每个SCL高电平期间,接收方采样SDA
- 第9个时钟脉冲用于应答
关键点:I2C协议没有规定字节之间的间隔时间。你可以连续发,也可以每发一个字节等一会儿。但要注意,有些从机需要处理时间,发太快了它可能来不及应答。
我习惯在每两个字节之间加一个微小的延时,尤其是跟慢速EEPROM通信的时候。虽然标准I2C可以跑到400kHz甚至1MHz,但实际项目中稳定比速度重要。
2.5 应答机制(ACK/NACK)
应答是I2C协议里最巧妙的设计之一。每传完一个字节,接收方必须用第9个时钟周期告诉发送方:“我收到了”或者“我没收到”。
ACK(应答成功):
- 接收方在第9个时钟周期将SDA拉低
- 表示“数据已收到,请继续”
NACK(非应答):
- 接收方在第9个时钟周期保持SDA高电平
- 表示“我没准备好”或“别再发了”
这里有个容易混淆的地方:谁发数据,谁控制应答时钟?
- 主机发送数据时:从机应答(ACK/NACK)
- 主机接收数据时:主机应答(ACK表示还要继续读,NACK表示够了,停止)
2.6 完整时序图
下面这张SVG图把起始条件、字节传输、应答、停止条件串在一起,你可以对照着看。
2.7 常见问题与避坑
做I2C驱动开发,下面这几个问题我几乎每次都会遇到:
- 总线卡死:从机没释放SDA,导致主机无法产生起始条件。解决办法是发9个以上的时钟脉冲让从机复位。
- 应答超时:从机没拉低SDA。可能是地址错了,或者从机正在忙。我一般会加超时处理,超过一定时间就报错重试。
- 时序不匹配:主机跑太快,从机跟不上。降低I2C时钟频率通常能解决。
我的调试小技巧:手边常备一个逻辑分析仪,抓一次波形就能看出起始条件、数据、应答、停止条件是否都对。比盯着示波器一行行看代码快多了。
嗯,这一章的内容就这些。起始、停止、数据有效性、字节格式、应答——五个要素串起来就是完整的I2C通信过程。你把这些搞清楚了,后面写驱动就是按部就班的事。