2. I2C协议基础:起始条件、停止条件、数据有效性、字节格式、应答机制(ACK/NACK)
I2C(Inter-Integrated Circuit)总线协议由Philips(现NXP)公司设计,是一种同步、多主从、串行通信总线。它仅使用两条双向开漏信号线——串行数据线(SDA)和串行时钟线(SCL),即可实现设备间的数据交换。理解I2C协议的底层时序是编写Linux内核I2C设备驱动的前提,因为驱动最终需要将这些时序转化为寄存器操作或通过I2C适配器抽象层进行收发。
2.1 起始条件(START Condition)与停止条件(STOP Condition)
I2C总线的空闲状态是SCL和SDA均为高电平。每次通信事务都以一个起始条件(S)开始,以一个停止条件(P)结束。这两个条件是总线上的“帧同步”信号,由主设备(Master)产生。
- 起始条件(S): 当SCL为高电平时,SDA从高电平切换到低电平。
- 停止条件(P): 当SCL为高电平时,SDA从低电平切换到高电平。
起始和停止条件具有唯一性,总线上的所有从设备都会检测这两个边沿,从而判断一次传输的开始和结束。在一次传输中,主设备可以发送多个字节的数据,并在不产生停止条件的情况下,通过重复起始条件(Sr)来改变数据传输方向或切换从设备。
2.2 数据有效性(Data Validity)
I2C协议规定,SDA线上的数据必须在SCL时钟的高电平周期内保持稳定。数据位的改变只允许在SCL为低电平时进行。这意味着,在SCL高电平期间,SDA的电平状态就是当前被采样的数据位。
| SCL 状态 | SDA 行为 | 数据有效性 |
|---|---|---|
| 高电平 | 必须保持稳定,不能变化 | 数据位被采样,有效 |
| 低电平 | 允许变化,准备下一位数据 | 数据位无效,正在切换 |
2.3 字节格式(Byte Format)与传输顺序
I2C总线上的数据以字节(8位)为单位进行传输。每个字节传输完成后,必须跟随一个应答位(ACK/NACK)。因此,一个完整的字节传输实际上需要9个SCL时钟周期。
- 位序: 数据从最高有效位(MSB,bit 7)开始传输,到最低有效位(LSB,bit 0)结束。
- 字节数: 每次传输的字节数没有限制,但每个字节后都必须有应答位。
- 地址字节: 起始条件后的第一个字节通常是7位从设备地址(高7位)加1位读写标志位(R/W)。
2.4 应答机制(ACK/NACK)
应答机制是I2C协议保证数据完整性的核心。每个字节传输完成后,接收方(可能是主设备或从设备)必须在第9个SCL时钟周期内拉低SDA线,表示应答(ACK);如果接收方不拉低SDA(保持高电平),则表示非应答(NACK)。
| 信号 | SDA 电平(第9个时钟) | 含义 | 典型场景 |
|---|---|---|---|
| ACK | 低电平 | 接收成功,可以继续发送下一字节 | 从设备确认地址匹配;从设备确认数据写入成功 |
| NACK | 高电平 | 接收失败或传输结束 | 主设备读取数据后发送NACK表示读取结束;从设备忙或无法响应 |
-ENXIO 错误码,表示设备不存在或未响应。
2.5 完整数据传输示例:写操作与读操作
结合上述所有要素,一个典型的I2C写操作序列如下:
- 起始条件(S):主设备产生起始条件。
- 发送地址字节:主设备发送7位从设备地址 + 写标志位(0)。
- 从设备应答(ACK):地址匹配的从设备拉低SDA应答。
- 发送寄存器地址:主设备发送8位寄存器地址(或命令字节)。
- 从设备应答(ACK):从设备确认收到寄存器地址。
- 发送数据字节:主设备发送一个或多个数据字节。
- 从设备应答(ACK):每个数据字节后从设备应答。
- 停止条件(P):主设备产生停止条件,结束传输。
读操作类似,但在发送寄存器地址后,主设备会发送重复起始条件(Sr),然后发送地址字节 + 读标志位(1),之后主设备变为接收方,从设备变为发送方。主设备在读取最后一个字节后发送NACK,表示读取结束。
i2c_master_send() 和 i2c_master_recv() 等API,它们自动处理起始、停止、地址和应答。驱动开发者只需填充 struct i2c_msg 结构体数组,指定标志位(如 I2C_M_RD)即可实现复合传输。
2.6 协议总结与驱动开发要点
| 协议要素 | 硬件行为 | Linux 驱动对应 |
|---|---|---|
| 起始条件 (S) | SCL高时SDA下降沿 | i2c_msg 数组起始,由适配器自动产生 |
| 停止条件 (P) | SCL高时SDA上升沿 | i2c_transfer() 返回后自动产生 |
| 地址 + R/W | 7位地址 + 1位读写标志 | struct i2c_msg.addr 和 flags 中的 I2C_M_RD |
| 数据字节 | MSB优先,8位传输 | struct i2c_msg.buf 指向的数据缓冲区 |
| 应答 (ACK) | 接收方拉低SDA | 由适配器硬件自动处理,驱动检查返回值 |
| 非应答 (NACK) | 接收方保持SDA高 | 返回 -ENXIO 或 -EIO 错误 |
i2c_transfer(),因为它可能睡眠等待硬件完成。应使用 i2c_master_send() 的原子版本或通过工作队列延迟传输。