xieco

导航

< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

统计

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详解

posted on   千叶の堕天聖黑猫  阅读(58)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)
点击右上角即可分享
微信分享提示