2. I2C协议详解:起始条件、停止条件、数据有效性、字节格式与应答机制(ACK/NACK)
I2C协议看起来简单,就两根线——SCL(时钟)和SDA(数据)。但真要在示波器上抓波形、定位问题,你得把每个细节都吃透。这一章,咱们就把协议最核心的几个动作掰开揉碎了讲。
2.1 起始条件(S)与停止条件(P)
总线空闲时,SCL和SDA都是高电平。这是背景,记住它。
起始条件(Start Condition):
SCL保持高电平,SDA从高电平切换到低电平。
停止条件(Stop Condition):
SCL保持高电平,SDA从低电平切换到高电平。
说白了,就是在时钟高电平期间,数据线跳变一次。这个跳变方向决定了是开始还是结束。
关键点:起始和停止条件都是由主机(Master)产生的。从机(Slave)永远不能主动发起或终止传输。
我个人习惯在调试时,先用示波器抓一下起始条件附近的波形。为什么?因为很多通信失败,根源就是起始条件没发出来——可能是主机GPIO配置错了,也可能是上拉电阻没焊好,SDA拉不下去。
实战技巧:如果你看到SCL在正常翻转,但SDA一直高,先查起始条件。我曾经遇到过一颗MCU的I2C外设,初始化顺序不对,导致起始条件永远发不出来。折腾了两天,最后发现是时钟使能位没先开。
2.2 数据有效性:时钟高电平采样,低电平变化
I2C的数据传输有个铁律:数据在SCL高电平时被采样,在SCL低电平时才能变化。
你想想看,如果数据在时钟高电平期间变了,接收方就不知道你传的是0还是1。所以协议规定:
- SCL为高电平时,SDA必须保持稳定。
- SCL为低电平时,SDA可以变化。
唯一的例外就是前面讲的起始和停止条件——那是在高电平跳变,属于特殊标记。
避坑指南:我曾经调试一个温湿度传感器,读出来的数据偶尔跳变。抓波形发现,从机在SCL低电平时改变数据没问题,但主机采样时刻刚好卡在上升沿附近,由于线缆电容导致SCL上升沿变缓,数据还没稳定就被采了。解决办法是降低I2C速率,从400kHz降到100kHz。
2.3 字节格式:8位数据 + 1位应答
I2C传输的基本单位是字节。每个字节固定为8位,MSB(最高位)在前。发送完8位数据后,紧接着跟一个应答位(ACK/NACK)。
所以一个完整的数据帧是:8位数据 + 1位应答 = 9个SCL时钟周期。
| 时钟周期 | SDA行为 | 说明 |
|---|---|---|
| 1-8 | 主机发送8位数据(MSB first) | 数据在SCL低电平时变化 |
| 9 | 从机拉低SDA(ACK)或保持高(NACK) | 主机释放SDA,从机控制 |
这里有个容易忽略的点:发送方在数据位期间控制SDA,应答位期间释放SDA,由接收方控制。如果接收方没拉低,SDA就被上拉电阻拉高,变成NACK。
2.4 应答机制:ACK与NACK
应答位是I2C协议的“握手信号”。每次传输完一个字节,接收方必须给出回应:
- ACK(Acknowledge):接收方在第9个时钟周期将SDA拉低,表示“收到,请继续”。
- NACK(Not Acknowledge):接收方在第9个时钟周期保持SDA高,表示“没收到”或“不想收了”。
NACK的出现通常意味着以下几种情况:
- 从机地址不匹配:总线上没有设备响应这个地址。
- 从机忙:正在处理内部操作,无法接收新数据。
- 传输结束:从机告诉主机“数据够了,别再发了”。
- 从机故障:芯片坏了或I2C接口死锁。
调试口诀:看到NACK,先查地址对不对,再查从机有没有上电,最后查从机是不是在忙。
我记得有一次调试一个EEPROM,写数据时总是NACK。查了半天,发现是EEPROM在写内部Flash时需要时间(tWR),这段时间内它不响应任何I2C命令。解决办法是在每次写操作后加一个延时,或者轮询直到收到ACK为止。
2.5 完整传输流程:从起始到停止
把上面几个知识点串起来,一次完整的I2C传输是这样的:
- 主机发送起始条件(S)。
- 主机发送7位从机地址 + 1位读写位(R/W)。
- 对应地址的从机回复ACK。
- 主机发送寄存器地址或命令字节(可选,取决于设备)。
- 从机回复ACK。
- 主机发送或接收数据字节,每字节后跟ACK。
- 主机发送停止条件(P)。
嗯,这里要注意:如果是读操作,在最后一个数据字节后,主机要回复NACK,然后发停止条件。这是告诉从机“别再发了”。
2.6 知识结构图:I2C协议核心要素
下面这张图把本章的核心概念串起来了。你可以把它当作调试时的“速查地图”。
2.7 常见问题与排查思路
最后,我把调试中遇到最多的几个问题列出来,你碰到类似情况可以直接对照:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 无起始条件 | GPIO配置错误、上拉电阻缺失 | 检查SCL/SDA电平,确认主机是否拉低SDA |
| 地址后NACK | 地址错误、从机未上电、总线冲突 | 核对地址(7位还是8位),测量从机VDD |
| 数据后NACK | 从机忙、不支持该命令、写保护 | 查看数据手册的tWR时间,检查WP引脚 |
| SCL无时钟 | 主机时钟未使能、从机时钟拉伸卡死 | 检查时钟配置,测量SCL是否被从机拉低 |
| 数据错位 | 速率不匹配、上升沿过缓 | 降低速率,检查总线电容和上拉电阻值 |
嗯,这一章的内容就到这里。协议细节是调试的基础,你把这些动作刻在脑子里,后面抓波形的时候一眼就能看出问题出在哪一步。