2. I2C时序单元详解:起始条件、停止条件、数据有效性、字节格式、应答机制
I2C总线的时序,说白了就是一套“谁在什么时候干什么”的规矩。你想想看,一条SCL时钟线,一条SDA数据线,主设备从设备都挂在上面,要是没个统一的时序约定,那不乱套了?
这一节我们把I2C时序拆成五个核心单元来讲。每个单元我都结合自己调试时的踩坑经历,帮你把那些容易忽略的细节抠清楚。
2.1 起始条件(START Condition)
起始条件是什么?就是主设备告诉总线上所有设备:“我要开始通信了,大家注意!”
电气定义:SCL为高电平时,SDA从高电平切换到低电平。
我刚开始学I2C时,总觉得这个定义很简单。直到有一次调试一个温湿度传感器,逻辑分析仪抓出来的波形怎么看都不对。后来发现,是我在SCL还没稳定在高电平时就拉低了SDA——这其实是个“准起始条件”,很多从设备不认。
起始条件的时序参数,我整理了一个表格,方便你对照:
| 参数 | 符号 | 标准模式(100kHz) | 快速模式(400kHz) | 高速模式(3.4MHz) |
|---|---|---|---|---|
| 起始条件保持时间 | tHD;STA | 4.0 μs | 0.6 μs | 160 ns |
| 起始条件建立时间 | tSU;STA | 4.7 μs | 0.6 μs | 160 ns |
2.2 停止条件(STOP Condition)
停止条件和起始条件正好相反。SCL为高电平时,SDA从低电平切换到高电平。从设备看到这个信号,就知道本次传输结束了。
这里有个坑,我曾经踩过:重复起始条件(Repeated START)。它是在没有停止条件的情况下,直接再发一个起始条件。很多新手会误以为必须先发停止再发起始,结果把一次完整的读写操作拆成了两次,中间还被其他设备抢占了总线。
2.3 数据有效性(Data Validity)
数据有效性这个规则,说白了就一句话:SDA上的数据必须在SCL为高电平时保持稳定,只有在SCL为低电平时才能变化。
为什么会这样?因为从设备只在SCL的上升沿或高电平期间采样SDA。如果你在SCL高电平时改变了SDA,从设备可能采到错误的数据,甚至把数据变化误判成起始或停止条件。
我记得有一次调试一个加速度计,读出来的数据总是跳变。用示波器一看,发现是主设备在SCL高电平时SDA有毛刺。后来在SDA上加了一个小电容滤波,问题就解决了。
2.4 字节格式(Byte Format)
I2C传输数据时,每次发8位(1个字节),高位在前(MSB first)。发完8位后,紧接着跟一个应答位。所以一个完整的字节传输,其实是9个SCL时钟周期。
字节格式的组成:
- 7位从机地址:高7位,决定跟谁通信
- 1位读写标志:0表示写,1表示读
- 1位应答位:从设备回应
举个例子,如果主设备要往地址0x50的设备写数据,发送的第一个字节就是:
二进制:1010 000 0
解释:高7位 1010000 = 0x50(从机地址)
第8位 0 = 写操作
第9位 等待从设备ACK
#define ADDR_WRITE(addr) ((addr) << 1) 和 #define ADDR_READ(addr) ((addr) << 1) | 0x01。
2.5 应答机制(ACK/NACK)
应答机制是I2C协议里最巧妙的设计之一。每发完一个字节,接收方要在第9个SCL周期拉低SDA,表示“我收到了,继续发”。如果接收方不拉低(SDA保持高),那就是NACK,表示“我不想要了”或“出错了”。
应答机制有三种常见场景:
- 主设备写,从设备应答:每发一个字节,从设备都要ACK。如果从设备NACK,说明地址不对、寄存器不存在或设备忙。
- 主设备读,从设备发数据:主设备每收到一个字节,要主动发ACK表示“我还要继续读”。当主设备想停止读取时,发NACK,然后发停止条件。
- 从设备NACK:我遇到过最典型的情况是,从设备正在处理内部操作(比如EEPROM在写页),这时它会对主设备的访问NACK。解决办法就是等一会儿再重试。
2.6 完整时序示例:一次写操作
把上面五个单元串起来,一次完整的I2C写操作时序是这样的:
1. 主设备发起始条件(S)
2. 主设备发7位地址 + 写标志(0)
3. 从设备发ACK
4. 主设备发8位数据(寄存器地址)
5. 从设备发ACK
6. 主设备发8位数据(要写的值)
7. 从设备发ACK
8. 主设备发停止条件(P)
用逻辑分析仪看,波形应该是:S → 地址+W → ACK → 数据 → ACK → 数据 → ACK → P。每个字节9个时钟,整个流程清晰明了。