2. I2C数据传输机制:7位与10位地址模式、读写操作流程、ACK/NACK应答机制
I2C(Inter-Integrated Circuit)总线协议的核心在于其精确定义的数据传输机制。理解7位与10位地址模式、完整的读写操作流程以及ACK/NACK应答机制,是编写可靠I2C驱动的基础。本章将深入剖析这些底层细节,为后续的设备树配置与驱动适配打下坚实的理论基础。
2.1 7位与10位地址模式
I2C总线上的每个从机设备都有一个唯一的地址。标准模式使用7位地址,理论上支持112个节点(保留16个地址)。为了扩展地址空间,协议定义了10位地址模式,兼容7位地址总线。
2.1.1 7位地址格式
在7位地址模式下,主机发送的第一个字节(起始条件后)包含7位从机地址和1位读写标志位(R/W)。
关键点: 7位地址范围是 0x00 ~ 0x7F,其中 0x00 为通用呼叫地址,0x01~0x03 为保留地址,0x04~0x77 为可用地址,0x78~0x7F 为保留地址(10位地址扩展用)。
| 位序 | 功能 | 说明 |
|---|---|---|
| Bit 7 ~ Bit 1 | 从机地址 (A6 ~ A0) | 7位从机地址,高位在前 |
| Bit 0 | R/W 标志 | 0 = 写操作 (主机发送数据到从机);1 = 读操作 (主机从从机读取数据) |
2.1.2 10位地址格式
10位地址模式使用两个字节来传输地址。第一个字节的前5位固定为 11110,后2位为10位地址的高2位 (A9 ~ A8),第8位为R/W标志。第二个字节为10位地址的低8位 (A7 ~ A0)。
工程提示: 在Linux内核中,10位地址模式需要显式设置
I2C_M_TEN 标志位。设备树中通常使用 reg = <0x30>; 表示7位地址,而10位地址需要特殊处理,例如 reg = <0x0030>; 或使用 reg = <0x30 0x10>; 等变体,具体取决于控制器驱动实现。
2.2 读写操作流程
I2C的读写操作遵循严格的时序。一次完整的传输以起始条件 (S) 开始,以停止条件 (P) 结束。数据在时钟线 (SCL) 高电平期间采样,在低电平期间变化。
2.2.1 写操作流程
- 主机发送起始条件 (S):SCL高电平时,SDA从高电平切换到低电平。
- 主机发送从机地址 + 写标志 (0):7位地址 + R/W=0。
- 从机发送ACK:从机在第9个时钟周期将SDA拉低,表示应答。
- 主机发送数据字节:每发送一个字节(8位),等待从机ACK。
- 重复步骤4:直到所有数据发送完毕。
- 主机发送停止条件 (P):SCL高电平时,SDA从低电平切换到高电平。
2.2.2 读操作流程
- 主机发送起始条件 (S)。
- 主机发送从机地址 + 读标志 (1):7位地址 + R/W=1。
- 从机发送ACK。
- 从机发送数据字节:从机在SCL驱动下,逐位发送数据。
- 主机发送ACK/NACK:主机每接收一个字节后,发送ACK表示继续读取,发送NACK表示停止读取。
- 重复步骤4-5:直到主机发送NACK。
- 主机发送停止条件 (P)。
常见错误: 在组合传输(Combined Transfer)中,例如先写寄存器地址再读数据,主机在写操作结束后发送重复起始条件 (Sr) 而不是停止条件,然后发送读标志。这是I2C驱动中实现寄存器读取的标准方法。
2.3 ACK/NACK应答机制
ACK/NACK是I2C总线保证数据可靠传输的核心机制。每个字节(8位)传输完成后,接收方必须在第9个时钟周期内发送一个应答位。
2.3.1 应答位定义
| 应答类型 | SDA电平 (第9个SCL周期) | 含义 | 典型场景 |
|---|---|---|---|
| ACK (Acknowledge) | 低电平 (0) | 接收方已成功接收数据,可以继续发送下一字节 | 从机确认地址匹配;主机确认数据写入成功;从机确认数据读取请求 |
| NACK (Not Acknowledge) | 高电平 (1) | 接收方未成功接收数据,或不再接收更多数据 | 从机地址不匹配;从机忙或数据错误;主机读取完最后一个字节后通知从机停止 |
2.3.2 应答在驱动中的处理
在Linux I2C驱动中,ACK/NACK的处理通常由I2C控制器硬件自动完成,但驱动开发者需要理解其状态,以便进行错误处理。
// 示例:Linux I2C消息结构体中的标志位
struct i2c_msg {
__u16 addr; // 从机地址
__u16 flags; // 标志位,如 I2C_M_RD, I2C_M_TEN, I2C_M_NOSTART 等
__u16 len; // 数据长度
__u8 *buf; // 数据缓冲区
};
// 驱动中检查ACK/NACK的典型方式(通过控制器状态寄存器)
// 假设控制器状态寄存器 bit0 表示 ACK 状态
if (readl(controller->base + I2C_STATUS) & I2C_STATUS_NACK) {
dev_err(&adapter->dev, "I2C transfer failed: NACK received\n");
return -EREMOTEIO; // 返回远程IO错误
}
调试技巧: 当I2C通信失败时,使用逻辑分析仪或示波器抓取SDA和SCL波形,观察第9个时钟周期的SDA电平。如果SDA保持高电平(NACK),通常表明从机地址错误、从机未上电或从机忙。如果SDA被拉低(ACK)但后续数据错误,则可能是时钟速率不匹配或总线电容过大。
2.3.3 应答机制与多从机适配
在多从机系统中,ACK/NACK机制用于总线仲裁和冲突检测。当多个从机同时响应时,只有成功将SDA拉低的从机获得总线控制权。驱动开发中,需要确保每个从机在地址匹配后正确发送ACK,否则主机将认为从机不存在并终止传输。