I2C协议基础:起始条件、停止条件、数据有效性、字节格式与应答机制
I2C协议,说白了就是一套“谁说话、谁听话、什么时候说、说完怎么确认”的规矩。我刚开始接触I2C时,觉得它比SPI啰嗦——又要应答又要地址,但后来发现,正是这些机制让它在多设备场景下特别稳。
咱们今天就把I2C最核心的几个动作拆开揉碎。你把这些搞懂了,后面移植驱动就是套模板的事。
2.1 起始条件与停止条件
I2C总线上,SCL(时钟线)和SDA(数据线)平时都是高电平。这个状态叫“空闲态”。什么时候总线开始干活?就看这两个信号怎么跳变。
起始条件(START): SCL为高电平时,SDA从高电平切换到低电平。
停止条件(STOP): SCL为高电平时,SDA从低电平切换到高电平。
你想想看,这两个条件都是“SCL高电平期间,SDA跳变”。为什么这么设计?因为数据采样也是在SCL高电平时做的,所以起始和停止用SDA跳变来区分,不会和数据位混淆。
我的习惯:写驱动时,我会把起始和停止封装成两个独立函数。每次调用前检查一下总线状态,防止在忙时误操作。
我曾经在一个项目里遇到过这样的坑:主机关闭后,从机还在输出低电平,导致下次起始条件发不出去。后来我加了一段“总线恢复”逻辑——先发9个时钟脉冲,让从机释放SDA。嗯,这招挺管用。
2.2 数据有效性
I2C的数据采样规则其实很简单:
- SCL低电平期间: SDA可以变化(准备下一位数据)
- SCL高电平期间: SDA必须稳定(主机采样数据)
换句话说,数据在SCL高电平时被“拍照”下来。你要是SCL高电平时去动SDA,要么被当成起始/停止条件,要么采样到错误电平。
注意: 数据线SDA的变化只能在SCL低电平时进行。这是硬性规定,违反了这个时序,从机根本不会理你。
我记得有一次调试一个温湿度传感器,波形怎么看都对,但读出来的数据全是0xFF。查了半天,发现是SCL高电平期间SDA有毛刺,把数据位全冲成1了。加了个小电容滤波,问题解决。
2.3 字节格式
I2C传输的基本单位是字节(8位),而且有固定的传输顺序:
- MSB先行: 先传最高位(bit 7),再传低位,最后是bit 0。
- 每字节后跟一个应答位: 第9个时钟周期用于应答。
- 无字节限制: 可以连续传多个字节,直到停止条件或重启条件。
举个例子,你要往一个I2C EEPROM的地址0x10写入数据0xA5,发送顺序是这样的:
起始条件 → 设备地址(0x50) + 写位(0) → 应答 → 寄存器地址(0x10) → 应答 → 数据(0xA5) → 应答 → 停止条件
这里设备地址0x50是7位地址左移1位的结果。嗯,很多新手在这里搞混——I2C地址是7位,但发送时占8位,最低位是读写标志。
| 位序 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|---|---|---|---|
| 数据 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | R/W |
| 说明 | 7位设备地址 (0x50) | 0=写, 1=读 | ||||||
2.4 应答机制
应答是I2C最巧妙的设计之一。每发完一个字节,接收方必须在第9个时钟周期给出反馈:
- ACK(应答): 接收方将SDA拉低,表示“收到了,继续发”。
- NACK(非应答): 接收方保持SDA高电平,表示“没收到/不想收/传完了”。
关键规则: 应答信号由接收方控制SDA,主机提供第9个时钟。谁发数据,谁就释放SDA,让对方拉。
我个人的经验是:调试I2C时,先看应答位。如果地址发出去没等到ACK,八成是地址错了或者从机没上电。如果数据发出去从机回了NACK,可能是寄存器地址不对或者从机忙。
为什么会这样?因为从机在收到数据后需要时间处理,如果处理不过来,就会回NACK让主机等一下。有些从机甚至会用“时钟拉伸”来拖慢主机——把SCL拉低,逼主机等它准备好。
避坑指南: 我曾经在一个项目里忽略了从机的时钟拉伸,结果主机按固定速率发数据,从机跟不上,数据全乱套。后来加了超时检测,发现从机偶尔会拉伸时钟几百微秒。所以,写驱动时一定要处理时钟拉伸,别假设从机永远能跟上。
2.5 完整传输流程
把上面几个概念串起来,一次完整的I2C写操作流程是这样的:
- 主机发送起始条件
- 主机发送7位设备地址 + 写位(0)
- 从机回复ACK
- 主机发送8位寄存器地址
- 从机回复ACK
- 主机发送8位数据
- 从机回复ACK
- 主机发送停止条件
读操作稍微复杂一点,需要先写寄存器地址,再重启条件,然后切换成读模式。这个咱们后面章节会细讲。
2.6 常见问题与排查思路
最后,我把自己这些年踩过的坑整理成一张表,你写驱动时对照着查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 起始条件发不出 | SDA被从机拉低 | 发9个时钟脉冲复位从机 |
| 地址发完无ACK | 地址错误/从机未上电 | 用示波器看SDA在第9时钟是否被拉低 |
| 数据读回全是0xFF | SDA上拉电阻失效/从机未应答 | 检查上拉电阻(典型4.7kΩ) |
| 偶尔通信失败 | 时钟拉伸未处理/中断干扰 | 加超时检测,关中断保护 |
| 停止条件后总线仍忙 | 从机未释放SDA | 检查从机是否支持时钟拉伸 |
最后提醒一句: I2C的时序看着简单,但实际项目中干扰因素很多。上拉电阻、总线电容、从机响应时间,任何一个环节出问题,波形就变形。我建议你手边常备一个逻辑分析仪,抓一次波形比看十遍手册都管用。