I2C(IIC)的仲裁、时钟同步和时钟扩展
注意,CSDN以及博客园上有大量抄袭和以讹传讹的情况,注意鉴别。
本文参考了Philip的I2C specification以及wiki pedia,并且通过实践验证。
阅读本文要求你已经对i2c的协议有基本的了解。我们将会着重介绍多主机模式(multi-master)下的仲裁(arbitration),仲裁时不同主机之间的时钟同步(clock synchronization),以及从机(slave)动态调整通信速率的措施时钟扩展(clock stretching)
仲裁
发生仲裁的前提是,一条总线上挂载了多个主机,并且这些主机都支持多主机模式,即每一个主机都可以实时监测SDA以及SCL的情况,从而通过start和stop位来确定总线的情况(被占用还是空闲)。当多个主机检测到当前总线处于空闲状态时(这里以2个主机为例),可能会同时发出启动标志位(发出的时间间隔很短,因此无法检测到其他节点产生的电平变化,误认为总线是空闲的),这时候仲裁机制开始生效。
总线归属权的仲裁是通过SDA线完成的。由于I2C的总线设计是线与(wired-AND),因此低电平是显性电平。只要有一个节点将SDA拉低,那么整条总线都会拉至低电平。在两个主机都认为总线空闲并开始通信之后,当遇到第一个两者发送的不同的位时,会决出总线的归属者。
直接举例。设主机1将向address为0b10100111的从机发送数据,主机2将向addres为0x10101000的从机发送数据。两者都占有总线并同时向SDA写入数据。注意I2C发送的顺序是MSB->LSB(先发送高位)。两者的前四位数据相同,而当发送进行到第五个时钟周期时,master1的第五位为0,而master2的第五位为1,此时根据线与特性,SDA总线的电平会被master1拉低。master2会检测到自己要发送的电平和总线的实际电平不符,从而获知有其他主机正在发送,随之停止后续的发送,让出总线的归属权。
若两主机向相同的地址写入数据,那么仲裁过程将继续,直到在后续的数据位中决出总线的归属权(仍然是通过上述的方法)。
P.S. 仲裁不能发生在start和stop段,否则行为是未定义的。即倘若两个主机向相同的地址写入相同的数据,那么并不能决出总线的归属权。在这时,一个主机发出stop结束通信,而另一个主机则发出restart开始新的通信,这时候的行为就是未定义的。详见I2C-bus specification and user manual的第十二页:
I2C的仲裁是通过采样SDA完成的,因此不会破坏数据的有效性,通信不会停止,也不会有数据帧丢失。但是I2C不具有主机优先级的设定,因为总线的归属权是根据主机发送的数据确定的,因此无法提前为每个主机设定发送或接收的优先级。
时钟同步
时钟同步只会在仲裁时发生。SCL是由主机产生的时钟信号,用于和从机确定数据发送和采样的时间点。倘若处在仲裁期间,会有多个主机同时发送往SCL上发送时钟信号。两个主机配置的通信速率可能不同,因此时钟频率必然不同;即使配置了相同的通信速率,两者开始发送数据的时间也不同。此时时钟信号具体如何确定就要通过clock synchronization的机制。
在总线空闲的时候,SCL被上拉电阻拉高。开始通信后,主机的时钟信号接入SCL中。如下图所示,有两个主机(时钟信号分别为CLK1和CLK2)都认为主机空闲,因此开始将时钟信号输入SCL。同步分为以下五个阶段:
- CLK1率先变为低电平,由于线与特性CLK2也变成低电平。
- 但是CLK2的低电平时间更长(时钟周期更长,CLK1的周期更短),即使主机1内部的CLK1已经变成高电平,SCL的实际电平仍然和CLK2保持一致。此时,主机1将会检测到这一情况,并从此时开始计数(计算低电平持续的时间,即下图中的wait state,稍后用)
- CLK2迎来高电平之后,主机1内部的wait state结束,因为两者都为高,因此SCL总线进入高电平,两个主机内部都会开始对高电平持续的时间进行计数。
- 随后,CLK1会比CLK2先回到低电平(此时CLK1的周期仍然比CLK2更短)。
- 现在,CLK1以及获得了需要延长的低电平时间,CLK2也获得了需要减短的高电平时间(都是通过刚才的计数获得的),两个主机会根据之前的计数重新调整自己的时钟周期,从而完成时钟同步。
例如,在时钟同步之前,主机1的速度为400kbit/s(fast mode),主机2的速度为100kbit/s(standard mode),同步之后,速度都会变为100kbit/s。如果两者的通信速率本就相同,但也会因为CLK的开始时间不一样而出现错位,这同样会通过时钟同步而矫正。
其他的教程中常出现一句话叫:“同步之后的时钟,低电平的时间取决于低电平最长的CLK,高电平时间取决于高电平时间最短的CLK”,描述的实际上就是I2C总线的线与特性。通过线与的特性以及I2C对SCL的持续检测(采样),我们就可以实现时钟同步了。
关于时钟同步的原始描述,可以参见I2C-bus specification and user manual的第11页。
时钟扩展
和其他常见的通信方式相比,I2C可以动态地调整总线的通信速率,这是通过时钟扩展(clock stretchign)完成的。
同样,时钟扩展需要I2C的硬件支持,其主机和从机都需要相关的硬件才可以实现这一功能。
虽然我们已经设定了I2C的通信速率(如标准模式,快速模式,高速模式等),但支持时钟扩展的I2C硬件可以根据情况动态改变通信速率。时钟扩展是由从机发起的。如从机正在处理其他任务无暇接收数据,或上一次收到的数据还没来得及写入或转移到其他地方时,就可以在收到一个byte的数据之后主动将SCL拉低(此时尚未发送ACK给主机),主机会检测到SCL的变化,进入等待状态(wait state),此时主机便会停止数据的发送,直到检测到SCL被释放(I2C的两条总线都是接了上拉电阻,默认为高电平,因此称回到高电平为“释放”)并收到释放之后的ACK信号。