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 位)?
1. 设备供电是否正常?
2. 地址引脚(如 ADDR0/ADDR1)是否与原理图一致?
3. 总线是否被其他设备拉死?
4. 是否使用了错误的地址格式(7 位 vs 10 位)?
2.4 完整 I²C 写帧与读帧流程
以下 SVG 图展示了音频 Codec 寄存器配置的典型写帧和读帧结构,包含地址、读写位、ACK/NACK 以及数据字节的时序关系。
2.5 10 位地址扩展格式
当使用 10 位地址时,首字节的前 5 位固定为 11110,后 2 位为地址高 2 位,最低位仍为 R/W。第二字节为地址低 8 位。这种格式在音频 Codec 中较少见,但在多路复用 I²C 总线或复杂音频系统中可能遇到。
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 条件。
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> 确认设备是否在预期地址上应答。如果设备不在列表中,优先检查硬件连接和电源,而不是软件配置。