I2C 协议基础:时序、起始条件、停止条件、数据位、ACK/NACK
做嵌入式这些年,I2C 是我打交道最多的总线——两根线,一主多从,看似简单,但坑一点不少。很多新手觉得“只要连上就能用”,结果波形一塌糊涂。今天我从逻辑分析仪的实际观察出发,把这些基础拆开揉碎,你以后抓波形时心里就有谱了。
1.1 两根线的默契:SCL 与 SDA
I2C 只有两条线:SCL(时钟)和 SDA(数据)。主设备控制时钟,从设备在时钟节拍下响应。所有通信都以起始条件开始,以停止条件结束。数据在 SCL 高电平时被采样,低电平时允许变化。嗯,核心就这些。
我遇到过两次因为上拉电阻选错导致通信间歇性失败的项目。你想想看,如果上拉太强(比如 1kΩ),上升沿虽快但功耗大、容性负载受限;上拉太弱(比如 10kΩ),上升沿变缓,SDA 在 SCL 高电平还没稳定就被采样了。逻辑分析仪上一看,数据位全是毛刺。
1.2 起始条件与停止条件——总线的“令牌”
起始条件:SCL 为高时,SDA 从高切换到低。停止条件:SCL 为高时,SDA 从低切换到高。这两个条件定义了总线的使用权移交。你可以把它们理解为通信的“开关”。
我个人习惯在逻辑分析仪上设置 SDA 下降沿触发的同时,检查 SCL 是否确实为高。有一次我在调试一块温湿度传感器,发现它经常不响应。抓波形后看到起始条件发生时 SCL 还没完全变高——建立时间不足,从机根本认不出这是起始。后来在代码里加了 1μs 的延时才解决。
| 条件 | SCL 状态 | SDA 变化 | 时序要求 |
|---|---|---|---|
| 起始条件 | 高 | 下降沿(高→低) | SDA 下降前 SCL 必须稳定在高电平至少 4.7μs(100kHz 标准模式) |
| 停止条件 | 高 | 上升沿(低→高) | SDA 上升前 SCL 必须稳定在高电平至少 4.0μs |
1.3 数据位与字节传输——SCL 高电平时才说了算
每个字节 8 位,加上 1 位应答位,共 9 个 SCL 周期。数据在 SCL 高电平时被采样,所以 SDA 只能在 SCL 低电平时改变。如果逻辑分析仪显示 SDA 在 SCL 上升沿附近变化,那大概率是时序违规——从机可能采到错误值。
我去年修过一个 LCD 驱动的 bug,显示字符偶尔乱码。用逻辑分析仪一帧一帧对比,发现第 7 位数据(高位)变化时恰好卡在 SCL 上升沿上。最终发现是主控的 GPIO 翻转速度太快,SDA 变化时间与 SCL 上升沿重叠。解决办法是在 SCL 下降沿后插入一个微的延迟再改变 SDA。
1.4 ACK/NACK——握手的“点头”与“摇头”
主机发送完 8 位数据后,在第 9 个时钟释放 SDA。如果从机正常工作,它会将 SDA 拉低一个时钟周期,表示 ACK(应答);维持高电平则表示 NACK(非应答)。NACK 通常用来结束读取操作或报告错误——比如从机正忙、地址不对或数据异常。
我记得有一次调试一个 EEPROM,写数据后读回来总是不对。逻辑分析仪捕获到第 9 个时钟时 SDA 一直为高——从机根本没 ACK。查手册发现 EEPROM 在写周期内不会应答任何命令。我在写操作后加了 5ms 延时,再读就正常了。这个事情告诉我:不要把 NACK 直接当故障,它可能是正常流程的一部分。
| SDA 在第 9 时钟状态 | 含义 | 常见原因 |
|---|---|---|
| 低(被从机拉低) | ACK | 从机已接收或准备好发送 |
| 高(主机释放后仍为高) | NACK | 地址不匹配、从机忙、数据错误、传输结束 |
I2C 一次完整传输流程
下面这张图概括了从起始到停止的完整流程。它是我在调试时贴在显示器旁边的参考,你也可以保存下来对照波形。
实际中,你可以在逻辑分析仪上设置触发条件为“起始条件 + 特定地址”来捕获某个从机的通信。把上图印在脑子里,看波形时就能快速定位问题。
实战代码:模拟 I2C 起始条件
下面是一个底层 GPIO 模拟 I2C 起始条件的函数。注意时序延迟必须匹配所用频率。我用标准模式(100kHz)作为例子,你可以根据自己芯片的时钟频率调整。
void i2c_start(void)
{
// 确保 SDA 和 SCL 初始为高
SDA_HIGH();
SCL_HIGH();
delay_us(5); // 保持时间,标准模式要求 ≥4.7μs
// 在 SCL 高电平时拉低 SDA 产生起始条件
SDA_LOW();
delay_us(5); // 起始保持时间
// 拉低 SCL 准备传输时钟
SCL_LOW();
delay_us(1); // 确保 SCL 完全变为低
}
以上便是 I2C 协议最基础的部分。起始/停止的边沿必须干净,数据必须在 SCL 高时稳定,ACK 必须被正确识别。把这些“规矩”记牢,后续分析波形失真时你才能分辨出是协议错误还是信号完整性问题。