4. I2C 数据传输模式:主设备发送/接收、从设备发送/接收、复合传输(Combined Format)
I2C 总线上的数据传输严格遵循主-从架构,所有传输均由主设备发起并控制时钟信号(SCL)。根据数据传输方向的不同,I2C 协议定义了四种基本传输模式以及一种高效的复合传输模式。理解这些模式是编写正确驱动、调试通信故障以及优化吞吐量的基础。
4.1 传输模式分类与方向控制
I2C 协议通过控制读写位(R/W#)来决定数据流方向。在起始条件(S)后,主设备发送 7 位从设备地址 + 1 位读写标志位:
- R/W# = 0:主设备发送数据(写操作)。
- R/W# = 1:主设备接收数据(读操作)。
| 传输模式 | 方向 | 主设备角色 | 从设备角色 | 典型应用场景 |
|---|---|---|---|---|
| 主设备发送 | 主 → 从 | 发送器(Transmitter) | 接收器(Receiver) | 配置寄存器、发送命令、写入 EEPROM |
| 主设备接收 | 从 → 主 | 接收器(Receiver) | 发送器(Transmitter) | 读取传感器数据、读取状态寄存器 |
| 复合传输 | 主→从 + 从→主 | 发送器 + 接收器 | 接收器 + 发送器 | 先写寄存器地址再读数据(最常用) |
驱动开发提示: 在 Linux 内核中,
i2c_msg 结构体的 flags 字段通过 I2C_M_RD 标志位控制方向。主设备发送时 flags = 0,主设备接收时 flags = I2C_M_RD。
4.2 主设备发送模式(Master Transmitter Mode)
在此模式下,主设备作为发送器,从设备作为接收器。数据从主设备流向从设备。这是 I2C 通信中最基本的写操作流程。
关键点: 主设备发送模式下,从设备通过拉低 SDA 线(ACK)来确认每个字节的接收。如果从设备未准备好或地址不匹配,从设备会在第 9 个时钟周期保持 SDA 为高(NACK),主设备应据此判断错误并发送停止条件。
4.3 主设备接收模式(Master Receiver Mode)
在此模式下,主设备作为接收器,从设备作为发送器。数据从从设备流向主设备。主设备在接收完每个字节后必须发送 ACK 以继续传输,发送 NACK 则通知从设备停止发送。
常见调试陷阱: 主设备接收模式下,如果主设备在最后一个字节后没有发送 NACK 就直接发送停止条件,从设备可能会继续发送数据,导致总线冲突。务必在读取最后一个字节前将 ACK 位设置为 NACK。
4.4 复合传输模式(Combined Format)
复合传输是 I2C 协议中最强大、最常用的模式。它允许在一次总线事务中,不释放总线(不发送停止条件),连续执行一次写操作和一次读操作。这种模式通常用于先向从设备写入寄存器地址,再读取该寄存器的数据。
4.5 Linux 内核中的复合传输实现
在 Linux 内核中,复合传输通过 i2c_transfer() 函数发送一个包含两个 i2c_msg 的消息数组来实现。第一个消息为写方向(flags = 0),第二个消息为读方向(flags = I2C_M_RD)。内核 I2C 核心层会自动处理起始条件、重启条件和停止条件。
#include <linux/i2c.h>
/* 复合传输:先写寄存器地址,再读数据 */
static int i2c_read_reg(struct i2c_client *client, u8 reg_addr, u8 *buf, int len)
{
struct i2c_msg msgs[2];
int ret;
/* 消息 0:主设备发送模式,写入寄存器地址 */
msgs[0].addr = client->addr; /* 从设备地址 */
msgs[0].flags = 0; /* 写方向 */
msgs[0].len = 1; /* 寄存器地址长度 */
msgs[0].buf = ®_addr; /* 寄存器地址数据 */
/* 消息 1:主设备接收模式,读取寄存器数据 */
msgs[1].addr = client->addr; /* 从设备地址 */
msgs[1].flags = I2C_M_RD; /* 读方向 */
msgs[1].len = len; /* 要读取的字节数 */
msgs[1].buf = buf; /* 数据缓冲区 */
/* 执行复合传输,内核自动处理 Sr 重启条件 */
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret < 0) {
dev_err(&client->dev, "复合传输失败: %d\n", ret);
return ret;
}
return 0;
}
性能调优要点: 复合传输模式将两次独立的传输合并为一次总线事务,减少了总线仲裁开销和时钟周期浪费。在批量读取传感器数据时,应始终使用复合传输而非两次独立的
i2c_master_send() + i2c_master_recv() 调用,后者会在两次传输之间插入停止-启动条件,降低总线利用率。
4.6 四种模式对比与选择指南
| 特性 | 主设备发送 | 主设备接收 | 复合传输 |
|---|---|---|---|
| 总线事务数 | 1 次 | 1 次 | 1 次(含重启) |
| 起始条件 | 1 次(S) | 1 次(S) | 2 次(S + Sr) |
| 停止条件 | 1 次(P) | 1 次(P) | 1 次(P) |
| 数据方向 | 单向(主→从) | 单向(从→主) | 双向(先写后读) |
| 典型用途 | 写配置、发送命令 | 读状态、读固定数据 | 读寄存器、读传感器值 |
| 总线效率 | 高 | 高 | 最高(无空闲周期) |
调试建议: 使用逻辑分析仪抓取 I2C 总线波形时,重点关注复合传输中的重启条件(Sr)。如果波形显示在写操作后出现了停止条件(P)再重新启动(S),说明驱动未正确使用复合传输,而是拆分为两次独立传输,这会降低性能并可能引入竞态条件。