浅皮懔 发表于 2025-6-11 13:14:00

I2C电平半高问题详解

I2C电平半高问题详解

最近遇到了I2C半高电平的问题,简单来说就是推挽模式输出强高电平+主机没有发送NACK导致的,想直接看问题解答请跳转至本文最后一小节。
I2C介绍

I2C是一个半双工、多主从的串行总线,仅由两条线就能完成多机通信,一条SCL时钟线,一条双向数据线SDA。本文只讨论一主多从的情况。
严格来说,I2C总线要求每个设备SCL、SDA都是开漏模式。但在我们的应用场景下,MCU总是作为主机来与其他从设备通信,SCL一直在MCU的控制之下;在这种情况下,仅要求SDA为开漏模式。
下图很明晰地展示了I2C的通信原理,高电平由外部上拉电阻产生,而串联在总线上的每一个设备都可以拉低其电平,因此主从设备需要按照一定的时序进行通信。

 
I2C时序

起始信号和停止信号

空闲状态时,SCL和SDA线会由于外部上拉电阻处于高电平状态。I2C规定数据不允许在SCL为高时变化,如果SCL为高时SDA发生改变,那么这种信号就是起始信号和停止信号。

[*]起始信号:SCL保持高电平,SDA由高向低跳变。


[*]停止信号:SCL保持高电平,SDA由低向高跳变。
[*]重启信号:即本该是停止信号的地方给起始信号,就会重新开始一个周期。

 
 
字节传送

每个字节为8bit,数据传送时,先传送最高位,后传送低位。在SCL为高时数据保持稳定。每发送一个字节后接收方必须发送一位应答位,即一帧共有9位。
ACK与NACK

在每8bit的数据传输完成后,主机或从机需要对接收到的数据做出反应,所以第9个bit位就是应答位,放置应答信号(ACK)或非应答信号(NACK)。其中应答信号就是希望对方继续发送数据,非应答信号就是希望对方停止发送数据。

[*]应答信号(ACK):电平为低
[*]非应答信号(NACK):电平为高

 
 
 
写与读

在此仅讨论向从机写一个字节和读一个字节数据的情况。

[*]写时序:开始信号 --> 7位从机地址 + 写位(0) --> 应答信号 --> 写地址 --> 应答信号 --> 八位数据 --> 停止信号


[*]读时序:开始信号 --> 7位从机地址 + 写位(0) --> 应答信号 --> 写地址 --> 应答信号 --> 重启信号 -->
7位从机地址 + 读位(1) --> 应答信号 --> 八位数据(由从机写入) --> 应答位(1,由主机应答) --> 停止信号
 

 
 
I2C半高电平问题解答

在开发中遇到读从机同一地址多次值却不同的问题,经查看波形发现SDA电平半高现象,并且大多出现在读周期。最终解决方案如下:

[*]使用推挽模式模拟IIC,MCU不主动输出高电平
[*]在读完8个bit位后主机给出NACK信号
半高电平出现的原因毫无疑问是主机与从机进行了争抢,如果是开漏状态下争抢只会造成电平为低而不是半高(一低俱低),因此“半高”的罪魁祸首是推挽模式输出的强高电平,即使从机拉也拉不下去,导致了半高电平的出现。因此,如果要使用推挽模拟IIC输出,就不能在SDA输出高电平,高电平也需要依靠外部上拉电阻。
而争抢出现的原因是由于读的时候,从机发送完一个字节并没有收到主机的NACK信号,即应答位为低,由于是ACK信号,IIC时序要求从机继续发送,而该从设备因为程序或是寄存器原因,继续发送的数据为全0,导致主机也无法拉高SDA。所以主机应当在读完数据后给出正确的非应答信号以拿回SDA的控制权。

 
 
 
推挽输出模拟IIC

输出高电平:将SDA设置为输入模式,靠外部上拉将SDA稳定在高电平
输出低电平:将SDA设置为输出模式且输出低电平
输入状态:将SDA设置为输入模式
参考

利尔达-I2C问题分享
一步一磕头的菜鸡-I2C详解

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: I2C电平半高问题详解