I2C
I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强, 不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C总线是一种用于芯片之间进行通信的串行总线。它由两条线组成:串行时钟线(SCL)和串行数据线(SDA)。这种总线允许多个设备在同一条总线上进行通信。
物理层
I2C通信协议是一种通用的总线协议。I2C通信协议有以下特征:
(1) 它是一个
支持设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中, 可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。(2) 一个I2C总线只使用两条总线线路,一条双向
串行数据线(SDA) , 一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。(3) 每个连接到总线的设备都有
一个独立的地址, 主机可以利用这个地址进行不同设备之间的访问。(4)
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态, 而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。(5) 多个主机同时使用总线时,为了防止数据冲突, 会
利用仲裁方式决定由哪个设备占用总线。(6) 具有三种传输模式:
标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。(7) 连接到相同总线的 IC 数量
受到总线的最大电容 400pF 限制(8) SDA数据线在每个SCL的时钟周期传输一位数据,SCL为高电平的时候SDA表示的数据有效。
(9) 应答信号和非应答信号I2C的数据和地址传输都带响应。

一主多从是指单片机作为主机,主导I2C总线的运行。挂在I2C总线上的所有外部模块都是从机,只有被主机点名后才能控制I2C总线,不能在未经允许的情况下访问I2C总线,以防止冲突。这就像在课堂上,老师是主机,学生是从机。未经点名允许,学生不能发言,但可以被动地听老师讲课。
另外,I2C还支持多主多从模型,即多个主机。在多主多从模型中,总线上任何一个模块都可以主动跳出来说,接下来我就是主机,你们都得听我的。这就像在教室里,老师正在讲课,突然一个学生站起来说,打断一下,接下来让我来说,所有同学听我指挥。但是,同一个时间只能有一个人说话,这时就相当于发生了总线冲突。在总线冲突时,I2C协议会进行仲裁,仲裁胜利的一方取得总线控制权,失败的一方自动变回从机。由于时钟线也由主机控制,所以在多主机的模型下还要进行时钟同步。多主机的情况下,协议是比较复杂的。
作为一个通信协议,I2C必须在硬件和软件上作出规定。硬件上的规定包括电路的连接方式、端口的输入输出模式等;软件上的规定包括时序的定义、字节的传输方式、高位先行还是低位先行等。这些硬件和软件的规定结合起来构成了一个完整的通信协议。
协议层
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
所有I2C设备的SCL连在一起,SDA连在一起
设备的SCL和SDA均要配置成开漏输出模式
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

这个图就是I2C的典型电路模型,这个模型采用了一主多从的结构。在左侧,我们可以看到CPU作为主设备,控制着总线并拥有很大的权利。其中,主机对SCL线拥有完全的控制权,无论何时何地,主机都负责掌控SCL线。在空闲状态下,主机还可以主动发起对SDA的控制。但是,从机发送数据或应答时,主机需要将SDA的控制权转交给从机。
接下来,我们看到了一系列被控IC,它们是挂载在12C总线上的从机设备,如姿态传感器、OLED、存储器、时钟模块等。这些从机的权利相对较小。对于SCL时钟线,它们在任何时刻都只能被动的读取,不允许控制SCL线;对于SDA数据线,从机也不允许主动发起控制,只有在主机发送读取从机的命令后,或从机应答时,从机才能短暂地取得SDA的控制权。这就是一主多从模型中协议的规定。
然后我们来看接线部分。所有I2C设备的SCL和SDA都连接在一起。主机的SCL线拉出来,所有从机的SCL都接在这上面。主机的SDA线也是一样,拉出来,所有从机的SDA接在这上面。这就是SCL和SDA的接线方式。
那到现在,我们先不继续往后看了,先忽略这两个电阻,那到现在,假设我们就这样连接,那如何规定每个设备SCL和SDA的输入输出模式呢?
由于现在是一主多从结构,主机拥有SCL的绝对控制权,因此主机的SCL可以配置成推挽输出,所有从机的SCL都配置成浮空输入或上拉输入。数据流向为主机发送、所有从机接收。
但是到SDA线这里就比较复杂了,因为这是半双工协议,所以主机的SDA在发送时是输出,在接收时是输入。同样地,从机的SDA也会在输入和输出之间反复切换。如果能够协调好输入输出的切换时机就没有问题。但是这样做的话,如果总线时序没有协调好,就极有可能发生两个引脚同时处于输出的状态。如果此时一个引脚输出高电平,一个引脚输出低电平,就会造成电源短路的情况,这是要极力避免的。
为了避免这种情况的发生,I2C的设计规定所有设备不输出强上拉的高电平,而是采用外置弱上拉电阻加开漏输出的电路结构。这两点规定对应于前面提到的“设备的SCL和SDA均要配置成开漏输出模式”以及“SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右”。对应上面这个图。

