I2C总线,全称为Inter-Integrated Circuit(集成电路互联总线),是MCU中常用的接口模块。
它是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
SDA 线上的数据必须在时钟的高电平周期保持稳定数据线的高或低电平状态,
只有在SCL 线的时钟信号是低电平时才能改变。为传输的每个数据位生成一个时钟脉冲。
所有的传输都是所有事务都以START(S)开始,并由STOP(P)终止(见图5)。
SCL 线是高电平时,SDA 线从高电平向低电平切换,表示起始条件。
SCL 是高电平时,SDA 线由低电平向高电平切换,表示停止条件。
起始和停止条件一般由主机产生,总线在起始条件后被认为处于忙的状态,
在停止条件的某段时间后,总线被认为再次处于空闲状态总线的空闲状态将在第15 章详细说明
如果产生重复起始Sr 条件而不产生停止条件总线会一直处于忙的状态此时的起始条件S和重复起始Sr 条件在功能上是一样的见下文。
发送到SDA 线上的每个字节必须为8 位每次传输可以发送的字节数量不受限制,每个字节后必须跟一个响应位,首先传输的是数据的最高位MSB 。
如果从机要完成一些其他功能,例如一个内部中断服务程序才能接收或发送下一个完整的数据字节,可以使时钟线SCL 保持低电平迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。
响应发生在每个字节之后。 应答位允许接收器向发送器发信号通知该字节已成功接收,并且可以发送另一个字节。 主机产生所有时钟脉冲,包括应答第9个时钟脉冲。
响应分为ACK和NACK。
ACK 信号:在响应的时钟脉冲期间,接收器必须将SDA 线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平 。当然必须考虑建立和保持时间。
NACK信号:当在第9个时钟脉冲期间SDA保持高电平时,这被定义为非应答信号。主设备可以生成STOP条件以中止传输,或者生成重复的START条件以开始新的传输。 导致生成NACK的条件有五个:
在传输过程中,接收器获取它不理解的数据或命令。
在传输过程中,接收器无法再接收数据字节。
主接收器必须发信号通知从发送器的传输结束。
两个主设备可以同时开始在空闲总线上进行传输,并且必须有一种方法来决定哪个控制总线并完成其传输。 这是通过时钟同步和仲裁完成的。 在单主系统中,不需要时钟同步和仲裁。
时钟同步通过线与连接I2C 接口到SCL 线来执行。这就是说SCL 线的高到低切换会使器件开始数它们的低电平周期,而且一旦器件的时钟变低电平,它会使SCL线保持这种状态直到到达时钟的高电平。但是,如果另一个时钟仍处于低电平周期,这个时钟的低到高切换不会改变SCL线的状态。因此,SCL线被有最长低电平周期的器件保持低电平,此时,低电平周期短的器件会进入高电平的等待状态。
当所有有关的器件数完了它们的低电平周期后,时钟线被释放并变成高电平。之后器件时钟和SCL线的状态没有差别。而且所有器件会开始数它们的高电平周期。首先完成高电平周期的器件会再次将SCL线拉低。
这样,产生的同步SCL时钟的低电平周期由低电平时钟周期最长的器件决定,而高电平周期由高电平时钟周期最短的器件决定。
仲裁与同步一样,只有在系统中使用多个主设备时才需要协议的一部分。主机只能在总线空闲的时侯启动传输。两个或多个主机可能在起始条件的最小持续时间(tHD;STA)内产生一个起始条件,结果在总线上产生一个规定的起始条件。
当SCL 线是高电平时,仲裁在SDA 线发生;这样,在其他主机发送低电平时,发送高电平的主机将断开它的数据输出级,因为总线上的电平与它自己的电平不相同。
仲裁是逐位进行的,在每个位期间,当SCL为高电平时,每个主器件都会检查SDA电平是否与它发送的电平相匹配。 此过程可能需要很多位。 只要传输完全相同,两个主设备实际上可以无错误地完成整个事务。 主设备第一次尝试发送HIGH,但检测到SDA电平为低电平,主机知道它已经丢失仲裁并关闭其SDA输出驱动器。
因为,I2C 总线的地址和数据信息由赢得仲裁的主机决定,在仲裁过程中不会丢失信息。丢失仲裁的主机,可以产生时钟脉冲直到丢失仲裁的该字节末尾。
如果主机也结合了从机功能,而且在寻址阶段丢失仲裁,它很可能就是赢得仲裁的主机在寻址的器件。因此,丢失仲裁的主机必须立即切换到它的从机模式。
由于I2C总线的控制仅取决于竞争主机发送的地址和数据,因此没有中央主机,也没有总线上的任何优先级顺序。
如果在一个主设备发送重复START或STOP条件而另一个主设备仍在发送数据时仲裁过程仍在进行中,则存在未定义的条件。 换句话说,仲裁在不能下面情况之间进行:
时钟延长通过将SCL线保持为低电平来暂停事务。 在该行再次释放HIGH之前,该事务不能继续。 时钟延长是可选的,事实上,大多数从设备不包括SCL驱动程序,因此它们无法延长时钟。
在字节级,设备可能能够以快速速率接收数据字节,但需要更多时间来存储接收到的字节或准备另一个要传输的字节。 然后,从器件可以在接收和确认一个字节后将SCL线保持为低电平,以强制主器件进入等待状态,直到从器件准备好在一种握手过程中进行下一个字节传输。
在位级,诸如具有或不具有用于I2C总线的有限硬件的微控制器的设备可以通过延长每个时钟LOW周期来减慢总线时钟。 任何主站的速度都适合该设备的内部运行速率。
数据的传输遵循图10 所示的格式,在起始条件S 后发送了一个从机地址,这个地址共有7 位,紧接着的第8 位是数据方向位R/W ,0 表示发送写,1 表示请求数据读,数据传输一般由主机产生的停止位P 终止,但是如果主机仍希望在总线上通讯它可以产生重复起始条件Sr和寻址另一个从机,而不是首先产生一个停止条件,在这种传输中可能有不同的读写格式结合。
I2C 总线的寻址过程是通常在起始条件后的第一个字节决定了主机选择哪一个从机例外的情况是可以寻址所有器件的广播呼叫地址使用这个地址时理论上所有器件都会发出一个响应但是也可以使器件忽略这个地址。
如果器件要求从广播呼叫地址得到数据它会响应这个地址并作为从机-接收器运转,第二个和接下来的字节会被能处理这些数据的每个从机-接收器响应。广播呼叫地址的含意通常在第二个字节说明。
10位寻址扩展了可能的地址数量。 具有7位和10位地址的器件可以连接到同一个I2C总线,7位和10位寻址都可以用于所有总线速度模式。 目前,10位寻址没有被广泛使用。
10位从地址由START条件(S)或重复START条件(Sr)之后的前两个字节构成。
第一个字节的前七位是组合1111 0XX,其中最后两位(XX)是10位地址的两个最高有效位(MSB); 第一个字节的第八位是决定消息方向的R / W位。
尽管保留地址位1111XXX有八种可能的组合,但只有四种组合1111 0XX用于10位寻址。 其余四种组合1111 1XX保留用于未来的I2C总线增强。
之前针对7位寻址描述的读/写格式的所有组合都可以通过10位寻址实现。 这里有两个详细说明:
主机发送器传输10位从地址发送到从接收器。
主接收器使用10位从地址读取从发送器。
最初,I2C总线限制为100 kbit / s操作。 随着时间的推移,规范已经增加了几个,因此现在有五种运行速度类别。 标准模式,快速模式(Fm),快速模式加(Fm +)和高速模式(Hs模式)设备向下兼容 - 任何设备都可以在降低总线速度。
超快速模式设备与以前的版本不兼容,因为总线是单向的。
双向总线:
带有快速或Hs 模式I2C 总线接口的新从机器件可以有7 位或10 位的从机地址,如果可能的话首选7 位地址因为它是最便宜的硬件解决方案而且报文长度最短
单向总线:
在蜂鸟e203中,采用的是Open core的开源I2C,感兴趣的同学可以在opencore上自行下载,整个模块分为三个层次:
顶层模块如图所示,定义了一个时钟端口,两个复位端口.
因为I2C模块是通过Wishbone总线进行输入,所以相应的有we,stb和cyc三个使能输入端口
Wb_adr_i和Wb_dat_i是地址和数据输入,adr端口连接到7个可配置寄存器,如下表所示。
这里要注意,opencore的I2C并没有直接定义双向端口SCL,SDA,所以需要我们自己在外面接一个IOBUF来驱动SCL和SDA总线,如下图所示:
顶层模块原理图:
寄存器配置表:
[注]表中寄存器在E203系统中的基地址位0x1004_20xx.
prerlo和prerhi是时钟分频寄存器,通过对I2C模块所在时钟域的时钟,进行分频,然后得到SCL的时钟频率,计算公式为:
Prescale寄存器的值=“I2C模块所处的时钟域的时钟频率”/((5*“SCL时钟频率”)-1)。
CTR寄存器是控制寄存器,用来控制I2C模块的功能和中断,控制信号分别对应Wb_dat_i的第7位和第6位,如下表所示:
I2C模块通过I2C_TXR寄存器发送数据,通过I2C_RXR寄存器接收数据。
TXR和RXR寄存器共用一个地址,一个负责写,一个负责读。
[注]I2C协议规定,在I2C传输从设备地址字节是,最低位表示读或者写操作。
I2C模块通过CR寄存器发送命令请求,CR寄存器在每个命令完成之后都会自动清除,因此I2C需要在发送命令之前重新对命令寄存器写值。
SR寄存器为只读寄存器,负责查看I2C的状态,其功能如下表所示。
CR寄存器:
SR寄存器:
byte_controller以字节为单位处理I2C的数据, 它从命令寄存器中获取数据,并根据单个字节的传输将其转换为序列。 例如,通过将命令寄存器中的START,STOP和READ位置1,字节命令控制器会生成一个序列,该序列导致产生START信号,从从设备读取一个字节并产生一个字节。 停止信号。 它通过将每个字节操作划分为单独的位操作来完成此操作,然后将这些位操作发送到位命令控制器。
bit_controller通过控制SCL和SDA线的oen来处理数据的实际传输以及START,重复的START和STOP信号的特定电平的生成。 字节命令控制器告诉位命令控制器必须执行哪个操作。 对于单字节读取,位命令控制器接收8个单独的读取命令。 每个位操作分为5个部分(空闲和A,B,C和D),而STOP操作分为4个部分(空闲和A,B和C)。
Opencore的I2C_SLAVE只有两个端口SCL,SDA,其中SDA是双向端口。
Slave在检测到start标志和stop标志时,产生复位信号,随后根据命令进行数据传输,
如下图所示:
[1]广州周立功单片机发展有限公司.《I2C总线规范》.
[2]电子工程世界.《I2C总线详解笔记》.
[3]胡振波.《RISC-V架构嵌入式开发快速入门》.