2、I2C 总线协议:物理层、条件、地址与读写位、应答机制
这一章咱们聊 I2C 总线的底层细节。别小看这些基础,我当年调第一块 I2C 传感器时,就因为没搞懂应答机制,浪费了一整天。
物理层:SCL 与 SDA
I2C 总线就两根线:SCL(时钟)和 SDA(数据)。两根线都是开漏输出,必须外接上拉电阻。
- SCL:由主设备驱动时钟信号。频率有 100kHz(标准模式)、400kHz(快速模式)等。
- SDA:双向数据线,主设备发送或从设备应答时驱动。
- 空闲状态:两根线都被上拉至高电平。
避坑提醒:上拉电阻阻值别乱选。我习惯用 4.7kΩ,但总线电容大时用 2.2kΩ 更稳。你想想看,阻值太大信号边沿会变缓,容易误判。
物理层还有一点:每个设备有一个唯一地址。那个地址是 7 位或 10 位,但我见过的器件基本都是 7 位。嗯,地址分配得小心,别跟板子上其他芯片撞车。
起始条件与停止条件
老规矩,先说条件怎么判断。
- 起始条件:SCL 为高时,SDA 从高电平切换到低电平。这表示总线开始传输。
- 停止条件:SCL 为高时,SDA 从低电平切换到高电平。这表示传输结束。
讲个我踩过的坑:有一次我代码里起始条件的时序不对,SCL 还没拉高就切 SDA,结果从设备根本不响应。调试时用逻辑分析仪一看,才明白起始条件必须是 SCL 高时 SDA 下降沿,顺序不能反。
重要:起始和停止条件只能由主设备产生。如果从设备想提前终止,它会拉低 SCL 延长时间,但条件信号始终在主设备手里。
地址与读写位
起始条件之后,主设备发送 7 位地址 + 1 位读写位。这 8 位组成第一个字节。
- 读写位为 0:写操作(主设备发数据给从设备)。
- 读写位为 1:读操作(主设备从从设备读数据)。
比如 I2C 地址 0x48(AT24C02 的默认地址),写操作时发送 0x90(地址左移一位 + 0),读操作发送 0x91。
| 操作 | 地址 7 位 | 读写位 | 发送字节(十六进制) |
|---|---|---|---|
| 写 | 1001000 | 0 | 0x90 |
| 读 | 1001000 | 1 | 0x91 |
我建议在代码里用一个宏定义,比如 #define I2C_ADDR_WRITE(addr) ((addr) << 1 | 0),这样可读性更高。
应答机制(ACK / NACK)
每个字节(包括地址字节和数据字节)发送完后,接收方必须给出应答。应答在 SCL 的第 9 个时钟脉冲上检测。
- ACK:接收方在第 9 个时钟周期将 SDA 拉低。表示“收到,请继续”。
- NACK:接收方在第 9 个时钟周期保持 SDA 为高。表示“没收到”或“不想接收”。
你想想看:如果主设备发送完地址后没有收到 ACK,说明地址无效或从设备没上电。我遇到过一家品牌的温度传感器,它在数据手册里写着需要 50ms 启动时间,启动期间不会应答地址。那会儿我代码里没有重试机制,直接挂了。
标准 I2C 数据帧格式
START | 7-bit ADDR + R/W | ACK | DATA | ACK | DATA | ACK | ... | STOP
START | 7-bit ADDR + R/W | ACK | DATA | ACK | DATA | ACK | ... | STOP
I2C 总线通信流程
把上面几个知识点串起来,就是一次完整的 I2C 通信流程。下面这张图可以帮你直观理解。
流程说明:
- 主设备发送起始条件。
- 主设备发送7 位地址 + 读写位。
- 从设备如果地址匹配,发送 ACK(拉低 SDA)。
- 主设备发送或接收数据字节,每个字节后接收方应答。
- 若接收方应答 NACK,主设备可以提前发停止条件。
- 最后主设备发送停止条件,释放总线。
我的经验总结
- 写驱动时,起始条件和停止条件的时序一定要用示波器或逻辑分析仪验证。我翻过车,因为代码里 SDA 翻转时机差了一个微秒,从设备就不认。
- 应答位检查不可省略。我一贯的做法是:发送完地址后等 ACK,超时或 NACK 就报错并复位总线。
- 多主环境要注意总线仲裁,但大部分嵌入式系统都是单主,咱们先搞明白基础流程。
再次强调:I2C 总线是同步的,SCL 低时才能改变 SDA(起始和停止条件除外)。这个规则很多新手会忽略。我曾经看到一个同事把 SDA 在 SCL 高时随意拉低,结果总线上所有设备都误判为起始条件,乱成一团。