2、I2C协议基础:起始条件、停止条件、数据有效性、字节格式与应答机制
好,咱们正式开始聊I2C协议的核心细节。说实话,很多工程师调不通I2C,往往不是硬件设计有问题,而是对这几个基础信号的理解不够透彻。我当年刚入行时也吃过这个亏——明明示波器上波形看着都对,但设备就是不响应。后来才发现,是起始条件的时序差了那么零点几微秒。
这一节,咱们就把I2C协议的五个基本要素掰开揉碎讲清楚。你搞懂了这些,后面排查故障时心里就有底了。
2.1 起始条件(S)与停止条件(P)
I2C总线在空闲时,SCL和SDA都是高电平。这是前提,你得记住。
起始条件(Start Condition):
当SCL为高电平时,SDA从高电平切换到低电平。这个下降沿告诉所有从机:「注意,要开始通信了!」
停止条件(Stop Condition):
当SCL为高电平时,SDA从低电平切换到高电平。这个上升沿表示:「好了,本次传输结束。」
关键点:起始和停止条件都是由主机(Master)产生的。从机只能被动检测,不能主动发起。
我在项目中遇到过一种情况:某次调试时,从机总是莫名其妙地卡死。用逻辑分析仪抓波形才发现,主机在发送停止条件时,SDA的上升沿不够陡峭,从机根本没识别到。嗯,这里要注意——停止条件的建立时间(tSU:STO)至少需要4.7μs(标准模式100kHz下)。
个人经验:我建议你在写驱动时,起始和停止条件用独立的函数封装。这样后期调试时,可以单独测量这两个信号的时序,方便定位问题。
2.2 数据有效性
I2C的数据采样规则其实很简单,就一句话:
在SCL的高电平期间,SDA上的数据必须保持稳定。
换句话说,SDA只能在SCL为低电平时改变。你想想看,如果SCL高电平时SDA变了,那就会被误判为起始或停止条件。
这个规则我刚开始总觉得多余——不就是个电平变化吗?直到有一次,我调试一个温湿度传感器,读出来的数据总是跳变。查了半天,发现是SDA线上有毛刺,在SCL高电平时恰好触发了一个伪起始条件。从那以后,我设计PCB时都会在SDA和SCL上加小电容滤波,一般10pF到100pF就够用。
| 参数 | 标准模式(100kHz) | 快速模式(400kHz) |
|---|---|---|
| 数据建立时间(tSU:DAT) | ≥ 250ns | ≥ 100ns |
| 数据保持时间(tHD:DAT) | ≥ 0ns | ≥ 0ns |
避坑指南:我曾经遇到过从机要求数据保持时间大于0的情况。虽然I2C规范说tHD:DAT可以为0,但某些芯片(尤其是老款)对保持时间有额外要求。建议你设计时留点余量,比如保持100ns以上。
2.3 字节格式
I2C传输的数据是以字节为单位的,每个字节8位。传输时,高位(MSB)在前,低位(LSB)在后。
一个完整的字节传输流程是这样的:
- 主机在SCL低电平时逐位设置SDA电平
- 每发送完一个位,主机产生一个SCL脉冲
- 从机在SCL高电平时采样SDA
- 重复8次,完成一个字节
- 第9个SCL脉冲用于应答位
说白了,就是8个数据位加1个应答位,一共9个SCL时钟周期。
我记得有一次,一个实习生问我:「为什么I2C不一次传16位?那样不是更快吗?」我笑了笑说:「你想想看,如果一次传16位,那应答机制怎么处理?从机怎么告诉你它准备好了?这就是I2C的设计哲学——简单可靠,而不是追求极致速度。」
2.4 应答机制(ACK/NACK)
应答机制是I2C协议中最巧妙的设计之一。它让主机和从机之间有了最基本的「握手」能力。
应答(ACK):
在第9个SCL脉冲期间,接收方将SDA拉低,表示「收到,请继续」。
非应答(NACK):
在第9个SCL脉冲期间,接收方保持SDA为高电平,表示「没收到」或「不想收」。
重要规则:应答位是由接收方产生的。主机发送数据时,从机产生应答;主机接收数据时,主机自己产生应答。
这里有个容易混淆的点:主机接收数据时,如果还想继续读,就发ACK;如果不想读了,就发NACK,然后发停止条件。我刚开始做项目时,就搞反过——主机读最后一个字节时发了ACK,结果从机又继续发数据,导致总线混乱。
我曾经在一个项目中,用I2C读取一个24C02 EEPROM。每次读到最后,主机发NACK后立即发停止条件,但EEPROM就是不释放总线。后来查手册才发现,这个芯片要求NACK之后至少等待4个SCL周期才能发停止条件。嗯,这就是典型的「芯片不按套路出牌」的情况。
调试技巧:当你发现I2C通信卡死时,先检查应答位。用示波器看第9个SCL脉冲时SDA的电平。如果一直是高电平(NACK),说明从机根本没响应。这时候别急着查硬件,先确认从机地址对不对。
2.5 完整的数据帧格式
把上面这些要素组合起来,一个完整的I2C数据帧长这样:
起始条件 | 7位从机地址 | 读/写位 | 应答位 | 数据字节 | 应答位 | ... | 数据字节 | 非应答位 | 停止条件
举个例子,主机向地址为0x50的从机写入一个字节0x3A:
S 1010000 0 A 00111010 A P
其中:
- S = 起始条件
- 1010000 = 从机地址(0x50左移1位)
- 0 = 写操作
- A = 应答位
- 00111010 = 数据字节0x3A
- A = 从机应答
- P = 停止条件
你可能会问:「为什么地址要左移一位?」其实I2C规范里,地址是7位的,但传输时占8位,最低位是读写标志。所以0x50这个地址,实际发送的是0xA0(写)或0xA1(读)。
我记得第一次用逻辑分析仪抓I2C波形时,看到地址是0xA0,还以为是芯片手册写错了。后来才明白这个左移的套路。说白了,这就是I2C协议为了节省一个位而做的设计。
注意事项:有些从机支持10位地址。10位地址的格式比较复杂,需要两个字节来传输地址。如果你用的芯片是10位地址,记得仔细看数据手册,别按7位地址去算。
好了,这一节的内容就这些。起始条件、停止条件、数据有效性、字节格式、应答机制——这五个要素构成了I2C通信的基石。你把这些搞懂了,后面看时序图、写驱动、排查故障都会轻松很多。
下一节,咱们聊聊I2C的三种传输模式:主机发送、主机接收、以及复合格式。到时候我会结合具体的代码示例,让你看看这些协议要素在实际中是怎么用的。