2. I2C协议详解:起始条件、停止条件、数据有效性、字节格式与应答机制
I2C(Inter-Integrated Circuit)总线协议由Philips(现NXP)设计,是一种同步、多主从、半双工的串行通信协议。它仅需两根线——串行数据线(SDA)和串行时钟线(SCL)——即可完成设备间的数据交换。理解其底层时序与信号规范,是驱动开发和性能调优的基石。
2.1 总线空闲状态与电气特性
在总线空闲时,SCL和SDA均通过上拉电阻保持高电平。任何设备在开始通信前,必须检测总线是否空闲(即SCL和SDA均为高)。
关键点: 总线空闲状态 = SCL高 + SDA高。所有主设备在发起通信前必须检测此状态,避免总线冲突。
2.2 起始条件(START Condition)与停止条件(STOP Condition)
起始条件和停止条件是I2C通信的“帧头”和“帧尾”,由主设备产生。它们定义了数据传输的边界。
- 起始条件(S): 当SCL为高电平时,SDA从高电平切换到低电平。
- 停止条件(P): 当SCL为高电平时,SDA从低电平切换到高电平。
调试提示: 使用逻辑分析仪抓取波形时,首先寻找SDA在SCL高电平期间的下降沿(起始)和上升沿(停止)。这是判断通信是否开始或结束的最直观标志。
2.3 数据有效性(Data Validity)
I2C协议规定,在数据传输过程中,SDA线上的数据必须在SCL的时钟周期内保持稳定。具体规则如下:
- 数据变化: 仅在SCL为低电平时,SDA允许改变状态。
- 数据采样: 接收方在SCL的上升沿(或高电平期间)对SDA进行采样。
常见错误: 如果SDA在SCL高电平期间发生跳变,会被误判为起始条件或停止条件,导致通信帧错乱。驱动中必须严格保证SCL低电平时才更新SDA输出。
2.4 字节格式与帧结构
I2C协议以字节(8位)为基本传输单位。每个字节后必须跟随一个应答位(ACK/NACK)。完整的通信帧结构如下:
| 字段 | 位数 | 说明 |
|---|---|---|
| 起始条件 (S) | 1 | 由主设备产生,标志传输开始 |
| 7位从设备地址 | 7 | 指定目标从设备(高7位) |
| 读/写位 (R/W) | 1 | 0 = 写,1 = 读 |
| 应答位 (ACK) | 1 | 从设备在第9个SCL周期拉低SDA表示应答 |
| 数据字节 (Data Byte) | 8 | 传输的数据,MSB优先 |
| 应答位 (ACK/NACK) | 1 | 接收方对每个数据字节的应答 |
| 停止条件 (P) | 1 | 由主设备产生,标志传输结束 |
字节传输顺序: 数据字节总是高位在前(MSB first)。例如发送0x4A(二进制0100 1010),SDA上的顺序为:0 → 1 → 0 → 0 → 1 → 0 → 1 → 0。
2.5 应答机制(ACK/NACK)
应答机制是I2C协议可靠性的核心。每个字节传输完成后,接收方必须在第9个SCL时钟周期内给出应答信号。
- ACK(应答): 接收方在第9个SCL周期将SDA拉低,表示“已收到,请继续发送”。
- NACK(非应答): 接收方在第9个SCL周期保持SDA为高,表示“未准备好/传输结束/地址无效”。
驱动调试要点:
- 主设备发送从设备地址后,若收到NACK,通常表示从设备不存在或忙。
- 主设备读取数据时,在最后一个字节发送NACK,然后发送停止条件,通知从设备停止发送。
- 在Linux内核I2C驱动中,
i2c_msg结构体的flags字段控制是否发送NACK。
2.6 协议总结与对比
下表总结了I2C协议中关键信号与数据有效性的关系,便于驱动开发时对照检查:
| 信号/条件 | SCL状态 | SDA动作 | 协议含义 |
|---|---|---|---|
| 起始条件 (S) | 高 | 下降沿 | 开始传输 |
| 停止条件 (P) | 高 | 上升沿 | 结束传输 |
| 数据位传输 | 低 | 允许变化 | 设置数据 |
| 数据位采样 | 高 | 必须稳定 | 读取数据 |
| 应答位 (ACK) | 第9个时钟高 | 被拉低 | 接收成功 |
| 非应答位 (NACK) | 第9个时钟高 | 保持高 | 接收失败/结束 |
性能调优关联: 应答机制直接影响传输效率。如果从设备频繁NACK,主设备需要重试,这会显著降低总线吞吐量。在调试阶段,应优先排查NACK原因(如地址错误、从设备未初始化、时钟拉伸超时等)。