2. I2C协议详解:起始条件、停止条件、数据有效性、字节格式与应答机制(ACK/NACK)
I2C(Inter-Integrated Circuit)总线协议由Philips(现NXP)公司设计,是一种同步、多主、半双工的串行通信协议。它仅使用两条双向开漏线——串行数据线(SDA)和串行时钟线(SCL)——即可实现设备间的数据交换。本章节将深入剖析I2C协议的核心时序与数据单元,为后续驱动开发与中断安全编程奠定基础。
2.1 总线空闲状态与电气特性
在深入时序细节之前,必须明确总线的空闲状态。由于SDA和SCL均为开漏(Open-Drain)结构,通过上拉电阻连接至电源电压(Vdd)。
- 空闲状态:当总线无设备驱动时,SDA和SCL均被上拉至高电平(逻辑1)。
- 线-与特性:任何设备将线拉低(逻辑0)时,总线电平即被拉低。这一特性是实现多主仲裁和时钟同步的物理基础。
驱动提示:在嵌入式系统中,上拉电阻的阻值选择(通常为4.7kΩ或10kΩ)直接影响总线上升时间和最大通信速率。高速模式(400kHz以上)通常需要更小的上拉电阻。
2.2 起始条件(START Condition)与停止条件(STOP Condition)
I2C总线上每一次数据传输都以起始条件开始,以停止条件结束。这两个条件是唯一允许SDA在SCL高电平期间发生变化的时刻。
| 条件 | 时序定义 | SCL状态 | SDA状态 | 作用 |
|---|---|---|---|---|
| 起始条件(S) | SCL为高电平时,SDA从高电平切换到低电平 | 高 | 高 → 低 | 宣告总线占用,开始一次新的传输 |
| 停止条件(P) | SCL为高电平时,SDA从低电平切换到高电平 | 高 | 低 → 高 | 释放总线,结束本次传输 |
| 重复起始条件(Sr) | 在未产生停止条件前,再次产生起始条件 | 高 | 高 → 低 | 改变传输方向或切换从机,不释放总线 |
关键点:起始和停止条件必须由主机(Master)产生。重复起始条件(Sr)常用于复合操作,例如在写寄存器地址后立即读取数据,无需停止总线,可避免其他主机介入。
2.3 数据有效性(Data Validity)
I2C协议对数据线上的电平变化有严格的时间约束,以确保数据在时钟高电平期间被可靠采样。
- 数据稳定期:在SCL时钟的高电平期间,SDA上的数据必须保持稳定(不变)。
- 数据变化期:只有在SCL为低电平时,SDA上的数据才允许发生改变(0→1或1→0)。
常见错误:初学者在软件模拟I2C时,容易在SCL高电平期间改变SDA状态(除起始/停止条件外),这会导致从机采样到错误数据或产生协议错误。务必在SCL低电平时更新SDA输出。
2.4 字节格式与数据传输
I2C总线上的数据以字节(8位)为单位进行传输,且遵循高位在前(MSB First)的顺序。
| 字段 | 位数 | 说明 |
|---|---|---|
| 从机地址 | 7位 或 10位 | 标识目标从机设备。7位地址模式下,地址范围为0x00~0x7F。 |
| 读/写位(R/W) | 1位 | 0表示主机写(发送数据),1表示主机读(接收数据)。 |
| 数据字节 | 8位 × N | 实际传输的数据负载,数量不限,直到停止条件或重复起始条件。 |
| 应答位(ACK/NACK) | 1位 | 每个字节后由接收方产生,用于确认字节接收成功。 |
一个完整的I2C数据帧结构如下:
| 起始条件 | 从机地址(7位) | R/W位 | ACK | 数据字节(8位) | ACK | ... | 数据字节(8位) | ACK/NACK | 停止条件 |
2.5 应答机制(ACK/NACK)
应答机制是I2C协议可靠性的核心。每成功传输一个字节(8位数据),接收方必须在第9个SCL时钟周期内产生一个应答位。
2.5.1 应答位产生规则
- ACK(应答,低电平):接收方在第9个时钟周期将SDA拉低,表示“我已成功接收到上一个字节,请继续发送下一个字节”。
- NACK(非应答,高电平):接收方在第9个时钟周期保持SDA为高电平(释放SDA),表示“我无法接收更多数据”或“我未识别到该地址”。
2.5.2 不同场景下的应答行为
| 场景 | 发送方 | 接收方 | 应答行为 |
|---|---|---|---|
| 主机发送地址+写位 | 主机 | 从机 | 从机匹配地址则产生ACK,否则NACK |
| 主机发送数据字节 | 主机 | 从机 | 从机成功接收则ACK;若从机忙或缓冲区满则NACK |
| 主机接收数据字节 | 从机 | 主机 | 主机若想继续接收则ACK;若想停止接收则NACK(随后发送停止条件) |
驱动编程要点:在Linux内核I2C驱动中,
i2c_msg结构体的flags字段用于控制应答行为。例如,在读取最后一个字节时,主机必须发送NACK,否则从机会继续发送数据导致总线挂死。内核中的I2C_M_IGNORE_NAK和I2C_M_STOP等标志位提供了精细控制。
2.6 协议时序总结与对比
下表汇总了I2C协议中关键时序参数(以标准模式100kHz为例),帮助驱动开发者理解硬件时序约束。
| 参数 | 符号 | 最小值 | 最大值 | 说明 |
|---|---|---|---|---|
| SCL时钟频率 | fSCL | 0 | 100 kHz | 标准模式;快速模式可达400kHz |
| 起始条件保持时间 | tHD;STA | 4.0 μs | - | SCL低电平后,SDA下降沿的保持时间 |
| SCL低电平周期 | tLOW | 4.7 μs | - | SCL低电平最小持续时间 |
| SCL高电平周期 | tHIGH | 4.0 μs | - | SCL高电平最小持续时间 |
| 数据建立时间 | tSU;DAT | 250 ns | - | SCL上升沿前,SDA数据必须稳定的时间 |
| 数据保持时间 | tHD;DAT | 0 | 3.45 μs | SCL下降沿后,SDA数据保持的时间 |
| 停止条件建立时间 | tSU;STO | 4.0 μs | - | SCL高电平后,SDA上升沿的建立时间 |
软件模拟建议:在GPIO模拟I2C时,应使用微秒级延时函数(如
udelay())来满足上述时序要求。对于Linux内核中的i2c-gpio驱动,这些时序由udelay和ndelay在i2c_algo_bit_data结构中配置。
2.7 本章小结
- I2C总线通过起始条件(S)和停止条件(P)界定一次传输的边界。
- 数据在SCL高电平时被采样,在SCL低电平时允许变化。
- 每个字节(8位)后紧跟一个应答位(ACK/NACK),由接收方控制。
- ACK表示继续传输,NACK表示终止传输或地址未匹配。
- 理解这些基础时序是编写正确、健壮的I2C驱动(无论是主机还是从机模式)的前提。