I2C
即
Inter-Integrated Circuit(
集成电路总线),是由
Philips
半导体公司(现在的
NXP
半导体公司)在八十年代初设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短 的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从 机。
I2C
总线由数据线
SDA
和时钟线
SCL
构成通信线路,既可用于发送数据,也可接收数据。在主控与被控 IC
之间可进行双向数据传送,数据的传输速率在标准模式下可达
100kbit/s
,在快速模式下可达
400kbit/s
, 在高速模式下可达 3.4Mbit/s
,各种被控器件均并联在总线上,通过器件地址(
SLAVE ADDR
,具体可查器件手册)识别。
I2C_SCL
是串行时钟线,
I2C_SDA
是串行数据线,由于
I2C
器件一般采用开漏结构与总线相连,
所以
I2C_SCL
和
I2C_SDA
均需接上拉电阻,也正因此,当总线空闲时,这两条线路都处于高电平状态,当 连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的 SDA
及
SCL
都是“线与”关系。
I2C
总线支持多主和主从两种工作方式,通常工作在主从工作方式,我们的开发板就采用主从工作方式。 在主从工作方式中,系统中只有一个主机,其它器件都是具有 I2C
总线的外围从机。在主从工作方式中, 主机启动数据的发送(发出启动信号)并产生时钟信号,数据发送完成后,发出停止信号。
I2C
总线结构虽然简单,使用两线传输,然而要实现器件间的通信,需要通过控制
SCL
和
SDA
的时序, 使其满足 I2C
的总线传输协议,方可实现器件间的数据传输。那么
I2C
协议的时序是怎样的呢?
在
I2C
器件开始通信(传输数据)之前,串行时钟线
SCL
和串行数据线
SDA
线由于上拉的原因处于高电平状态,此时 I2C
总线处于空闲状态。如果主机(此处指
FPGA
)想开始传输数据,只需在
SCL
为高电平时将 SDA
线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在 SCL
为高电平时,
SDA
从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。I2C
整体时序如下图。起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传输状态,主机可以向从机写数据,也可以读取从机输出的数 据,数据的传输由双向数据线(SDA)完成。停止信号产生后,总线再次处于空闲状态。
![](https://i-blog.csdnimg.cn/direct/358538a437144e62bc58cfbac960ee07.png)
![](https://i-blog.csdnimg.cn/direct/a6f78eb46772404cb8c210fb16d085b1.png)
由图
20.1.3
可知,我们在起始信号之后,主机开始发送传输的数据;在串行时钟线 SCL
为低电平状态时,
SDA
允许改变传输的数据位(1 为高电平,
0
为低电平),在 SCL
为高电平状态时,
SDA
要求保持稳定,相当于一个时钟周期传输
1bit
数据,经过
8
个时钟周期后,传输了 8bit
数据,即一个字节。第
8
个时钟周期末,主机释放
SDA
以使从机应答,在第
9
个时钟周期,从机将 SDA
拉低以应答;如果第
9
个时钟周期,
SCL
为高电平时,
SDA
未被检测到为低电平,视为非
应答,表明此次数据传输失败。第
9
个时钟周期末,从机释放
SDA
以使主机继续传输数据,如果主机发送停止信号,此次传输结束。我们要注意的是数据以 8bit
即一个字节为单位串行发出,其最先发送的是字节的最高位。
I2C
的时序部分已经基本介绍完了,但还有一个小问题,就是当多个
I2C
器件挂接在总线上时,怎样才能与我们想要传输数据的器件进行通信。这就涉及到了器件地址(也称从机地址,SLAVE ADDRESS
)。 每个 I2C
器件都有一个器件地址,有些
I2C
器件的器件地址是固定的,而有些
I2C
器件的器件地址由一个固定部分和一个可编程的部分构成,这是因为很可能在一个系统中有几个同样的器件,器件地址的可编程部分能最大数量的使这些器件连接到 I2C
总线上,例如
EEPROM
器件,为了增加系统的
EEPROM
容量,可能需要多个 EEPROM
。器件可编程地址位的数量由它可使用的管脚决定,比如
EEPROM
器件一般会留下 3
个管脚用于可编程地址位。但有些
I2C
器件在出厂时器件地址就设置好了,用户不可以更改(如实时时钟 PCF8563
的器件地址为固定的
7’h51
)。所以当主机想给某个器件发送数据时,只需向总线上发送接 收器件的器件地址即可。
对于
AT24C64
而言,其器件地址为
1010
加
3
位的可编程地址,
3
位可编程地址由器件上的
3
个管脚A2、
A1
、
A0
(见图
20.3.2
)的硬件连接决定。当硬件电路上分别将这三个管脚连接到
GND
或
VCC
时,就可以设置不同的可编程地址。对于我们的开发板,这 3
个管脚连接到地。
进行数据传输时,主机首先向总线上发出开始信号,对应开始位
S
,然后按照从高到低的位序发送器件地址,一般为 7bit
,第
8bit
位为读写控制位
R/W
,该位为
0
时表示主机对从机进行写操作,当该位为
1
时表示主机对从机进行读操作,然后接收从机响应。对于 AT24C64
来说,其传输器件地址格式如下图所示。
![](https://i-blog.csdnimg.cn/direct/8b19dab686ab4c1ca2cc3b3f58f5dd7e.png)
发送完第一个字节(7 位器件地址和一位读写控制位)并收到从机正确的应答后就开始发送字地址(
Word Address)。一般而言,每个兼容
I2C
协议的器件,内部总会有可供读写的寄存器或存储器,对于我们本次实验用到的 EEPROM
存储器,内部就是一系列顺序编址的存储单元。所以,当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度,具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256
)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示,例如同是 EEPROM
存储器,
AT24C02
的存储单元容量为
2Kbit=256Byte
(一般
bit
缩写为
b
,Byte 缩写为
B
),用一个字节地址即可寻址所有的存储单元,而
AT24C64
的存储单元容量为
64Kb=8KB
, 需要 13
(
2^13=8KB
)的地址位,而
I2C
又是以字节为单位进行传输的,所以需要用两个字节地址来寻
址整个存储单元。图
20.1.5
和图
20.1.6
分别为单字节字地址和双字节字地址器件的地址分布图,其中单字 节字地址的器件是以存储容量为 2Kb
的
EEPROM
存储器
AT24C02
为例,双字节字地址的器件是以存储容量为 64Kb
的
EEPROM
存储器
AT24C64
为例,
WA7
即字地址
Word Address
的第
7
位,以此类推,用
WA是为了区别前面器件地址中的A
。
![](https://i-blog.csdnimg.cn/direct/e2e6de2b986a44de91151bc4e74faca6.png)
![](https://i-blog.csdnimg.cn/direct/fca1c6e3dde84cd2ab6c6f8ab4330157.png)
主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位
R/W 位为“0
”即写命令,从机就处于接收数据的状态,此时,主机就开始写数据了。写数据分为单次写(对于EEPROM 而言,称为字节写)和连续写(对于
EEPROM
而言,称为页写),那么这两者有什么区别呢?对比图 20.1.7
和图
20.1.8
可知,两者的区别在于发送完一字节数据后,是发送结束信号还是继续发送下一字节数据,如果发送的是结束信号,就称为单次写,如果继续发送下一字节数据,就称为连续写。图 20.1.7是 AT24C64
的单次写(字节写)时序,对于字地址为单字节的
I2C
器件而言,在发送完字地址(对应图
20.1.7的字地址高位),且从机应答后即可串行发送 8bit
数据。图
20.1.8
是
AT24C64
连续写(页写)时序。要注意的是,对于 AT24C64
的页写,是不能发送超过一页的单元容量的数据的,而
AT24C64
的一页的单元容量为 32Byte
,当写完一页的最后一个单元时,地址指针指向该页的开头,如果再写入数据,就会覆盖该页的起始数据。
![](https://i-blog.csdnimg.cn/direct/4e3c89dfe84c4fd8a6f7b2c5a7f20426.png)
![](https://i-blog.csdnimg.cn/direct/3ee4792bac7e4d058d5f957a5276b712.png)
如果读写控制位
R/W
位为“
1
”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。 读数据有三种方式:当前地址读、随机读和连续读。当前地址读是指在一次读或写操作后发起读操作。由于 I2C
器件在读写操作后,其内部的地址指针自动加一,因此当前地址读可以读取下一个字地址的数据。 也就是说上次读或写操作的单元地址为 02
时,当前地址读的内容就是地址
03
处的单元数据,时序图如图20.1.9 所示。
![](https://i-blog.csdnimg.cn/direct/2ae793492649471da241e1559700cabe.png)
由于当前地址读极不方便读取任意的地址单元的数据,所以就有了随机读,随机读的时序有点奇怪, 见图 20.1.10
,发送完器件地址和字地址后,竟然又发送起始信号和器件地址,而且第一次发送器件地址时后面的读写控制位为“0
”,也就是写命令,第二次发送器件地址时后面的读写控制位为“
1
”,也就是读。 为什么会有这样奇怪的操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次 Dummy Write
也就是虚写操作,只所以称为虚写,是因为我们并不是真的要写数据,而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以以当前地址读的方式读数据了,如图 20.1.10
所示,随机地址读是没有发送数据的单次写操作和当前地址读操作的结合体。
![](https://i-blog.csdnimg.cn/direct/c525c87cd56e465abb9291c1151e69d5.png)
至于连续读,对应的是当前地址读和随机读都是一次读取一个字节而言的,它是将当前地址读或随机 读的主机非应答改成应答,表示继续读取数据,图 20.1.11
是在当前地址读下的连续读。
![](https://i-blog.csdnimg.cn/direct/ace16580c4b843e1af112a0f07592eb3.png)
至此,
I2C
协议就基本讲完了,本章我们主要采用单次写和随机读的方式进行
EEPROM
读写测试。