正常的推挽输出方式如下:上面一个开关管连接正极,下面一个开关管连接负极。当上面导通时,输出高电平;下面导通时,输出低电平。因为这是通过开关管直接连接到正负极的,所以这是强上拉和强下拉的模式。
而开漏输出呢,就是去掉这个强上拉的开关管,输出低电平时,下管导通,是强下拉,输出高电平时,下管断开,但是没有上管了,此时引脚处于浮空的状态,这就是开漏输出。
和这里图示是一样的,输出低电平,这个开关管导通,引脚直接接地,是强下拉,输出高电平,这个开关管断开,引脚什么都不接,处于浮空状态,这样的话,所有的设备都只能输出低电平而不能输出高电平,为了避免高电平造成的引脚浮空,这时就需要在总线外面,SCL和SDA各外置一个上拉电阻,这是通过一个电阻拉到高电平的,所以这是一个弱上拉。
这样做的好处是:
第一,完全杜绝了电源短路现象,保证电路的安全。你看所有人无论怎么拉杆子或者放手,杆子都不会处于一个被同时强拉和强推的状态,即使有多个人同时往下拉杆子,也没问题
第二,避免了引脚模式的频繁切换。开漏加弱上拉的模式,同时兼具了输入和输出的功能,你要是想输出,就去拉杆子或放手,操作杆子变化就行了,你要是想输入,就直接放手,然后观察杆子高低就行了,因为开漏模式下,输出高电平就相当于断开引脚,所以在输入之前,可以直接输出高电平,不需要再切换成输入模式了。
第三,就是这个模式会有一个“线与”的现象。就是只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有设备都输出高电平,总线才处于高电平。I2C可以利用这个电路特性执行多主机模式下的时钟同步和总线仲裁,所以这里SCL虽然在一主多从模式下可以用推挽输出,但是它仍然采用了开漏加上拉输出的模式,因为在多主机模式下会利用到这个特征。
I2C时序设计
起始条件:SCL高电平期间,SDA从高电平切换到低电平
终止条件:SCL高电平期间,SDA从低电平切换到高电平

起始条件是指SCL高电平期间,SDA从高电平切换到低电平。在I2C总线处于空闲状态时,SCL和SDA都处于高电平状态,由外挂的上拉电阻保持。当主机需要数据收发时,会首先产生一个起始条件。这个起始条件是,SCL保持高电平,然后把SDA拉低,产生一个下降沿。当从机捕获到这个SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤。之后,主机需要将SCL拉低。这样做一方面是占用这个总线,另一方面也是为了方便这些基本单元的拼接。这样,除了起始和终止条件,每个时序单元的SCL都是以低电平开始,低电平结束。
终止条件是,SCL高电平期间,SDA从低电平切换到高电平。SCL先放开并回弹到高电平,SDA再放开并回弹高电平,产生一个上升沿。这个上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平,回归到最初的平静状态。这个起始条件和终止条件就类似串口时序里的起始位和停止位。一个完整的数据帧总是以起始条件开始、终止条件结束。另外,起始和终止都是由主机产生的。因此,从机必须始终保持双手放开,不允许主动跳出来去碰总线。如果允许从机这样做,那么就会变成多主机模型,不在本节的讨论范围之内。这就是起始条件和终止条件的含义。
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

就这样的流程,主机拉低SCL,把数据放在SDA上,主机松开SCL,从机读取SDA的数据,在SCL的同步下,依次进行主机发送和从机接收,循环8次,就发送了8位数据,也就是一个字节,另外注意,这里是高位先行,所以第一位是一个字节的最高位B7,然后依次是次高位B6…
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
I2C从机地址
I2C的完整时序,主要有指定地址写,当前地址读和指定地址读这3种。
首先注意的是,我们这个I2C是一主多从的模型,主机可以访问总线上的任何一个设备,那如何发出指令,来确定要访问的是哪个设备呢?
为了解决这个问题,我们需要为每个从设备分配一个唯一的设备地址。这些地址就像是每个设备的名字,主机通过发送这些地址来确定要与哪个设备通信。
当主机发送一个地址时,所有的从设备都会收到这个地址。但是,只有与发送的地址匹配的设备会响应主机的读写操作。
在I2C总线中,每个挂载的设备的地址必须是唯一的,否则当主机发送一个地址时,多个设备响应,就会导致混乱。
在I2C协议标准中,从机设备地址分为7位和10位两种。我们今天主要讨论7位地址,因为它们相对简单且应用广泛。
每个I2C设备在出厂时都会被分配一个7位的地址。例如,MPU6050的7位地址是1101 000,而AT24C02的7位地址是1010 000。不同型号的芯片地址是不同的,但相同型号的芯片地址是相同的。
如果多个相同型号的芯片挂载在同一条总线上,我们可以通过调整地址的最后几位来解决这个问题。例如,MPU6050的地址可以通过ADO引脚来改变,而AT24C02的地址可以通过A0、A1、A2引脚来改变。这样,即使相同型号的芯片,挂载在同一个总线上,也可以通过切换地址低位的方式,保证每个设备的地址都是唯一的。这就是i2C设备的从机地址。
阿源的知识库