1、简介
I2C(集成电路总线),由Philips公司(2006年迁移到NXP)在1980年代初开发的一种简单、双线双向的同步串行总线,它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递,为设备之间数据交换提供了一种简单高效的方法。每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。
I2C 标准是一个具有冲突检测机制和仲裁机制的真正意义上的多主机总线,它能在多个主机同时请求控制总线时利用仲裁机制避免数据冲突并保护数据。作为嵌入式开发者,使用I2C总线通信的场景有很多,例如驱动FRAM、E2PROM、传感器等。
总结来说,I2C总线具有以下特点:
-
只需要SDA、SCL两条总线;
-
没有严格的波特率要求;
-
所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一地址进行软件寻址;
-
I2C是真正的多主设备总线,可提供仲裁和冲突检测;
-
传输速度分为四种模式:
标准模式(Standard Mode):100 Kbps
快速模式(Fast Mode):400 Kbps
高速模式(High speed mode):3.4 Mbps
超快速模式(Ultra fast mode):5 Mbps
-
最大主设备数:无限制;
-
最大从机数:理论上,1008个从节点,寻址模式的最大节点数为2的7次方或2的10次方,但有16个地址保留用于特殊用途。
I2C有16个保留I2C地址。这些地址对应于以下两种模式之一:0000 XXX或1111 XXX。下表显示了为特殊目的而保留的I2C地址。
I2C 节点地址 | R/W | 位功能描述 |
0000 000 | 0 | 广播地址 |
0000 000 | 1 | 起始字节 |
0000 001 | X | CBUS 地址 |
0000 010 | X | 保留用于不同总线格式 |
0000 011 | X | 保留供未来使用 |
0000 1XX | X | 高速模式主代码 |
1111 1XX | X | 保留供未来使用 |
1111 0XX | X | 10位节点地址 |
I2C还有两个变体,分别专注于系统和电源应用,称为系统管理总线(SMBus)和电源管理总线(PMBus)。
2、物理特性
2.1 I2C总线的硬件连接
I2C 总线使用连接设备的 "SDA"( 串行数据总线)和"SCL"( 串行时钟总线 ) 来传送信息。
I2C 总线内部使用漏极开路输出驱动器,因此 SDA和 SCL 可以被拉低为低电平,但是不能被驱动为高电平,所以每条线上都要使用一个上拉电阻,默认情况下将其保持在高电平。
2.2 使用开漏进行双向通信
SDA/SCL 线的基本内部结构
开漏是指一种输出类型,它可以将总线拉低到电压(在大多数情况下为地),或者“释放”总线,让它被上拉电阻拉起。如果总线由主机或从机释放,线路上的上拉电阻 (RPU) 负责将总线电压拉至电源轨。由于没有设备可以强制拉高线路上的电压,这意味着总线永远不会遇到通信问题,即一个设备可能尝试发送高电平,而另一个设备发送低电平,导致短路(电源轨对地)。I2C 要求,如果多主机环境中的一个主机准备传输高电平,但看到线路电平为低电平(另一个设备正在将其拉低),说明另一个设备正在使用总线,因而将会停止通信。推挽式接口不允许这种类型的自由,这是 I2C 的优势。
开漏输出允许线与(与门)的逻辑功能,导致某个设备输出为低时,线路就是低电平,而输出高电平时,可以通过输入缓冲器来判断总线的电平,就不需要再修改IO口的输入输出模式了,对于代码编写软件模拟I2C 来说是极其方便的。
开漏拉低:
如上一节所述,漏极开路设置只能将总线拉低,或“释放”总线,让电阻将其拉至高电平。下图显示了将总线拉低的电流。想要发送低电平的逻辑,将激活下拉式FET,这将提供接地短路,从而将线路拉低。
开漏释放总线:
当从机或主机希望发送逻辑高电平时,它只能通过关闭下拉式FET来释放总线。这使得总线悬空,上拉电阻将电压拉到电压轨,这将被解释为高电平。 下图显示了流经上拉电阻的电流,该电阻将总线拉至高电平。
2.3 I2C总线上拉电阻阻值的计算
I2C 总线上拉电阻阻值取决于系统应用,TI 官方手册推荐使用以下公式来计算上拉电阻值:
根据上表,这里不难发现需要在做电阻选择需要满足几个条件:
-
灌电流最大值为3mA;
-
低电平输出电压设置了最大值为0.4V。
所以根据上述公式可以计算,对于5V的电源,每个上拉电阻阻值至少1.53kΩ,而对于3.3V的电源,每个电阻阻值至少967Ω。
如果觉得计算电阻值比较麻烦,也可以使用典型值 4.7kΩ。若各位想了解更多可直接参见手册说明。
3、通讯特性
通常情况下,一个完整的I2C通信过程包括以下 4 部分:
-
开始条件
-
地址传送
-
数据传送
-
停止条件
主机在 SCL 线上输出串行时钟信号,数据在 SDA 线上进行传输,每传输一个字节(最高位 MSB 开始传输)后面跟随一个应答位,一个 SCL 时钟脉冲传输一个数据位。
标准的I2C时序如下图所示:
3.1 开始和停止条件
当总线上的主机都不驱动总线,总线进入空闲状态, SCL 和 SDA 都为高电平。总线空闲状态下总线上设备都可以通过发送开始条件启动通信。
当 SCL 线为高时,SDA 线上出现由高到低的信号,表明总线上产生了起始信号。 SDA 线上出现由低到高的信号,表明总线上产生了停止信号,如下图所示:
当两个起始信号之间没有停止信号时,即产生了重复起始信号。主机采用这种方法与另一个从机或相同的从机以不同传输方向进行通信(例如:从写入设备到从设备读出)而不释放总线。如下图所示:
3.2 地址传送
开始条件或者重新开始条件后面的帧是地址帧(一个字节),用于指定主机通信的对象地址,在发送停止条件之前,指定的从机一直有效。
I2C通讯支持:7 位寻址和10 位寻址两种模式。
7 位寻址模式,地址帧(8bit)的高 7 位为从机地址,地址帧第 8 位来决定数据帧传送的方向:7 位从机地址 + 1位 读/写位,读/写位控制从机的数据传输方向(0:写; 1:读) 。帧格式如下所示:
10 位寻址模式,主机发送帧,第一帧 发送头序列(11110XX0,其中 XX 表示 10 位地址的高 两位),然后第二帧发送低八位从机地址。 主机接收帧 ,第一帧发送头序列(11110XX0,其中 XX 表示 10 位地址的高两位),然后第二帧发送低八位从机地址。接下来会发送一个重新开始条件,然后再发送一帧头序列(11110XX1 ,其中 XX 表示 10 位地址的高两位)帧格式如下所示:
解析如下:
-
S :表示开始条件;
-
SLA :表示从机地址;
-
R/W#:表示发送和接收的方向。当 R/W# 为“1” 时,将数据从从机发送到主机;当 R/W#为“0” 时,将数据从主机发送到从机;
-
Sr :表示重新开始条件;
-
DATA :表示发送和接收的数据;
-
P :表示停止条件。
3.3 数据传送与ACK
地址匹配一致后,总线上的主机根据 R/W 定义的方向一帧一帧的传送数据。 所有的地址帧后传送的数据都视为数据帧。10 位地址格式的低 8 位地址也视为数据帧。
数据帧的长度是 8 位。 SCL 的低电平 SDA 变化, SCL 的高电平 SDA 保持,每个时钟周期发送一位数据。数据帧后的第 9 个时钟是应答位,是接收方传送的握手信号。
从机接收数据时,第 9 个时钟周期不响应主机,从机必须发送 NACK;主机接收数据时,发送 NACK,从机接收到后停止发送数据;主机可以发送停止条件释放总线或重新开始条件;
如果从端设备无法接收或者发送另一个完整字节的数据。这时,可以先拉低SCL线,使得主设备进入等待状态,直到执行完毕其他功能。当从端设备准备好接受另一个字节的数据时,将其SCL线拉高,数据传输继续进行。
3.4 在 I2C 总线上写入从机
要在 I2C 总线上写入,从机将在总线上发送一个起始条件,其中包含从机的地址,以及设置为0的最后一位 (R/W位) 这表示写入。在从机发送应答位后,主机将发送它希望写入的寄存器的寄存器地址。从机会再次应答,让主机知道它已经准备好了。在此之后,主机将开始将寄存器数据发送到从机,直到主机发送了它需要的所有数据(有时这只是一个字节),主机将以STOP条件终止传输。
3.5 从I2C 总线上的从机读取数据
从从机那里读取与写入非常相似,但有一些额外的步骤。为了从从机中读取,主机必须首先告诉从机希望读取的寄存器。这是由主机以与写入类似的方式开始传输来完成的,方法是发送 R/¯W 位等于0的地址(表示写入),然后是它希望读取的寄存器地址。一旦从机应答了此寄存器地址,主机将再次发送START条件,然后是 R/¯W 位设置为1(表示读取)的从地址。这一次,主机将应答读取请求,主机释放SDA总线,但将继续向从机提供时钟。在这一部分通讯中,主机将成为主-接收方,从机将成为从-发送器。
主机将继续发出时钟脉冲,但会释放SDA线,以便从机可以传输数据。在每个数据字节结束时,从机将向从机发送一个ACK,让从机知道它已经准备好处理更多数据。一旦主机收到它所期望的字节数,它将发送一个NACK,向从机发出STOP 条件以停止通信并释放总线。
3.6 总线仲裁
1)线与
I2C 支持多个主设备与多个从设备连接在同一根总线上,如果多个设备同时占用总线,怎么判断谁先占用总线呢?所以就需要一种仲裁机制。I2C 没有 Arbiter 直接的来处理仲裁,而是通过线与的逻辑实现仲裁。
2)遵循3个机制
-
“线与”机制。多主机时,总线具有“线与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。
-
SDA回读机制。总线被启动后,多个主机在每发送一个数据位时都要对自己的输出电平进行检测,只要检测的电平与自己发出的电平一致,就会继续占用总线。
-
低电平优先机制。由于线与的存在,当多主机发送时,谁先发送低电平谁就会掌握对总线的控制权。
3)仲裁过程
当主设备A准备占用I2C时,需要在SCL为高时,将SDA拉高,再拉低,满足一个启动条件。当主设备A将SDA拉高后,需要检查SDA的电平:
-
如果此时SDA电平为高,说明主设备可以占用总线,然后主设备A会将SDA拉低,一次满足启动条件,开始传输;
-
如果此时SDA电平为低,说明总线已经被其他设备占用,主设备A会退出。
为什么SDA为低,就是被其他设备占用了呢?
因为线与逻辑的存在。只有总线上有其他的设备将SDA置为0,线与后,SDA线的电平为0。主设备A检查SDA线的电平时,会发现为低电平。所以仲裁时,哪个设备更早地将SDA线拉低,谁就抢占了优先权。
4)仲裁过程实例
在图中不难看出DATA1发送的数据是 10111....
DATA2发送的数据是 100101....
在起始信号的被DATA1先行拉低;
在1、2周期的时候DATA1、2的数据位都是一样的,保持持续仲裁,当在第三周期时DATA1的数据位是1,DATA2的数据位是0,根据总线具有“线与”的逻辑功能(低电平优先),DATA2赢得仲裁,DATA1失去总线控制权。
3.7 时钟同步/时钟延展
I2C规范没有为时钟同步规定任何超时条件,也就是说,任何器件都可以根据需要保持SCL。
在I2C通信协议中,时钟速度和信号始终由主器件产生。I2C主器件产生的信号提供主器件和节点连接之间的同步。
在某些情况下,节点或子节点不是以全状态工作,在接收主器件生成的时钟之前,需要减慢速度。这是通过一种称为"时钟同步/时钟延展"的机制来实现的。
在时钟同步/时钟延展期间,为了降低总线速度,允许节点压低时钟。而在主器件方面,在其变为高电平状态后,必须回读时钟信号。然后,它必须等待,直至线路达到高电平状态。
通过时钟同步/时钟延展,I2C节点器件可以强制主器件进入等待状态。当节点器件需要更多时间来管理数据时,例如存储接收到的数据或准备发送另一字节的数据时,它可能会执行时钟同步/时钟延展。这通常发生在节点器件接收并确认收到一个字节的数据之后。
是否需要时钟延展取决于节点器件的功能。这里有两个例子:
处理器件(如微处理器或微控制器)可能需要额外的时间来处理中断,接收和管理数据,以及执行适当的功能;
较简单的器件(如EEPROM)不在内部处理数据,因此不需要时钟延展来执行任何功能。
4、通信时序和协议
说起I2C通信协议必然离不开通信时序,主器件和从节点必须遵守I2C时序规格才能正确传输数据。
下表显示了时序规格表上给出的符号和参数。
符号 | 参数 | 单位 |
fSCL | SCL 时钟频率 | kHz |
tHD(STA) | (重复)起始条件的保持时间 | µs |
tLOW | 引脚的低电平周期 | µs |
tHIGH | 引脚的高电平周期 | µs |
tSU(STA) | 重复起始条件的建立时间 | µs |
tHD(DAT) | 数据保持时间 | µs |
tSU(DAT) | 数据建立时间 | ns |
tr | SDA 信号的上升时间 | ns |
tf | SDA 信号的下降时间 | ns |
tSU(STO) | 停止条件的建立时间 | µs |
4.1 起始条件
起始条件总是在传输开始时出现,并由主器件发起。这样做是为了唤醒总线上的空闲节点器件。SDA线从高电平切换到低电平,然后SCL线从高电平切换到低电平。时序和协议如下图所示:
4.2 重复起始条件
在不发出停止条件的情况下,起始条件可以在传输期间重复。这是一种特殊情况,称为重复起始,用于改变数据读、写传输方向、重复尝试传输、同步多个IC,甚至控制串行存储器等。如下图所示:
4.3 地址帧
地址帧包含7位或10位序列,具体取决于可用性(参见数据手册)。如下图所示:
不像SPI协议,I2C没有节点选择线路,因此它需要另一种方法来让节点知道数据正向其发送,而不是向另一个节点发送。这是通过寻址来实现的。地址帧始终是新消息中起始位之后的第一帧。
主器件将其想要与之通信的节点地址发送到其所连接的每个节点。然后,每个节点将主器件所发送的地址与其自己的地址进行比较。如果地址匹配,它便向主器件发送一个低电压ACK位。如果地址不匹配,则节点什么也不做,SDA线保持高电平。
4.4 读⁄写位
地址帧的最后一位告知节点,主器件是想要将数据写入其中还是从中接收数据。如果主器件希望将数据发送到节点,则读⁄写位处于低电平。如果主器件请求从节点得到数据,则该位处于高电平。如下图所示:
4.5 ACK⁄NACK位
消息中的每一帧后面都跟随一个应答⁄不应答位。如果成功接收到一个地址帧或数据帧,则从机会向主机返回一个ACK位。如下图所示:
4.6 数据帧
主器件检测到来自从节点的ACK位之后,就准备发送第一数据帧。数据帧总是8位长,并以MSB优先方式发送。每个数据帧之后紧接着一个ACK⁄NACK位,以验证该帧是否已成功接收。主器件或节点(取决于谁发送数据)必须收到ACK位,然后才能发送下一数据帧。时序和协议如下图所示:
4.7 停止条件
发送完所有数据帧之后,主器件可以向节点发送停止条件以停止传输。停止条件是指SCL线上的电压从低电平变为高电平,然后在SCL线保持高电平的情况下,SDA线上的电压从低电平变为高电平。时序和协议如下图所示:
5、工作过程
第1步:起始条件
第2步:发送从设备地址
主设备向每个从机发送要与之通信的从机的7位或10位地址,以及相应的读/写位,如下图所示:
第3步:接收应答
每个从设备将主设备发送的地址与其自己的地址进行比较。如果地址匹配,则从设备通过将SDA线拉低一位以表示返回一个ACK位。
如果来自主设备的地址与从机自身的地址不匹配,则从设备将SDA线拉高,表示返回一个NACK位。
第4步:收发数据
主设备发送或接收数据到从设备,如下图所示:
第5步:接收应答
在传输完每个数据帧后,接收设备将另一个ACK位返回给发送方,以确认已成功接收到该帧,如下图所示:
第6步:停止通信
为了停止数据传输,主设备将SCL切换为高电平,然后再将SDA切换为高电平,从而向从机发送停止条件,如下图所示:
5.1 单个主设备连接多个从机
I2C单个主设备连接多个从机。使用7位地址可提供128 (2的7次方)个唯一地址。使用10位地址很罕见,但可提供1024 (2的10次方)个唯一地址。要将多个节点连接到单个主器件,请使用4.7 kΩ上拉电阻连接这些节点,并将SDA和SCL线连接到VCC,如下图所示:
5.2 多个主设备连接多个从机
多个主设备可以连接到一个或多个从机。
当两个主设备试图通过SDA线路同时发送或接收数据时,同一系统中的多个主设备就会出现问题。
为了解决这个问题,每个主设备都需要在发送消息之前检测SDA线是低电平还是高电平;
-
如果SDA线为低电平,则意味着另一个主设备可以控制总线,并且主设备应等待发送消息;
-
如果SDA线为高电平,则可以安全地发送消息。
参考链接: