1 IIC总线上的IIC通信协议

1.1 IIC总线上挂载多个从机

  一条I2C总线,上面挂载了许多设备,普遍意义上的通信具有发送方和接收方这两边,我们为了方便将总线上的IIC设备分为主设备(master)从设备(slave),主设备接收来自从设备发出的信息,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。当然,可以通过编程来更改主、从设备。
在这里插入图片描述

一般IIC能挂多少个IIC设备呢?

  按照实际设计中经验大概是不超过8个器件。
  【原因】由IIC地址决定,8位地址,减去1位广播地址,是7位地址,2^7=128,但是地址0x00不用,那就是127个地址, 所以理论上可以挂127个从器件。但是,IIC协议没有规定总线上device最大数目,但是规定了总线电容不能超过400pF。管脚都是有输入电容的,PCB上也会有寄生电容,所以会有一个限制。实际设计中经验值大概是不超过8个器件。
总线之所以规定电容大小是因为,IIC的OD要求外部有电阻上拉,电阻和总线电容产生了一个RC延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险。传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以RC乘积必须更小。

IIC设备的地址

  I2C设备的地址为8位,但是时序操作时最后一位不属于地址,而是读or写状态位。这就是为什么arduino的SH1106库里操作的地址不是0x7-而是0x3-,因为有用的是前7位,地址整体右移一位处理了。再一个设备地址的前四位是固定死的,是厂家用来表示设备类型的,比如接口为I2C的温度传感器类设备地址前四位一般为1001即9X、EEPROM存储器地址前四位一般为1010即AX、oled屏地址前四位一般为0111即7X等等。

1.2 线与(Wired-AND)

  • Wired: 二组(或者是多组) 接线直接就接在一起;
  • AND: 它具备逻辑与门AND的功能,与门当全部输入为High 时, 输出为High.只要任何一个输入为Low 时, 输出即为Low.
    在这里插入图片描述

所以IIC总线上这些IIC设备的输出接脚以Wired-AND接在一起,就会有如下的效果:

  • 所有IIC设备都输出High 时, 我们可以在接线上量测到High.
  • 只要有一个IIC设备输出Low, 我们就会在接线上量测到Low.

这意味着:

  • 任何一个IIC设备可以自由控制Bus输出Low, 却不一定能控制Bus输出High.
  • IIC总线上的其他IIC设备都输出High 时, 我们才能完全掌控IIC-Bus的输出.

1.3 IIC总线上的通信

怎么实现主设备接收从设备的数据呢?
从逻辑上考虑,首先需要确定一个主设备和需要数据的从设备,然后需要进行配对,才能进行指定设备间的数据传输。

2 IIC总线通信协议

2.1 IIC总线上实现通信的逻辑

在这里插入图片描述
  IIC总线设计的非常的精巧,通过改变SCL和SDA就可以实现设备间的通信,下面三条定义刚好把SCL 和SDA 二条线上会发生的信号变化全部都包括了。

  • SCL为Low 时,可以改变SDA上的数据值。如下图标示➀ 的位置.
  • SCL为High时,SDA 必须保持数据稳定,不可以改变,以方便对方读取(锁存)数据。如下图标示➁的位置.
  • SCL 为High时, 如果SDA有变动则视为特殊状况: Start (启始, SDA 由High 转为Low) 或Stop (结束, SDA 由Low 转为High). 如下图二个标示➂的位置.

2.2 IIC(I2C)协议定义的主要标志位

根据SDA和SCL的关系可以表示不同的协议标志位,如图所示为I2C协议定义的主要标志位:
在这里插入图片描述

  1. 起始位start
      顾名思义,也就是 I2C 通信起始标志,通过这个起始位就可以告诉 I2C 从机,“我”要开始进行 I2C 通信了。在 SCL 为高电平的时候,SDA 出现下降沿就表示为起始位,如下图所示:

  2. 重复开始repeat start
      在每个开始和停止条件对之间,总线被认为是忙碌的,没有主机可以控制总线。如果主机试图启动一个新的传输并且不想在开始新的传输之前释放总线,它会发出一个新的 START 条件。它被称为重复启动条件Rep Start。SDA在SCL低电平时拉高,然后SCL拉高,接着master就可以产生一个开始条件继续新的消息传输。
      有时master需要在一次通信中进行多次消息交换(例如与不同的slave传输消息,或切换读写操作),并且期间不希望被其他master干扰,这时可以使用“重复开始条件”-----在一次通信中,master可以产生多次start condition,来完成多次消息交换,最后再产生一个stop condition结束整个通信过程。由于期间没有stop condition,因此master 一直占用总线,其他master无法切入。
      除了发送停止条件之外,还允许再次发送另一个开始条件,然后再发送一个地址(当然还包括一个读/写位)和更多数据。这是递归定义的,允许发送任意数量的开始条件。这样做的目的是允许在不释放总线的情况下对一个或多个设备进行组合的写/读操作,从而保证操作不会中断。

  3. stop
      当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。
    在时钟线 SCL 保持高电平期间,数据线 SDA 被释放,使得 SDA 返回高电平(即正跳变),称
    为 I2C 总线的停止信号,它标志着一次数据传输的终止。停止信号也是由主控器主动建立的,
    建立该信号之后, I2C 总线将返回空闲状态。

  4. write
      从机把数据写到SDA上

  5. read
      主机把数据从SDA上下载下来

