2. I²C 寻址机制:7位地址与10位地址、读写位、从设备应答(ACK/NACK)

在音频 Codec 驱动调试中,I²C 总线是配置寄存器的核心通道。理解寻址机制是故障排查的第一步:地址错误、读写位混淆、应答丢失是导致 Codec 无响应或寄存器写入失败的三大常见原因。本节将深入解析 7 位与 10 位地址结构、读写位控制逻辑以及 ACK/NACK 握手协议。

2.1 7 位地址与 10 位地址

I²C 总线支持两种从设备地址格式:7 位地址(标准模式)和 10 位地址(扩展模式)。音频 Codec 绝大多数使用 7 位地址,但部分高端或复合功能 Codec 可能采用 10 位地址以避免地址冲突。

特性 7 位地址 10 位地址
地址范围 0x00 ~ 0x7F(128 个设备) 0x000 ~ 0x3FF(1024 个设备)
首字节格式 bit[7:1] = 地址,bit[0] = R/W bit[7:5] = 11110,bit[4:3] = 地址高 2 位,bit[0] = R/W
第二字节 无 地址低 8 位
常见 Codec 示例 WM8960 (0x1A), TLV320AIC23 (0x1A) 部分多路复用器或扩展器
驱动配置 直接赋值 7 位地址 需设置 I2C_TENBIT 标志
调试提示: 在 Linux 内核中,使用 i2c_detect 或 i2cdetect 工具扫描总线时,显示的是 7 位地址(左对齐)。如果 Codec 数据手册给出的地址是 8 位格式(含读写位),需要右移 1 位才能得到 7 位地址。例如手册写 0x34,实际 7 位地址为 0x1A。

2.2 读写位(R/W)控制

I²C 帧中,地址字节的最低位(bit[0])用于指示传输方向:

  • 0:主设备写(Master Write)—— 主设备发送数据到从设备。
  • 1:主设备读(Master Read)—— 主设备从从设备接收数据。

音频 Codec 的寄存器配置通常使用写操作,而读取寄存器状态(如故障标志、音量值)则使用读操作。驱动中常见的错误是将读写位错误地包含在地址中,导致从设备无法识别。

关键公式:
I2C_SLAVE_ADDR_7BIT = (Device_Addr_8bit >> 1)
I2C_SLAVE_ADDR_10BIT = Device_Addr_10bit & 0x3FF

2.3 从设备应答(ACK/NACK)机制

I²C 总线使用 ACK/NACK 实现逐字节的传输确认。每次主设备发送一个字节后,从设备必须在第 9 个 SCL 时钟周期拉低 SDA 线(ACK)或保持高电平(NACK)。

信号 SDA 电平(第 9 时钟) 含义 常见故障场景
ACK 低电平 从设备已接收字节,准备继续 正常操作
NACK 高电平 从设备无法接收或结束传输 地址错误、设备掉电、总线冲突
故障排查要点: 如果 Codec 在地址阶段返回 NACK,请检查:
1. 设备供电是否正常?
2. 地址引脚(如 ADDR0/ADDR1)是否与原理图一致?
3. 总线是否被其他设备拉死?
4. 是否使用了错误的地址格式(7 位 vs 10 位)?

2.4 完整 I²C 写帧与读帧流程

以下 SVG 图展示了音频 Codec 寄存器配置的典型写帧和读帧结构,包含地址、读写位、ACK/NACK 以及数据字节的时序关系。

I²C 写帧与读帧结构(音频 Codec 寄存器配置) 写帧(Master Write): START 地址(7bit)+W ACK 寄存器地址 ACK 数据字节 ACK STOP 读帧(Master Read): START 地址(7bit)+W ACK 寄存器地址 ACK RESTART 地址(7bit)+R ACK 数据字节 NACK STOP 图例说明: START / RESTART 条件 地址字节(含读写位) ACK(从设备应答) NACK / STOP 条件 寄存器地址字节 数据字节 注意:读帧中,主设备在最后一个数据字节后发送 NACK,表示不再需要更多数据,随后发送 STOP。 写帧中,每个字节后从设备都必须回复 ACK,否则主设备应终止传输。

