IIC 通信协议
参考:CSDN-Projectsauron、B站-江协科技
IIC Overview
IIC
协议(Inter-Integrated Circuit
,可简写为 I2C
),是一种用于各种电子设备之间进行通信和数据交换的串行通信协议。它是由飞利浦(Philips)公司于 1982 年首次提出并推广的一种简单、高效、低成本的通信协议。I2C
协议具有广泛的应用范围,如连接传感器、存储器、显示器等设备,常用于微控制器和嵌入式系统中,因为它使用的引脚较少,可以同时连接多个设备,并且具有简单的硬件和软件实现。
I2C
协议采用双线结构传输数据,包括一个数据线和一个时钟线(即 SDA
和 SCL
线),其中 SDA(Serial Data)
线用于双向数据传输,而 SCL(Serial Clock)
线则用于同步数据传输的时钟信号。
连接在 I2C
总线上的器件分为主机和从机。通信始终由主机(Master
)控制,从机(Slave
)被动接收和回应。主机有权发起和结束一次通信,从机只能被动呼叫。
每个连接到 I2C
总线上的器件都有一个唯一的地址(7 bit),且每个器件都可以作为主机也可以作为从机(但同一时刻只能有一个主机),当总线上有多个主机同时启用总线时,I2C
也具备冲突检测和仲裁的功能来防止错误产生。总线上的器件增加和删除不影响其他器件正常工作。
上图就是I2C
总线的硬件电路,所有设备(IC)
的SCL
和SDA
均要配置为开漏输出模式,并在外围电路各添加一个上拉电阻(通常为4.7KΩ左右),避免同时有多个设备输出高电平导致短路问题发生。
注意,按照实际设计中经验大概是不超过 8 个器件。
IIC的8 位地址,减去 1 位广播地址,是 7 位地址, 2 7 = 128 2^7 = 128 27=128。但是地址 0x00 不用,那就是 127 个地址, 所以理论上可以挂 127 个从器件。I2C 协议没有规定总线上设备最大数目,但是规定了总线电容不能超过 400pF。管脚都是有输入电容的,PCB 上也会有寄生电容,所以会有一个限制。实际设计中经验值大概是不超过 8 个器件。
总线之所以规定电容大小是因为,I2C 的 OD 要求外部有电阻上拉,电阻和总线电容产生了一个 RC 延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险。传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以 RC 乘积必须更小。
IIC Detailed Explanation
起始位
如下图,就是 I2C
通信起始标志,通过这个起始位就可以告诉 I2C
从机,主机要开始进行 I2C
通信了。在 SCL
为高电平的时候,SDA
出现下降沿就表示为起始位:
停止位
如下图,就是停止 I2C
通信的标志位,和起始位的功能相反。在 SCL
位高电平的时候,SDA
出现上升沿就表示为停止位:
数据传输
如下图,I2C
总线在数据传输的时候要保证在 SCL
高电平期间,SDA
上的数据稳定,即 SDA
上的数据变化只能在 SCL
低电平期间发生:
应答信号
当 I2C
主机发送完 8
位数据以后会将 SDA
设置为输入状态(释放SDA),等待 I2C
从机应答,也就是等到 I2C
从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完 8
位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将 SDA
拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
I2C 设备地址格式
I2C
设备的地址为 8
位,但是时序操作时最后一位不属于地址,而是 R/W
状态位。所以有用的是前 7
位,使用时地址整体右移一位处理即可。
除此之位,一个设备地址的前四位是固定的,是厂家用来表示设备类型的:
- 比如接口为
I2C
的温度传感器类设备地址前四位一般为 1001 即 9X; - EEPROM 存储器地址前四位一般为 1010 即 AX;
- oled屏地址前四位一般为 0111 即 7X 等。
I2C 时序图
下面结合图例,将前面所提到的信息整合一下:
①起始信号
当 SCL
为高电平期间,SDA
由高到低的跳变,起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。
②停止信号
当 SCL
为高电平期间,SDA
由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。
③应答信号
发送器每发送一个字节,就在时钟脉冲 9
期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK
简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK
),一般表示接收器接收该字节没有成功。
④数据有效性
IIC
总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在 SCL
的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。
⑤数据传输
在 IIC
总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL
串行时钟的配合下,在 SDA
上逐位地串行传送每一位数据。数据位的传输是边沿触发,即每当时钟脉冲发生跳变(从低电平到高电平或从高电平到低电平)时,数据线上的数据会被传输或采样。
⑥空闲状态
IIC
总线的 SDA
和 SCL
两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
I2C 写时序
要在 I2C
总线上写入,主机将在总线上发送一个启动开始标志、从机地址、最后一位R/W位
设置为 0
,这表示写入。从设备发送 ACK
响应确认后,主设备将发送其希望写入的寄存器地址,从设备将再次确认,让主设备知道它已准备就绪。在此之后,主机将开始向从机发送寄存器数据,直到主机发送了它需要的所有数据(有时这只是一个字节),并且主机将以停止标志终止传输。
具体步骤如下:
- 主机发送开始信号。
- 主机发送
I2C
设备地址,每个I2C
器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C
器件。 - 主机发送读写控制位,因为是向
I2C
从设备发送数据,因此是写信号0
。(为1
的话表示这是一个读操作,为0
的话表示这是一个写操作) - 从机发送的
ACK
应答信号。 - 主机发送要写入数据的寄存器地址。
- 从机发送的
ACK
应答信号。 - 主机发送要写入寄存器的数据。
- 从机发送的
ACK
应答信号。 - 主机发送停止信号。
I2C 读时序
主机为了读取从设备的数据,主机必须首先指出希望从从设备的哪个寄存器读取数据。这是由主机写入从设备的“写操作”类似的方式开始传输,通过发送 R/W
位等于 0
的地址(表示写入),然后是它希望从中读取的寄存器地址来完成的。
一旦从设备确认该寄存器地址,主机将再次发送启动条件,然后发送从设备地址,R/W
位设置为 1
(表示读取)。这一次,从设备将确认读取请求,主机释放 SDA
总线,但将继续向从设备提供时钟。在这部分事务中,主机将成为“接收器”,从机将成为“发射器”。
在数据的每个字节结束时,主机将向从设备发送 ACK
,让从设备知道它已准备好接收更多数据。一旦主机接收到预期的字节数,它将发送一个 NACK
,向从设备发送信号以停止通信并释放总线。之后,主机将设置停止条件。
具体步骤如下:
- 主机发送起始信号。
- 主机发送要读取的
I2C
从设备地址。 - 主机发送读写控制位,因为是向
I2C
从设备发送数据,因此是写信号0
。 - 从机发送的
ACK
应答信号。 - 主机发送要读取的寄存器地址。
- 从机发送的
ACK
应答信号。 - 主机发送
START
信号。 - 主机发送要读取的
I2C
从设备地址。 - 主机发送读写控制位,这里是读信号
1
,表示接下来是从I2C
从设备里面读取数据。 - 从机发送的
ACK
应答信号。 - 从机发送主机指定寄存器中的数据,主机读取数据。
- 主机发出
NACK
信号,表示读取完成,不需要从机再发送ACK
信号了。 - 主机发出
STOP
信号,停止I2C
通信。
单个/多个字节的写入/读取
Extend
Dummy Write
“Dummy Write”是指在进行读操作之前,先进行一次写操作,但不向从设备发送有效的数据。 这通常出现在主设备希望从某个特定寄存器读取数据时,特别是当从设备的内部寄存器需要通过指针访问时。
在一些I2C
设备中(例如传感器或存储器),数据是通过设备内部寄存器指针来访问的。为了读取正确的寄存器内容,主设备必须告诉从设备应该从哪个寄存器读取数据。通常主设备会先通过写操作设置寄存器指针。这时主设备进行一次写操作(即“Dummy Write”),但是它并不传输实际的数据,仅仅是为了发送寄存器地址,配置从设备的寄存器指针。
这个“Dummy Write”操作之后,主设备会立即发送读命令(读取数据),从设备就会返回相应的寄存器值。由于I2C
协议本身没有一个直接配置“寄存器指针”的命令,主设备通过这种方式间接地告知从设备寄存器地址,才能进行下一步的读取。
总结:两次设备地址传输: 一次用于写操作(配置寄存器地址),一次用于读操作(从寄存器读取数据)。Dummy Write 主要用于在读取数据之前设置从设备的寄存器地址,确保数据读取是从正确的寄存器位置开始的。
时钟同步
在 I2C
总线上传送信息时的时钟同步信号是由挂接在 SCL
线上的所有器件的 逻辑“与” 完成的。即如果有多个主机同时产生时钟,那么只有所有主机都发送高电平时(释放SCL
),SCL
上才表现为高电平,否则 SCL
都表现为低电平。
SCL
线上由高电平到低电平的跳变将影响到这些器件,一旦某个器件的时钟信号下跳为低电平,将使 SCL
线一直保持低电平,使 SCL
线上的所有器件开始低电平期。此时,低电平周期短的器件的时钟由低至高的跳变并不能影响 SCL
线的状态,于是这些器件将进入高电平等待的状态。当所有器件的时钟信号都上跳为高电平时,低电平期结束,SCL
线被释放返回高电平,即所有的器件都同时开始它们的高电平期。其后,第一个结束高电平期的器件又将 SCL
线拉成低电平。这样就在 SCL
线上产生一个同步时钟。可见,时钟低电平时间由时钟低电平期最长的器件确定,而时钟高电平时间由时钟高电平期最短的器件确定。
总线仲裁
总线仲裁与时钟同步类似,当所有主机在 SDA
上都写 1
时,SDA
的数据才是 1
,只要有一个主机写0
,那此时 SDA
上的数据就是 0
。一个主机每发送一个 bit
数据,在 SCL
为高电平时,就检查 SDA
的电平是否和发送的数据一致,如果不一致,这个主机便知道自己输掉了仲裁,然后停止向 SDA
写数据。也就是说,如果主机一致检查到总线上数据和自己发送的数据一致,则继续传输,这样在仲裁过程中就保证了赢得仲裁的主机不会丢失数据。==输掉仲裁的主机在检测到自己输了之后也就不再产生时钟脉冲,并且要在总线空闲时才能重新传输。==仲裁的过程可能要经过多个 bit
的发送和检查,实际上两个主机如果发送的时序和数据完全一样,则两个主机都能正常完成整个数据传输。
过程中就保证了赢得仲裁的主机不会丢失数据。==输掉仲裁的主机在检测到自己输了之后也就不再产生时钟脉冲,并且要在总线空闲时才能重新传输。==仲裁的过程可能要经过多个 bit
的发送和检查,实际上两个主机如果发送的时序和数据完全一样,则两个主机都能正常完成整个数据传输。