2.3 应答机制ACK/NACK

  I2C协议规定数据传输过程必须包含应答。接收器通过应答(1 bit)通知发送的字节已被成功接收,发送器可以进行下一个字节的传输。主机产生数据传输过程中的所有时钟,包括应答的第9个时钟。发送器在应答时钟周期内释放对SDA的控制,这样接收器可以通过将SDA拉低通知发送器数据已被成功接收(图2.4)。
在这里插入图片描述

IIC Bus 协定在传输的内容上也有明确的规定:

  • 发送端在每组(8 bits) 讯号送出后, 需读取接收端所回应的一个ACK bit (讯号为Low) 或者NACK bit (讯号为High)。 (注意:发送端不一定是master. 例如: 读取资料时,发送端为slave)

1、I2C 的SCL 始终是由Master产生。
2、I2C 的 ACK和 NACK 始终是由数据的接收方产生,数据的接收方可以是Master 也可以是 Slave.
3、如果是 Master 单纯地往 Slave 里送数据,那么即便是最后一个字节,Slave 也要产生ACK信号,紧接着主机会产生一个 Stop 信号
4、如果是 Master 向 Slave 读取数据,即 Slave 向 Master发送数据,则最后一个字节就会是 NACK 信号,这表示主机不想再 接受数据了,紧接着会产生一个 Stop 信号。

2.4 数据传输格式(Byte)

  除了Start (启始) 和Stop (结束) 二个讯号之外,所有的讯号传输固定8 bits (1 Byte) 为一组,msb (Most Significant Bit) 先送出。
在这里插入图片描述
说明
  SDA上传输字节数据必须是8比特长度,每次传输不限定传输的字节数。每个字节(8位)数据传送完毕后紧接着应答信号(第9位,Acknowledge Bit)。数据传输过程中,先发送高位(MSB),再发送低位(LSB),如图所示。如果在数据传输过程中,从机如果没有准备好接收或发送下一个字节(比如内部中断需要处理等),它可以通过拉低SCL强制主机进入等待状态。直到从机释放SCL,主机才开始下一个字节的发送或接收。

2.5 总线上的数据格式

  1. 无数据(空闲):SCL=1,SDA=1;
  2. 开始位(Start):当SCL=1时,SDA由1向0跳变;
  3. 停止位(Stop):当SCL=1时,SDA由0向1跳变;
  4. 数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;当SCL保持为0时,SDA上的数据可随意改变;
  5. 地址位:定义同数据位,但只由Master发给Slave;
  6. 应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
  7. 否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
  8. 当数据为单字节传送时,格式为:开始位 + 8位地址位(含1位读写位) + 应答 + 8位数据 + 应答 + 停止位。
  9. 当数据为一串字节传送时,格式为:开始位 + 8位地址位(含1位读写位) + 应答 + 8位数据 + 应答 + 8位数据 + 应答 + …… + 8位数据 + 应答 + 停止位。

3. 读写操作、IIC读写时序详解

  实际上,在学习读写逻辑之前,首先需要确定明白两个事情:

  • 读写操作是:主机通过总线读写指定从机的指定寄存器
  • IIC总线上进行的通信由主设备引发,通过IIC总线进行通信时,先找到总线上的某个IIC从设备。总线上的所有通信都是由主控器引发的。在一次通信中,主控器与被控器总是在扮演着两种不同的角色。
    在这里插入图片描述
      IIC读写需要按照SDA与SCL在时序上的配合,即本文2.4所讲的主要标志位区分。可以完成读写操作。

3.1 写操作时序

  要在I2C总线上写入,主机将在总线上发送:一个启动“Start”标志从机地址最后一位(R/W位)设置为0,这表示写入。
  从设备发送ACK响应确认后,主设备将发送其希望写入的寄存器的寄存器地址。从设备将再次确认,让主设备知道它已准备就绪。在此之后,主机将开始向从机发送寄存器数据,直到主机发送了它需要的所有数据(有时这只是一个字节),并且主机将以停止条件终止传输。