2.5 10 位地址扩展格式

当使用 10 位地址时,首字节的前 5 位固定为 11110,后 2 位为地址高 2 位,最低位仍为 R/W。第二字节为地址低 8 位。这种格式在音频 Codec 中较少见,但在多路复用 I²C 总线或复杂音频系统中可能遇到。

10 位地址帧格式 START 11110 + A[9:8] + W ACK A[7:0] 低8位 ACK 数据... STOP 10 位地址注意事项: • 首字节高 5 位固定为 11110,用于与 7 位地址区分。 • 驱动中需设置 I2C_TENBIT 标志,否则主控制器会按 7 位地址解析。

2.6 驱动代码示例:地址配置与读写操作

以下代码展示了在 Linux 内核音频 Codec 驱动中如何正确设置 7 位地址并执行寄存器读写:

/* 音频 Codec 驱动 - I²C 地址配置与读写示例 */

#define CODEC_I2C_ADDR_7BIT  0x1A   /* 从数据手册获取,右移后的 7 位地址 */

/* 寄存器写操作 */
static int codec_reg_write(struct i2c_client *client, u8 reg, u16 val)
{
    u8 buf[3];
    struct i2c_msg msg;
    int ret;

    buf[0] = reg;              /* 寄存器地址 */
    buf[1] = (val & 0xFF);     /* 数据低字节 */
    buf[2] = (val & 0xFF00) >> 8; /* 数据高字节(16位寄存器) */

    msg.addr = client->addr;   /* 7 位地址,由 i2c_client 自动处理 */
    msg.flags = 0;             /* 写操作 */
    msg.len = 3;
    msg.buf = buf;

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret != 1) {
        dev_err(&client->dev, "I2C write failed for reg 0x%02x\n", reg);
        return -EIO;
    }
    return 0;
}

/* 寄存器读操作 */
static int codec_reg_read(struct i2c_client *client, u8 reg, u16 *val)
{
    u8 tx_buf[1];
    u8 rx_buf[2];
    struct i2c_msg msgs[2];
    int ret;

    /* 第一步:写寄存器地址 */
    tx_buf[0] = reg;
    msgs[0].addr = client->addr;
    msgs[0].flags = 0;         /* 写 */
    msgs[0].len = 1;
    msgs[0].buf = tx_buf;

    /* 第二步:读数据(带 RESTART 条件) */
    msgs[1].addr = client->addr;
    msgs[1].flags = I2C_M_RD;  /* 读 */
    msgs[1].len = 2;
    msgs[1].buf = rx_buf;

    ret = i2c_transfer(client->adapter, msgs, 2);
    if (ret != 2) {
        dev_err(&client->dev, "I2C read failed for reg 0x%02x\n", reg);
        return -EIO;
    }

    *val = (rx_buf[1] << 8) | rx_buf[0];
    return 0;
}
调试技巧: 使用逻辑分析仪抓取 I²C 波形时,重点关注:
1. 地址字节是否与预期一致(含 R/W 位)。
2. 每个字节后是否有 ACK 低电平脉冲。
3. 读帧中最后一个字节后主设备是否发送 NACK。
4. 是否有意外的 STOP 或 RESTART 条件。

2.7 常见寻址故障与排查清单

故障现象 可能原因 排查方法
地址阶段 NACK 地址错误、设备未上电、总线冲突 用万用表测 SDA/SCL 电平,核对原理图地址引脚
写数据后 NACK 寄存器地址越界、Codec 处于复位状态 检查寄存器范围,确认 Codec 初始化时序
读数据返回全 0xFF 读帧地址错误、未发送 RESTART 检查驱动是否使用组合事务(combined transaction)
总线死锁(SDA 一直低) 从设备未释放总线、时钟拉伸异常 复位 Codec,重新初始化 I²C 控制器
重要提醒: 在调试音频 Codec 时,如果遇到 I²C 通信失败,不要盲目修改驱动代码。首先使用 i2cdetect -y <bus> 确认设备是否在预期地址上应答。如果设备不在列表中,优先检查硬件连接和电源,而不是软件配置。