2. I2C 数据帧格式:7位地址模式、10位地址模式、读写位、ACK/NACK机制
I2C 总线上的数据传输以帧为单位,每一帧由起始条件、从机地址、读写控制、数据字节和应答位组成。理解数据帧格式是编写 I2C 驱动的基础,直接决定了寄存器配置中地址寄存器、控制寄存器和数据寄存器的操作方式。
2.1 帧结构基础:起始条件与停止条件
SCL 为高电平时,SDA 上的跳变定义总线的开始与结束:
- 起始条件 (S):SCL 高电平,SDA 从高切换到低。
- 停止条件 (P):SCL 高电平,SDA 从低切换到高。
每次传输必须以 S 开始,以 P 结束。中间可以插入重复起始条件 (Sr),用于改变传输方向而不释放总线。
2.2 7位地址模式数据帧
7 位地址模式是最常用的 I2C 寻址方式,一帧完整的数据传输包含以下字节序列:
| 字节序号 | 位域 | 说明 |
|---|---|---|
| 1 | 7 位从机地址 + R/W | 高 7 位为地址,最低位为读写方向 |
| 2 | ACK/NACK | 从机在第 9 个 SCL 周期拉低 SDA 表示应答 |
| 3 ~ N | 8 位数据 + ACK/NACK | 每字节后跟一个应答位,直到停止条件 |
关键点:R/W = 0 表示主机写(发送数据),R/W = 1 表示主机读(接收数据)。从机地址左移 1 位后与 R/W 位拼接,构成第一个字节。
// 7位地址模式下,主机向从机 0x48 写入一个字节 0xA5
// 地址左移1位 + W = 0x90
// 发送序列:S + 0x90 + ACK + 0xA5 + ACK + P
2.3 10位地址模式数据帧
当系统中从机数量超过 127 个时,使用 10 位地址模式。该模式将地址分两个字节发送:
| 字节序号 | 位域 | 说明 |
|---|---|---|
| 1 | 11110 + 地址[9:8] + R/W | 高 5 位固定为 11110,后跟地址高 2 位 |
| 2 | 地址[7:0] | 地址低 8 位 |
| 3 ~ N | 数据 + ACK/NACK | 与 7 位模式相同 |
驱动开发提示:MCU 的 I2C 外设通常通过寄存器位(如 I2C_CR1 中的 ADDR10 位)选择 7 位或 10 位模式。10 位模式下,地址寄存器需要分两次写入。
2.4 读写位与传输方向控制
R/W 位控制总线的数据流向:
- 主机写 (W = 0):主机发送数据,从机接收。主机产生 SCL 时钟,并在每个字节后等待从机 ACK。
- 主机读 (R = 1):主机接收数据,从机发送。主机产生 SCL 时钟,并在每个字节后主动发送 ACK(最后一个字节发 NACK 以通知从机停止)。
组合传输(先写后读)通过重复起始条件实现:
// 典型组合传输:写寄存器地址 + 读数据
// S + 0x90(写) + ACK + REG_ADDR + ACK + Sr + 0x91(读) + ACK + DATA + NACK + P
2.5 ACK/NACK 机制
每个字节传输完成后,接收方在第 9 个 SCL 周期控制 SDA 线:
| 信号 | SDA 状态 | 含义 |
|---|---|---|
| ACK | 低电平 | 接收成功,可以继续传输下一字节 |
| NACK | 高电平 | 接收失败或传输结束,主机应发送停止条件 |
常见驱动 Bug:主机读最后一个字节后未发送 NACK,导致从机继续发送数据,总线卡死。务必在最后一个数据字节的 ACK 周期前配置 NACK 位。
2.6 数据帧结构总览
下图展示了 7 位地址模式下一次完整写传输的帧结构:
2.7 10位地址模式与7位地址模式对比
| 对比项 | 7位地址模式 | 10位地址模式 |
|---|---|---|
| 地址范围 | 0x00 ~ 0x7F(128 个地址,保留 16 个) | 0x000 ~ 0x3FF(1024 个地址) |
| 首字节格式 | 地址[6:0] + R/W | 11110 + 地址[9:8] + R/W |
| 地址字节数 | 1 字节 | 2 字节 |
| 兼容性 | 所有 I2C 设备 | 仅支持 10 位地址的设备 |
| 寄存器配置 | ADDR10 = 0 | ADDR10 = 1 |
2.8 驱动开发中的帧处理要点
- 起始条件检测:轮询模式下等待 SB 标志位,中断模式下在 SB 中断服务程序中处理。
- 地址发送完成:等待 ADDR 标志位,读取 SR1+SR2 寄存器清除标志。
- 数据发送/接收:每字节后检查 TXE/RXNE 标志,轮询或中断触发。
- NACK 处理:收到 NACK 后立即发送停止条件,避免总线死锁。
- 重复起始:在发送 Sr 前设置 START 位,不发送停止条件。
实战建议:在中断驱动中,将帧状态机(SEND_ADDR、SEND_DATA、RECV_DATA、WAIT_STOP)与 I2C 事件标志位绑定,每个中断事件推进状态机一步。轮询驱动则用循环等待 + 超时保护。