2. I2C协议时序:起始条件、停止条件、数据有效性、应答机制(ACK/NACK)

I2C协议说白了就是一套「谁说话、谁听话、什么时候说」的规矩。你想想看,SCL和SDA两根线,要挂多个设备,没有一套严格的时序规则,总线早就乱成一锅粥了。

我个人习惯把I2C时序拆成四个核心要素来理解:起始条件、停止条件、数据有效性、应答机制。把这四个搞明白,I2C通信的骨架你就抓住了。

2.1 起始条件(START Condition)

总线空闲时,SCL和SDA都是高电平(由上拉电阻拉高)。主设备想开始通信,必须发出一个起始信号。

时序定义:当SCL为高电平时,SDA从高电平切换到低电平。

关键点:起始条件是一个电平跳变,不是电平本身。SCL高电平期间,SDA的下降沿就是起始信号。

我在项目中遇到过一个问题:某次调试LCD驱动,示波器抓波形发现SDA在SCL低电平时就拉低了,然后SCL才变高。这根本不是起始条件,从机根本不认。折腾了半天,最后发现是代码里GPIO操作顺序写反了。

调试技巧:用示波器抓起始条件时,重点看SCL上升沿和SDA下降沿的相对位置。SDA必须先于SCL变化,且变化时SCL必须是高电平。

2.2 停止条件(STOP Condition)

通信结束,主设备要释放总线。停止条件的时序正好和起始条件相反。

时序定义:当SCL为高电平时,SDA从低电平切换到高电平。

条件 SCL状态 SDA跳变方向 作用
起始条件 高电平 高 → 低(下降沿) 通知所有从机:准备接收
停止条件 高电平 低 → 高(上升沿) 通知所有从机:通信结束

嗯,这里要注意:停止条件之后,总线恢复空闲状态(SCL和SDA都是高)。如果主设备没有发停止条件就释放总线,从机会一直等着,后续通信可能乱掉。

曾经踩过的坑:有一次我写驱动时,在异常处理分支里忘了发停止条件。结果LCD卡死,后续所有I2C操作都超时。从那以后,我每条I2C操作路径都会检查:是否确保有停止条件发出。

2.3 数据有效性(Data Validity)

数据怎么才算有效?I2C协议规定得很清楚:SCL高电平时,SDA上的数据必须稳定。数据变化只能在SCL低电平时发生。

为什么这样设计?你想想看,起始条件和停止条件都是在SCL高电平时检测SDA跳变。如果数据也在SCL高电平时变化,那从机就分不清这是数据还是起始/停止信号了。

数据采样规则:

  • SCL高电平期间:主设备/从设备读取SDA上的数据位
  • SCL低电平期间:发送设备可以改变SDA电平,准备下一位数据
  • 数据位从最高位(MSB)开始传输,每个SCL脉冲传输1位

一句话总结:SCL高看数据,SCL低换数据。这个节奏不能乱。

我记得刚学I2C时,总觉得数据有效性这个规则有点多余。后来调试一个EEPROM驱动,发现读出来的数据总是错位。抓波形一看,原来是MCU的I2C外设在SCL上升沿采样,而我的软件模拟时序在SCL下降沿才更新数据——时序对不上,数据全乱套了。

2.4 应答机制(ACK/NACK)

I2C的应答机制,说白了就是接收方给发送方回个话:「收到了」或者「没收到」。这个机制让I2C比SPI多了错误检测能力。

应答时序:

  1. 发送方发送完8位数据后,释放SDA(设为输入或高阻)
  2. 主设备产生第9个SCL时钟脉冲
  3. 接收方在第9个SCL脉冲的高电平期间,将SDA拉低(ACK)或保持高(NACK)
应答类型 SDA状态(第9个SCL高电平) 含义 常见原因
ACK(应答) 低电平 接收成功,可以继续发送 正常通信
NACK(非应答) 高电平 接收失败或拒绝接收 地址不匹配、设备忙、数据错误

实用经验:调试LCD驱动时,如果发送设备地址后收到NACK,90%的情况是地址写错了。LCD的I2C地址通常是7位,但数据手册上给的可能是8位(左移1位后的值)。我建议你仔细核对:手册给的是0x27还是0x4E?

2.5 完整数据传输流程

把上面四个要素串起来,一次完整的I2C数据传输是这样的:

  1. 起始条件:主设备发出START
  2. 发送从机地址:7位地址 + 1位读写位(R/W)
  3. 等待应答:从机返回ACK表示地址匹配
  4. 发送/接收数据:每8位数据后跟1个应答位
  5. 停止条件:主设备发出STOP

这里有个细节:读操作时,最后一个字节主设备要发NACK,告诉从机「别再发了,我要停了」。然后主设备再发停止条件。这个NACK是读操作结束的标志,不是错误。

曾经踩过的坑:我第一次写LCD读寄存器时,最后一个字节发了ACK而不是NACK。结果从机继续发数据,总线被占死,停止条件发不出去。从那以后,我写读操作代码都会特意检查:最后一个字节是不是NACK。

2.6 时序知识图谱

下面这张图把I2C协议时序的四个核心要素串在一起,方便你对照理解:

I2C协议时序核心要素 起始条件 SCL高,SDA下降沿 停止条件 SCL高,SDA上升沿 数据有效性 SCL高时数据稳定 应答机制 第9个SCL脉冲 完整数据传输流程:起始 → 地址+读写位 → ACK → 数据+ACK → ... → 停止 常见问题排查 • 起始条件失败:SDA拉低时SCL不是高电平 • 停止条件丢失:异常退出未发STOP • 数据错位:SCL高电平时SDA变化 • 地址NACK:从机地址不匹配 • 读操作NACK遗漏:最后一个字节未发NACK 调试工具建议 • 逻辑分析仪:看时序关系最直观 • 示波器:测电平跳变时间精度 • 重点关注:SCL高电平期间的SDA状态 • 检查第9个SCL脉冲的SDA电平 • 对比数据手册的时序参数表

2.7 时序参数速查

实际调试时,时序参数也很关键。不同I2C设备对时序要求不一样,LCD驱动芯片通常支持标准模式(100kHz)和快速模式(400kHz)。

参数 符号 标准模式 快速模式 说明
SCL时钟频率 fSCL ≤100 kHz ≤400 kHz 主设备产生
起始条件保持时间 tHD;STA ≥4.0 μs ≥0.6 μs SCL高后SDA保持低
数据保持时间 tHD;DAT ≥0 μs ≥0 μs SCL下降沿后数据保持
数据建立时间 tSU;DAT ≥250 ns ≥100 ns SCL上升沿前数据稳定
停止条件建立时间 tSU;STO ≥4.0 μs ≥0.6 μs SCL高后SDA拉高

个人经验:软件模拟I2C时,我习惯把延时设得比最小值宽裕一些。比如标准模式,我会用5μs而不是4μs。虽然慢一点,但兼容性好,换不同厂家的LCD芯片不用改代码。

嗯,I2C时序就这些核心内容。起始条件、停止条件、数据有效性、应答机制——这四个概念你吃透了,I2C通信的底层逻辑就通了。后面章节我们会把这些知识用到实际的LCD驱动调试中,到时候你会感谢自己现在花时间搞懂了这些基础。