简介
I2C 是飞利浦公司设计的,一种很常见的总线协议, I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候 SCL 和 SDA 处于高电平。 I2C 总线标准模式下速度可以达到 100Kb/S
,快速模式下可以达到400Kb/S
。 I2C 总线工作是按照一定的协议来运行的,接下来就看一下 I2C 协议。
I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C从设备有不同的器件地址,因而 I2C 主控制器可以通过 I2C 设备的器件地址访问指定的 I2C设备。主从机的接线图如下图所示:
其中SDA 和 SCL 这两根线必须要接一个上拉电阻,一般是 4.7k
。
1.通信过程
IIC的通信过程稍微复杂一些,分成读时序和写时序。
1.1写时序
主机向从机写程序时的时序如图所示:
从上面的时序图可以看出,需要两次地址确认(从机地址和寄存器地址),保证从机在位且准备就绪后才可以写入数据。
- 开始信号。SDA上面输出低电平,为通讯做准备。
- 发送设备地址,从而选择通信的对象。7位数据是设备地址。
- 读写位:0表示写操作,1表示读操作。
- 从机发出的应答信号(低电平)。
- 第二次发送开始信号。
- 发送需要写入的寄存器地址。
- 从机发出的应答信号。
- 发送要写入寄存器的地址。
- 从机发出的应答信号(低电平)。
- 停止信号(高电平)。
1.2读时序
读时序比写时序更复杂,但是若看懂了写时序,读时序就很简单了。也就是读设备地址->读寄存器地址->读设备地址->读数据的一个过程。
读时序的过程基本如下,开始的时候SDA发送低电平,表示通信开始。然后和写时序一样,发送从机地址,等待应答,有了应答信号后再次写入需要读数据的寄存器地址,等待应答,若有了应答信号,可再次发送从机地址进行确认。再从从机中读取相应的数据,然后主机主动发出NO ACK信号,表示读取完成,然后主机发出STOP信号(高电平)停止I2C通信。
1.3多字节读写
有时候我们需要读写多个字节,多字节读写时序和单字节的基本一致,只是在读写数据的时候可以连续发送多个自己的数据,其他的控制时序都是和单字节一样的。
1.4助记口诀
为了方便记忆I2C的通信过程,编写了如下的助记口诀:
- 起始拉低,停止拉高很好理解,就是在空闲的时候,由于上拉电阻的存在,SCL和SDA都是高电平,等到开始通信的时候就将其电平拉低,通信结束后将其拉高。
- 低电翻转,高电传输指的是(通信过程中)在时钟线低电平期间,数据线是可以进行电平翻转的,而在高电平期间不能,传输的是确定的电平信号。
2关于上拉电阻的相关思考
为什么I2C每根线需要一个上拉电阻呢?关于这个问题查阅了大量的资料。网上的一般说法都是设计上拉电阻是因为主机和从机的I2C接口是开漏输出,那。。。为什么I2C接口要采用开漏输出呢?
关于什么是开漏输出,可以看我之前写的这篇帖子:(嵌入式系列)STM32系列单片机的GPIO简介
众所周知:开漏输出有两个优点。
- 可以根据实际需求,灵活地配置输出电平。
- 实现“线与”的功能。
第一点不是很重要,因为一般的I2C设备都不会有太特殊的电平要求,而I2C是国际统一的通信标准。而第二点非常重要。因为I2C是支持一主多从或者一从多主的通信(如下图是一主多从,一从多主同理)。
原因一:若采用推挽输出,当两个芯片之间的SCL或者SDA引脚的电平不相同,则很有可能会出现一个_MOS和一个N_MOS同时导通而损坏元器件。
原因二:方便总线仲裁。在多个设备通信的时候,难免会出现总线的“竞争”问题,也就是多个设备同时申请通信,假设主设备A需要启动I2C,它需要在SCL
高电平时,将SDA由高电平转换为低电平作为启动信号。主设备A在把SDA
拉高后,它需要再检查一下SDA
的电平(开漏输出只能输出低电平和高阻态,所以不能保证100%输出高电平)。
若SDA是高
电平,说明主设备A可以占用总线,然后主设备A将SDA拉低,开始通信。
若SDA是低
电平, 说明有设备提前申请了通信权限,主设备A不能占用总线, 结束与当前设备的通信。
如果主设备A拉高SDA时,已经有其他设备将SDA拉低了。由于与运算以后还是低电平,那么主设备A在检查SDA电平时,会发现不是高电平,而是低电平,说明主设备与其他设备的通信尚未结束,新设备只能放弃占用总线。如果是高电平, 则可以占用。
从上面的描述也可以发现,其实这个时候的主机和从机已经成了相对的概念,每个设备(芯片)都可以连接好几个设备(通过I2C)协议。