3. I2C数据传输格式:单字节读写、多字节读写、重复起始条件
在音频Codec驱动开发中,I2C总线是配置寄存器最常用的物理层协议。理解I2C的数据传输格式,是定位“寄存器写不进去”、“读回全0xFF”等故障的前提。本节从单字节读写出发,逐步深入到多字节传输和重复起始条件(Repeated START),并结合Codec寄存器操作的典型场景进行讲解。
3.1 I2C总线基本时序要素
在分析具体传输格式之前,需要明确I2C总线上的三个基本信号要素:
| 信号要素 | 电气特征 | 功能说明 |
|---|---|---|
| 起始条件 (S) | SCL高电平时,SDA由高→低 | 通知所有从设备:总线开始传输 |
| 停止条件 (P) | SCL高电平时,SDA由低→高 | 通知所有从设备:总线释放,传输结束 |
| 应答位 (ACK/NACK) | 第9个SCL时钟,接收方拉低SDA为ACK | 接收方确认收到字节;NACK表示错误或结束 |
调试要点:使用逻辑分析仪抓取SDA/SCL波形时,首先确认起始条件和停止条件的边沿是否符合规范。许多Codec驱动故障的根源是MCU的I2C控制器在时序上产生了毛刺,导致从设备无法识别起始条件。
3.2 单字节写操作
单字节写是I2C最基础的传输格式,也是音频Codec寄存器配置的核心操作。其完整数据帧如下:
主机发送: S + 7位地址 + W(0) + ACK + 寄存器地址 + ACK + 数据字节 + ACK + P
从机应答: ACK ACK ACK
操作步骤分解:
- 起始条件 (S):主机拉低SDA,启动传输。
- 从机地址 + 写位 (7位地址 + W=0):主机发送8位数据,高7位为从机地址,最低位为0表示写操作。
- 从机应答 (ACK):从机在第9个时钟拉低SDA,确认地址匹配。
- 寄存器地址 (8位):主机发送要写入的Codec内部寄存器偏移地址。
- 从机应答 (ACK):从机确认收到寄存器地址。
- 数据字节 (8位):主机发送要写入寄存器的值。
- 从机应答 (ACK):从机确认数据写入成功。
- 停止条件 (P):主机拉高SDA,结束传输。
故障排查案例:某Codec驱动中,写寄存器0x12时总是失败。抓取波形发现,主机在发送寄存器地址0x12后,从机返回了NACK。原因是该Codec的寄存器地址为16位(高字节+低字节),而驱动只发送了8位地址。修正为发送两个字节的寄存器地址后,写操作恢复正常。
3.3 单字节读操作
单字节读操作比写操作多一个“方向切换”步骤,需要先写寄存器地址,再重新发起读传输。其数据帧格式如下:
阶段1(写寄存器地址):
主机发送: S + 7位地址 + W(0) + ACK + 寄存器地址 + ACK + P(或Sr)
阶段2(读数据):
主机发送: Sr + 7位地址 + R(1) + ACK + 数据字节 + NACK + P
从机发送: 数据字节
操作要点:
- 阶段1结束后,如果使用停止条件 (P)再发起新的起始条件,称为“两次独立传输”。
- 如果使用重复起始条件 (Sr),则两个阶段合并为一个原子操作,中间不释放总线。
- 读数据阶段,主机在第9个时钟发送NACK,表示只读取一个字节后即结束传输。
常见陷阱:许多I2C控制器在单字节读时,如果驱动代码没有正确设置“读完成后发送NACK”标志,主机会错误地发送ACK,导致从机继续发送下一个字节,造成总线冲突。调试时务必检查控制器寄存器中的“ACK控制位”配置。
3.4 多字节读写操作
音频Codec通常包含多个连续寄存器(如滤波器系数、增益表),多字节读写可以显著提高配置效率。多字节传输的核心是:在同一个起始条件之后,连续发送或接收多个数据字节,每个字节后跟一个ACK(读操作最后一个字节为NACK)。
3.4.1 多字节写
主机发送: S + 7位地址 + W(0) + ACK + 起始寄存器地址 + ACK + Data0 + ACK + Data1 + ACK + ... + DataN + ACK + P
从机应答: ACK ACK ACK ACK ACK ACK
多字节写时,从机内部的地址指针会自动递增。例如,向Codec的寄存器0x10写入4个字节,实际会写入0x10、0x11、0x12、0x13四个寄存器。
3.4.2 多字节读
阶段1(写起始寄存器地址):
主机发送: S + 7位地址 + W(0) + ACK + 起始寄存器地址 + ACK + Sr
阶段2(连续读取):
主机发送: Sr + 7位地址 + R(1) + ACK + Data0 + ACK + Data1 + ACK + ... + DataN + NACK + P
从机发送: Data0 Data1 DataN
多字节读的ACK策略:
| 字节序号 | 主机应答行为 | 含义 |
|---|---|---|
| 第1 ~ N-1字节 | 发送ACK | 请求从机继续发送下一个字节 |
| 第N字节(最后一个) | 发送NACK | 通知从机停止发送,准备结束传输 |
性能优化:在音频Codec初始化阶段,如果需要对一组连续的寄存器(如采样率配置、时钟分频器)写入相同的值,使用多字节写可以将I2C总线开销从“每个寄存器约20个时钟周期”降低到“每批寄存器约10个时钟周期”,初始化速度提升近50%。
3.5 重复起始条件 (Repeated START)
重复起始条件(通常记为Sr)是I2C协议中一个关键但容易被忽视的特性。它允许主机在不释放总线(不发送停止条件)的情况下,直接发起一个新的起始条件,从而改变数据传输方向或切换从机地址。
3.5.1 重复起始条件的时序特征
3.5.2 重复起始条件的应用场景
在音频Codec驱动中,重复起始条件主要用于以下场景:
- 寄存器读操作:先写寄存器地址,再读回数据值,中间使用Sr保持总线原子性。
- 多从机切换:在同一个传输序列中,先配置Codec A,再通过Sr切换地址配置Codec B,避免总线空闲时被其他主设备抢占。
- 批量寄存器回读:使用Sr实现“写起始地址→连续读多个寄存器”的原子操作,确保回读数据的连续性。
3.5.3 重复起始条件 vs 停止+起始
| 对比维度 | 重复起始条件 (Sr) | 停止+起始 (P+S) |
|---|---|---|
| 总线占用 | 持续占用,不释放 | 释放总线,其他主设备可介入 |
| 原子性 | 整个传输序列不可中断 | 两次传输之间可能被其他设备打断 |
| 从机状态 | 从机内部状态保持(地址指针不变) | 从机可能复位内部状态 |
| 兼容性 | 所有标准I2C从机均支持 | 所有标准I2C从机均支持 |
| 驱动实现复杂度 | 需要控制器支持Sr模式(通常通过寄存器配置) | 简单,两次独立调用写/读函数 |
调试陷阱:某些低端MCU的I2C外设不支持硬件自动生成重复起始条件,需要驱动开发者通过操作控制寄存器手动产生Sr。如果驱动代码在“写寄存器地址”后直接调用“发送停止条件”再“发送起始条件”,会导致从机地址指针复位,读回的数据永远是寄存器0x00的值。此时应检查I2C控制器的“重复起始使能位”是否置位。
3.6 音频Codec驱动中的典型传输模式
结合上述三种传输格式,音频Codec驱动中最常见的操作模式可以归纳为以下三种:
3.7 常见故障与排查方法
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| 写寄存器后读回全0xFF | 从机地址错误、未使用重复起始条件、寄存器地址位数不匹配 | 抓取波形,检查地址字节和ACK位;确认寄存器地址是8位还是16位 |
| 多字节写只写入了第一个寄存器 | 从机不支持地址自动递增、主机在每字节后发送了停止条件 | 检查Codec数据手册中“地址自动递增”特性;确认驱动未在字节间插入P |
| 读操作返回数据错位 | 多字节读时ACK/NACK顺序错误、从机地址指针未正确设置 | 验证最后一个字节是否发送NACK;确认写寄存器地址阶段使用了Sr而非P |
| 总线挂死(SCL被拉低) | 从机未收到停止条件、时钟拉伸超时、总线竞争 | 检查停止条件是否完整;增加I2C超时处理;使用总线恢复序列(9个时钟脉冲) |
调试工具推荐:在排查I2C传输格式相关故障时,建议使用逻辑分析仪(如Saleae、Kingst)抓取完整的传输波形。重点关注:起始条件边沿、地址字节的7位地址和R/W位、每个字节后的ACK/NACK状态、重复起始条件的SDA边沿是否符合规范。对于音频Codec驱动,建议在驱动初始化函数入口和出口各抓一次波形,对比预期与实际传输序列的差异。