【从零学习硬件】I2C总线协议学习
I2C协议
I2C是由 Phiilps提出的,目前被广泛应用在系统内多个IC间的通讯。
I2C是每个连接到总线的设备都有一个独立的地址,主机可以通过该地址来访问不同设备。主机通过SDA线发送设备地址(SLAVE_ADDRESS)查找从机,SLAVE_ADDRESS可以是7位或10位,紧跟着SLAVE_ADDRESS的一个数据位用来表示数据传输方向,即第8位或11位。
物理层介绍
I2C总线是一种双向的同步串行总线,它支持设备之间的短距离通信,经常用于处理器和一些外围设备之间的接口通信。
通信模式 | 速率 | 电容负载 | 上拉电阻 |
标准模式 | 100Kbps | 400pF | |
快速模式 | 400Kbps | 200pF | |
高速模式 | 3.4Mbps |
一个支持多个设备的总线,I2C总线物理层由两根线组成:串行时钟线SCL、串行数据线SDA。由于这两根线都是开漏输出结构,因此必须都接上拉电阻到高电平,因此当总线处于空闲状态时,两根线都处于高电平状态。
而且各个设备之间的SCL和SDA线都是线与关系。I2C总线上扩展的器件的数量主要由电容负载来决定,其负载能力为400pF。I2C总线具有极低的电流消耗。0时表示写;1时表示读。
I2C总线的上拉电阻的阻值要精心考虑,
- 上拉电阻的阻值太大,则由于I2C设备输入端的输入电容的存在,会造成信号上升沿和下降沿变缓,以至于不能满足I2C设备的建立时间和保持时间,造成通信的错误发生;
- 上拉电阻的阻值过小,则会造成较大的功率损耗。因此I2C通信的上拉电阻的阻值要满足设备上升沿和下降沿要求的同时尽量选择较大的阻值,以降低消耗的功耗问题。
协议相关的硬件相关的时序描述详见下图
硬件时序及电平要求详见下表
协议层
2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的设备地址。
通常将CPU模块作为主设备,而挂接在总线上的其他设备作为从设备。
I2C总线上的主设备与从设备之间以8字节为单位进行双向数据传输,并且每个单位后还须跟着一位ACK位。
其中数据在SCL处于低电平时被放到SDA数据线上,在SCL处于高电平时进行数据的采样。
I2C总线的传输时序包括:开始条件、地址帧、数据帧、停止条件、重复开始条件。
数据有效性
SCL为高电平时表示有效数据,SDA为高电平表示“1”,低电平表示“0”;
SCL为低电平时表示无效数据,此时SDA会进行电平切换,为下次数据表示做准备。
起始条件S和停止条件P
- 起始条件S:标识传输正式开始,当SCL高电平时,SDA由高电平向低电平转换;
- 停止条件P:当SCL高电平时,SDA由低电平向高电平转换。
起始和停止条件一般由主机产生。总线在起始条件后处于busy,停止条件的某段时间后,总线才再次处于空闲状态。
数据传输格式
传输的每个字节必须为8位,而总字节数不受限制。每个字节后必须跟一个响应位。首先开始传输的是数据最高位,即MSB位。如果此时从机正忙于其他功能,如正在中断服务程序,则需要使SCL线保持低电平迫使主机进入等待状态,直到从机准备完成。
数据接收方收到传输的一个字节数据后,需要给出响应,此时处在第九个时钟,发送端释放SDA线控制权,将SDA电平拉高,由接收方控制。若希望继续,则给出“应答(ACK)”信号,即SDA为低电平;反之给出“非应答(NACK)”信号,即SDA为高电平。
通讯过程
地址帧:地址帧总是在一次通信的最开始出现,通常包括7位的设备地址(MSB)和最后1位的读写控制位(1表示读,0表示写)。接下来是1位的NACK/ACK,当这8位地址发送完成后,Slave设备获得SDA的控制权,此时Slave设备应该在第9个时钟脉冲之前回复一个ACK(将SDA拉低)以表示数据接收正常,否则表示数据接受失败,控制权交由Master设备处理。
数据帧:在地址帧发送之后,就可以开始传送数据了。每个数据帧8位,数据帧的数量可以是任意的,直到产生停止条件。每一个8位数据传输完成之后,接收方就需要回复一个ACK/NACK。
最后在产生一个停止条件结束整个通信过程。
注1:上面都是以8位为一个单元进行的数据传输,其实I2C也是支持10位地址空间的。对于10位地址的传输时序,需要2个地址帧完成地址的传输,其他和8位的传输协议相同。下图为10位地址传输时序图。
通讯过程中会不断地往状态寄存器I2C_SRx写入参数,我们可以通过读取相应位的值来了解通讯状态。
- 主机产生起始信号S,产生“EV5”事件,对I2C_SR1寄存器的SB位置1,表示起始信号已发送;
- 接下来发送设备地址,并等待从机应答(A),从机应答后产生“EV6”和“EV8”事件,此时I2C_SR1寄存器的ADDR位和TxE位被置1。ADDR置1表示地址已发送,TxE置1表示数据寄存器为空;
- 往数据寄存器I2C_DR写入要发送的数据,此时TxE被置0,数据寄存器非空,通过SDA信号线逐位发送数据后,又会产生“EV8”事件,TxE位又被置1,并且从机给出应答。那么,重复这个过程,就可以连续发送多个字节的数据了;
- 数据发送完成后,产生停止信号P,产生“EV8_2”事件,此时I2C_SR1的TxE位和BTF位被置1,BTF位被置1表示字节发送结束。那么至此,通讯结束。
以上的过程会在I2C读写数据编程中作为参考。
另外,如果设置控制寄存器I2C_CR2相应位的中断使能,则以上事件产生时,都会进入I2C中断,进入中断程序后再通过检查寄存器的相应位来判断具体为哪个事件。
而接收数据的过程,也就是读数据的过程,有两次起始信号。第一次起始信号后,发送从设备地址找到从设备后,向从设备内部写入需要读取的数据地址;在第二次起始信号后才开始读取数据。
时钟同步:低电平由时钟低电平最长的器件决定
从机希望主机降低传送速度,通过将SCL主动拉低延长其低电平时间来通知主机,当主机在准备下一次传送发现SCL的电平被拉低时就进行等待,直至从机完成操作并释放SCL线的控制控制权。这样,主机实际上受到从机的时钟同步控制。
可见SCL线上的低电平是由时钟低电平最长的器件决定;高电平的时间由高电平时间最短的器件决定。这就是时钟同步,它解决了I2C总线的速度同步。
I2C仲裁
IIC设备对总线的操作仅有“把线路接地”——输出逻辑0。基于IIC总线的设计,线路上不可能出现电平冲突现象。
如果一设备发送逻辑0,其他发送逻辑1,那么线路看到的只有逻辑0。也就是说,发送逻辑0的始终是“赢家”。
总线的物理接法允许主设备往总线写数据的同事读取数据。这样两主设备争总线的时候“赢家”并不知道竞争的发生,只有“输家”发现了冲突——当写一个逻辑1,却读到了0——而退出竞争。
假设主控器1要发送的数据DATA1为“101 ……”;主控器2要发送的数据DATA2为“1001 ……”总线被启动后两个主控器在每发送一个数据位时都要对自己的输出电平进行检测,只要检测的电平与自己发出的电平一致,他们就会继续占用总线。在这种情况下总线还是得不到仲裁。当主控器1发送第3位数据“1”时(主控器2发送“0” ),由于“线与”的结果SDA上的电平为“0”,这样当主控器1检测自己的输出电平时,就会测到一个与自身不相符的“0”电平。这时主控器1只好放弃对总线的控制权;因此主控器2就成为总线的唯一主宰者。
不难看出:
① 对于整个仲裁过程主控器1和主控器2都不会丢失数据;
② 各个主控器没有对总线实施控制的优先级别;
③总线控制随即而定,他们遵循“低电平优先”的原则,即谁先发送低电平谁就会掌握对总线的控制权。
根据上面的描述,“时钟同步”与“总线仲裁”可以总结如下规律:
①主机通过检测SCL上的电平来调节与从机的速度同步问题——时钟同步;
②主机通过检测SDA上自身发送的电平来判断是否发生总线“冲突”——总线仲裁。
因此,I2C总线的“时钟同步”与“总线仲裁”是靠器件自身接口的特殊结构得以实现的。