4. I2C数据传输模式:主发送/主接收、从发送/从接收、组合传输(复合帧)

I2C总线上的数据传输严格遵循主从架构,所有传输均由主设备发起并控制时钟信号(SCL)。根据数据传输方向的不同,I2C协议定义了四种基本传输模式:主发送、主接收、从发送、从接收。此外,实际应用中经常需要在一个传输周期内改变数据方向,这就构成了组合传输(复合帧)。理解这些模式是编写正确I2C设备驱动的核心基础。

4.1 四种基本传输模式

在I2C总线上,主设备通过控制SCL时钟和生成START/STOP条件来管理传输。数据线SDA上的每一位数据在SCL高电平时被采样。以下表格总结了四种基本模式下的角色与行为:

模式 主设备行为 从设备行为 方向(相对主设备) 典型应用场景
主发送 生成START、时钟、发送从地址(写位)及数据 接收数据并回复ACK 主 → 从 配置寄存器、写入传感器命令
主接收 生成START、时钟、发送从地址(读位),随后接收数据并回复ACK/NACK 发送数据并等待主设备ACK 主 ← 从 读取传感器数据、读取寄存器值
从发送 作为从设备,响应主设备的读请求,在SCL控制下发送数据 等待主设备发送读地址,然后发送数据 主 ← 从 从设备主动提供数据(如EERPOM读操作)
从接收 作为从设备,响应主设备的写请求,在SCL控制下接收数据 等待主设备发送写地址,然后接收数据 主 → 从 从设备接收配置或数据(如DAC写操作)
驱动开发提示: 在Linux内核中,主发送和主接收模式通常通过 i2c_master_send() 和 i2c_master_recv() 接口实现。而从设备模式则需要注册 struct i2c_algorithm 中的 master_xfer 回调(对于主设备控制器驱动)或 smbus_xfer 回调。

4.2 主发送模式详解

主发送模式是I2C通信中最基本的操作。主设备负责生成所有时序信号,并将数据字节逐个发送到从设备。其典型流程如下:

  1. 生成START条件: 主设备将SDA从高电平拉低,此时SCL为高电平。
  2. 发送从设备地址(7位或10位): 地址的最高位(LSB)为0,表示写操作。
  3. 等待从设备ACK: 从设备在第9个SCL周期将SDA拉低,表示应答。
  4. 发送数据字节: 主设备逐位发送数据,每个字节后等待从设备ACK。
  5. 生成STOP条件: 主设备将SDA从低电平释放至高电平,此时SCL为高电平。
关键时序约束: 主设备在发送每个字节后必须释放SDA总线,以便从设备在第9个时钟周期拉低SDA产生ACK。如果从设备未应答(NACK),主设备应生成STOP条件终止传输。
主发送模式时序图 SCL SDA START 地址+W ACK 数据字节 ACK STOP 主设备控制整个传输过程,从设备仅在ACK阶段驱动SDA

4.3 主接收模式详解

主接收模式用于从从设备读取数据。与主发送模式的关键区别在于:地址字节的LSB为1(读操作),且数据字节的ACK由主设备控制。

  1. 生成START条件。
  2. 发送从设备地址(读位): 地址字节的LSB为1。
  3. 等待从设备ACK。
  4. 接收数据字节: 主设备释放SDA,从设备驱动数据线。主设备在每个字节后发送ACK(表示继续接收)或NACK(表示最后一个字节)。
  5. 主设备发送NACK后生成STOP条件。
常见错误: 在主接收模式下,如果主设备在接收最后一个字节后仍然发送ACK而不是NACK,从设备会继续发送数据,导致总线冲突或数据错位。务必在最后一个字节前设置NACK标志。
主接收模式时序图 SCL SDA START 地址+R ACK 数据(从发) ACK(主) NACK+STOP 主设备在最后一个数据字节后发送NACK,通知从设备停止发送

4.4 组合传输(复合帧)

组合传输(Combined Transfer)是I2C协议中极为重要的特性,它允许在一个传输周期内,不产生STOP条件而改变数据传输方向。最常见的场景是:先向从设备写入一个寄存器地址(主发送),然后立即读取该寄存器的值(主接收),中间只使用一个重复起始条件(Repeated START)。

组合传输的优势: 避免了STOP后再START带来的总线竞争风险,保证了操作的原子性。在Linux内核中,组合传输通过 struct i2c_msg 数组实现,多个消息通过 i2c_transfer() 一次性提交。

组合传输的典型流程如下:

  1. START条件
  2. 发送从设备地址(写位)
  3. 等待ACK
  4. 发送寄存器地址(数据字节)
  5. 等待ACK
  6. 重复起始条件(Sr) — 不产生STOP
  7. 发送从设备地址(读位)
  8. 等待ACK
  9. 接收数据字节(主设备发送ACK/NACK)
  10. STOP条件
组合传输(复合帧)结构图 START 地址+W ACK 寄存器地址 ACK 重复START 地址+R ACK 数据 主发送阶段:写入寄存器地址 主接收阶段:读取寄存器数据 组合传输关键特征 • 整个传输过程中只有一个START和一个STOP,中间使用重复起始条件(Sr)切换方向 • 保证了对从设备寄存器读写的原子性,避免其他主设备干扰

4.5 Linux内核中的组合传输实现

在Linux内核中,组合传输通过 struct i2c_msg 结构体数组和 i2c_transfer() 函数实现。每个 i2c_msg 代表一个独立的传输段,内核I2C核心层会自动处理重复起始条件。

#include <linux/i2c.h>

/* 定义两个消息:先写寄存器地址,再读数据 */
struct i2c_msg msg[2];
unsigned char reg_addr = 0x10;
unsigned char rx_buf[4];

/* 消息0:主发送模式,写入寄存器地址 */
msg[0].addr = 0x50;          /* 从设备地址 */
msg[0].flags = 0;            /* 写标志 */
msg[0].len = 1;
msg[0].buf = &reg_addr;

/* 消息1:主接收模式,读取数据 */
msg[1].addr = 0x50;          /* 从设备地址 */
msg[1].flags = I2C_M_RD;     /* 读标志 */
msg[1].len = 4;
msg[1].buf = rx_buf;

/* 执行组合传输,内核自动插入重复起始条件 */
int ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0) {
    dev_err(&client->dev, "组合传输失败: %d\n", ret);
} else {
    /* 成功读取4字节数据到rx_buf */
}
驱动编写建议: 使用组合传输时,务必确保 i2c_msg 数组中的地址一致(除非是特殊的多地址访问场景)。内核I2C子系统在检测到连续消息的地址相同时,会自动生成重复起始条件;如果地址不同,则会生成STOP后再START,这可能导致非预期的总线状态。

4.6 四种模式对比总结

特性 主发送 主接收 从发送 从接收
时钟控制 主设备 主设备 主设备 主设备
数据驱动方 主设备 从设备 从设备 主设备
ACK控制方 从设备 主设备 主设备 从设备
地址位LSB 0 (写) 1 (读) 1 (读) 0 (写)
内核接口 i2c_master_send() i2c_master_recv() 从设备回调 从设备回调
典型用途 写寄存器/发送命令 读传感器/读寄存器 从设备主动上报 从设备接收配置

掌握这四种基本模式以及组合传输的机制,是理解I2C设备驱动中数据流控制的基础。在实际驱动开发中,组合传输是最常用的操作方式,因为它能够高效、原子地完成“写地址-读数据”这一经典操作序列。