2. I2C总线基础:I2C总线协议简介、I2C时序图、I2C地址与读写操作、Linux内核I2C子系统框架
2.1 I2C总线协议简介
I2C(Inter-Integrated Circuit)总线是由Philips公司开发的一种同步、多主从、串行通信总线。在音频Codec驱动开发中,I2C是最主流的控制总线,用于Host CPU对Codec内部寄存器进行读写配置。音频数据流则通常由I2S或PCM总线承载,两者分工明确。
I2C总线仅需两根线:
- SCL(Serial Clock):由主设备驱动的时钟线,决定通信速率(标准模式100kHz,快速模式400kHz,高速模式3.4MHz)。
- SDA(Serial Data):双向数据线,用于传输地址、读写标志、寄存器地址和数据。
关键概念: I2C是开漏+上拉电阻架构。SCL和SDA在空闲时均为高电平;任何设备都可以将线拉低。这种设计支持多主设备仲裁,但音频Codec通常只作为从设备。
2.2 I2C时序图
理解I2C时序是调试Codec驱动的基础。一次完整的I2C写操作(向Codec寄存器写入数据)包含以下阶段:
调试技巧: 使用逻辑分析仪抓取SCL/SDA波形时,重点关注START/STOP条件是否完整、ACK位是否被从设备拉低。如果ACK位一直为高(NACK),说明从设备地址错误或设备未上电。
2.3 I2C地址与读写操作
每个I2C从设备都有一个7位设备地址(部分设备支持10位地址,但音频Codec几乎都使用7位)。读写操作由第8位(R/W位)决定:
| 操作 | R/W位 | 发送序列 | 典型场景 |
|---|---|---|---|
| 写操作 | 0 | START + 设备地址(7bit) + W(0) + ACK + 寄存器地址(8bit) + ACK + 数据(8bit) + ACK + STOP | 配置Codec采样率、增益、静音等寄存器 |
| 读操作 | 1 | START + 设备地址(7bit) + W(0) + ACK + 寄存器地址(8bit) + ACK + 重复START + 设备地址(7bit) + R(1) + ACK + 数据(8bit) + NACK + STOP | 读取Codec状态寄存器、故障标志 |
常见陷阱: 许多Codec的7位设备地址是左对齐的(如0x1A左移1位后为0x34),而Linux内核I2C子系统使用右对齐的7位地址。在设备树或板级代码中填写地址时,务必确认Codec数据手册给出的地址格式。例如:WM8960的7位地址为0x1A(二进制 0011010),在Linux中应写为0x1A,而不是0x34。
2.4 Linux内核I2C子系统框架
Linux内核I2C子系统采用分层架构,将I2C控制器驱动(适配器)与I2C设备驱动(客户端)解耦。音频Codec驱动位于客户端驱动层,通过标准API与I2C核心交互。
2.5 音频Codec驱动中的I2C操作流程
在音频Codec驱动中,I2C读写通常封装在regmap框架中。以下是标准操作流程:
- 设备树匹配:驱动通过
of_match_table匹配Codec节点,获取I2C设备地址。 - I2C设备注册:
i2c_probe()回调中,分配struct i2c_client并初始化。 - regmap初始化:使用
devm_regmap_init_i2c()创建regmap实例,指定读写寄存器格式。 - 寄存器读写:通过
regmap_write()/regmap_read()操作Codec寄存器,regmap自动处理I2C传输。 - 批量初始化:使用
regmap_multi_reg_write()一次性写入Codec初始化序列。
推荐实践: 在音频Codec驱动中,始终使用regmap框架而非直接调用
i2c_master_send()/i2c_master_recv()。regmap提供缓存、批量操作、调试文件系统(regmap debugfs)等能力,极大简化故障排查。
2.6 常见I2C故障排查清单
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| I2C传输返回-ENXIO | 设备地址错误、设备未上电、SCL/SDA线序反 | 使用i2cdetect扫描总线,确认设备地址;检查原理图 |
| ACK位丢失(NACK) | 从设备忙、寄存器地址越界、时钟拉伸超时 | 逻辑分析仪抓波形;降低I2C速率至100kHz测试 |
| 寄存器写入无响应 | Codec处于复位状态、电源未稳定、MCLK未提供 | 检查Codec电源时序;确认MCLK频率正确 |
| regmap读回全0或全F | I2C总线被拉死、从设备地址偏移错误 | 测量SCL/SDA电平;检查设备树中地址格式 |
内核调试接口: 挂载debugfs后,查看
/sys/kernel/debug/regmap/<i2c-bus>-<addr>/目录,可以dump寄存器缓存、统计I2C传输次数和错误计数,是定位Codec寄存器问题的利器。