3. I2C协议层:开始条件、停止条件、数据位传输规则、ACK/NACK应答机制
好了,前面我们聊了I2C的物理层,搞清楚了SCL和SDA怎么接线,上拉电阻怎么选。现在咱们进入协议层——这才是真正好玩的地方。
说白了,协议层就是一套“握手暗号”。主设备和从设备之间,什么时候开始说话,什么时候结束,每一句话怎么说,对方听没听清楚,全得靠这套规则来保证。我当年第一次调I2C,以为只要把数据扔到总线上就行,结果示波器一看,波形乱成一团。嗯,从那以后我老老实实把协议层的每一条规则都背了下来。
3.1 开始条件(START Condition)
一切通信的开始,都得有个明确的信号。在I2C总线上,这个信号叫开始条件。
规则很简单:
- SCL保持高电平时,SDA从高电平跳变到低电平。
只有主设备能发起开始条件。一旦总线上出现了这个信号,所有从设备都会知道:“嘿,有消息要来了,准备好接收。”
注意,开始条件必须是SDA在SCL高电平期间变化。SCL低电平时SDA怎么变都不算数。这个细节很多人第一次会忽略,结果仿真通过了,板子上死活跑不通。
3.2 停止条件(STOP Condition)
有开始就得有结束。停止条件就是告诉大家:“好了,话讲完了,总线释放了。”
规则也很明确:
- SCL保持高电平时,SDA从低电平跳变到高电平。
停止条件同样由主设备发出。停止后,总线进入空闲状态,SDA和SCL都变回高电平,等待下一次开始。
我见过一个坑:有人在一次传输结束后忘了发停止条件,结果下一个开始条件发不出来,总线卡死在那了。所以写驱动时,每次传输完毕后一定要记住发STOP。
| 信号 | SCL状态 | SDA变化方向 | 谁发起 |
|---|---|---|---|
| 开始条件 | 高电平 | 下降沿(高→低) | 主设备 |
| 停止条件 | 高电平 | 上升沿(低→高) | 主设备 |
3.3 数据位传输规则
开始条件和停止条件之间的部分,就是数据的主体了。I2C的数据传输,遵循一个非常朴素的规则:SCL高电平时采样数据,SCL低电平时改变数据。
为什么?你想想看,如果数据和时钟同时变化,接收方根本分不清哪个才是有效的。所以协议规定:SDA只能在SCL低电平期间变化。SCL一拉高,SDA就必须稳定住,保持不动,这时候接收方去读SDA的电平。
每次传输一个字节(8位),从最高位MSB开始。意思是先发bit7,再发bit6……一直发到bit0。我习惯在脑子里把它想象成一个移位寄存器:每次时钟脉冲过来,移出去一位。
对了,还有一点:重复开始条件(Repeated START)。有时候主设备不想释放总线,但需要改变通信方向。比如在读操作之前,先发个地址,然后不发停止,再次发开始条件。这在协议里是允许的,而且很常用。我做过一个项目,需要连续读取传感器的多个寄存器,就用重复开始条件来避免总线被别的设备抢走。
3.4 ACK/NACK应答机制
数据发出去,对方收到了没有?总得有个反馈吧。I2C用了一个非常巧妙的应答机制。
主设备每发送完8位数据(一个字节)后,会释放SDA的控制权。然后在第9个时钟脉冲期间,从设备会把SDA拉低,表示“我收到了,请继续”。这个动作叫应答(ACK)。
如果从设备没有拉低SDA,也就是SDA保持高电平,那就表示“我没收到,或者我不想再收了”,这叫非应答(NACK)。主设备收到NACK后,通常会发送停止条件,结束本次传输。
应答规则:
- ACK:SDA在第9个SCL脉冲期间为低电平。
- NACK:SDA在第9个SCL脉冲期间为高电平。
应答由接收方发出。发送方只负责在第9个时钟周期释放SDA,并检查电平。
注意:地址字节发送后也需要等待应答。如果总线上没有设备响应这个地址,从设备就不会拉低SDA,这时候主设备收到NACK就得知道——要么地址写错了,要么设备没在线。
3.5 完整的帧结构
把上面这些串起来,一个完整的I2C帧结构是这样的:
- 主设备发出开始条件
- 主设备发送7位从设备地址 + 1位读写位(0=写,1=读)
- 从设备发送应答位(ACK/NACK)
- 主设备发送/接收8位数据(多个字节)
- 每次数据字节后跟着应答位
- 主设备发出停止条件
读写位是紧跟在地址后面的第8位。0表示主设备要写数据给从设备,1表示主设备要从从设备读数据。这在后面章节我们会细讲。
3.6 应答的几种特殊情况
实际项目中,应答机制还会遇到几种特殊情况:
- 从设备ACK后不响应:有时候从设备应答了,但后续数据处理不过来。这时候它会拉低SCL(时钟拉伸),逼主设备等它。主设备需要检测SCL电平,如果被拉低就继续等待。
- 主设备主动发NACK:在读取模式下,主设备想结束传输,可以在最后一个字节之后不发ACK,而是发NACK。这表示“行了我不要了,结束吧”。然后主设备接着发送停止条件。
- 广播地址(0x00):有些支持广播的设备,应答地址0x00。一般用得不多,但知道有这个功能就好。