在这里插入图片描述
上图就是 I2C 写时序,我们来看一下写时序的具体步骤:

1)、开始信号。
2)、发送 I2C 设备地址,每个 I2C 器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个 I2C器件。这是一个 8 位的数据,其中高 7 位是设备地址,最后 1 位是读写位,为1 的话表示这是一个读操作,为 0 的话表示这是一个写操作。
3)、 I2C 器件地址后面跟着一个读写位,为 0 表示写操作,为 1 表示读操作。
4)、从机发送的 ACK 应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的 ACK 应答信号
8)、发送要写入寄存器的数据。
9)、从机发送的 ACK 应答信号。
10)、停止信号。

3.2 读操作时序

  主设备“读”从设备 的操作称为读操作,它和写操作非常相似,但需要一些额外的步骤。(以下master代表主设备,slave代表从设备)
  Master为了读取slave的数据,master必须首先指出希望从slave的哪个寄存器读取数据。这是由master写入slave的“写操作”类似的方式开始传输,通过发送R/W位等于0的地址(表示写入),然后是它希望从中读取的寄存器地址来完成的。
  一旦slave确认该寄存器地址,master将再次发送启动条件,然后发送slave地址,R/W位设置为1(表示读取)。这一次,slave将确认读取请求,master释放SDA总线,但将继续向slave提供时钟。在这部分事务中,master将成为主“接收器”,slave将成为从“发射器”。
  master将继续发送时钟脉冲SCL,但会释放SDA,以便slave可以传输数据。在数据的每个字节结束时,master将向slave发送ACK,让slave知道它已准备好接收更多数据。一旦master接收到预期的字节数,它将发送一个NACK,向slave发送信号以停止通信并释放总线。之后,master将设置停止条件。

在这里插入图片描述

I2C 单字节读时序比写时序要复杂一点,读时序分为 4 大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是 I2C 从器件输出要读取的寄存器值,我们具体来看一下这步。

1)、主机发送起始信号。
2)、主机发送要读取的 I2C 从设备地址。
3)、读写控制位,因为是向 I2C 从设备发送数据,因此是写信号。
4)、从机发送的 ACK 应答信号。
5)、重新发送 START 信号。
6)、主机发送要读取的寄存器地址。
7)、从机发送的 ACK应答信号。
8)、重新发送 START 信号。
9)、重新发送要读取的 I2C 从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从 I2C 从设备里面读取数据。
11)、从机发送的 ACK 应答信号。
12)、从 I2C器件里面读取到的数据。
13)、主机发出 NO ACK 信号,表示读取完成,不需要从机再发送 ACK 信号了。
14)、主机发出 STOP信号,停止 I2C 通信。

3.3 写入单个/多个字节;读取一个/多个字节

在这里插入图片描述

  • 写入单个字节
      向从机设备的某一个寄存器写一个字节数据:开始信号+设备地址(7位)+读/写(1位)+等待从机应答+寄存器地址(8位)+等待从机应答+要写的数据(8位)+等待从机应答+终止信号。

  • 写入多个字节
      向从机设备的某一个寄存器写多个字节数据:开始信号+设备地址(7位)+读/写(1位)+等待从机应答+寄存器地址(8位)+等待从机应答+要写的数据_1(8位)+等待从机应答+要写的数据_2(8位)+等待从机应答+······+要写的数据_N(8位)+等待从机应答+终止信号。

  • 读取一个字节
      从机设备的某一个寄存器读取一个字节数据:该过程基本类似于下文将要介绍到的从机设备的某一个寄存器读取多个字节数据,故在此处不再过多赘述,可参照多字节的读取方式。

  • 读取多个字节
      从从机设备的某一个寄存器读取多个字节数据:开始信号+设备地址(7位)+写(1位)+等待从机应答+数据地址(8位)+等待从机应答。
      需要注意,此处的写为假写,主要目的是为了告诉M24C08,我将要从哪一个寄存器开始进行数据读取。
    接着,再发送:开始信号+设备地址(7位)+读(1位)+等待从机应答+从机返回读取数据_1(8位)+主机(接收机)应答+从机返回读取数据_2(8位)+主机(接收机)应答+从机返回读取数据_N(8位)+主机(接收机)不再应答+终止信号

参考文献

  1. I2C bus 簡介 (Inter-Integrated Circuit Bus)
  2. I2C中关于ACK和NACK的几点东西
  3. I2C 总线协议详解
  4. I2C学习总结
  5. I2C总线传输协议
  6. TI-Understanding the I2C Bus
  7. I2C通信协议详解和通信流程分析
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