首先介绍一下I2C协议,I2C协议是飞利浦公司在1980年代开发的简单,双线串行同步总线。他仅仅只需要两根线就可以实现数据的传输。
我们常见的支持I2C协议的设备一般有四根线,如下图的0.96寸OLED显示屏
可以看到该屏幕的上方从左到右有四个引脚分别是GND VCC SCK以及SDA引脚。而用于I2C数据传输的就是SDA和SCK引脚。
接下来我为大家说明一下这两个引脚的作用。首先是SCK引脚,我们也叫时钟线,这个引脚规范了主机和从机之间读取数据的时间。当SCk为低电平的时候从机将数据放到SDA总线之上,在SCK变为高电平的时候主机读取SDA上的电平状态从而判断从机发送的是0还是1。
而SDA引脚也就是数据传输引脚,从机通过拉低或者不拉低从而在一个时钟周期内向主机传输数据。
在使用I2C协议的时候我们要在SCK和SDA这两条线上接一个上拉电阻,上拉电阻的阻值一般给4.7K即可,因为I2C的这两条线默认的情况下是高电平。同时当我们打算利用单片机读取I2C的时候,记得要将单片机IO口配置为开漏输出模式,同时可以默认上拉电阻。(单片机开漏输出模式的意义在于引脚可以被下拉到低电平)
搞懂硬件层之后我们来看一下I2C协议的具体部分,也就是时序图:
首先是I2C通讯的起始信号:
我们可以看到I2C起始信号的产生条件是当SCK高电平的时候SDA从高电平变为低电平。此时I2C协议开始。
其次是I2C通讯的结束信号:
和起始信号类似,I2C通讯的结束信号发生在SCK高电平期间SDA从低电平变为高电平。此时表示I2C通讯结束。
介绍完了I2C协议的开始和结束信号,我们接下来介绍他的数据传输和应答信号。
I2C协议在开始之后,我们首先要进行的是寻址,也就是寻找我们需要读取设备的地址。因为I2C总线支持挂载多设备,所以寻址实际上就是寻找你要读取的设备的地址,通过寻址我们才能读到自己想要的设备的数据。举一个例子,寻址就好比,现在有一条街,街上住了10口人,我想去找小明玩,可是我不认识路,那么这个时候我就需要小明的地址,比如小明住在街口进入第三栋房子里面。这个第三栋就类似于I2C的地址了。
我们还需要知道I2C协议支持的寻址方式有两种,一种是7位寻址一种是10位寻址。对于10位寻址我个人也没咋接触过所以不做解释。而七位寻址是比较常见的一种方式。
所谓七位寻址也就是从机设备的地址是由7位二进制数表示的。但是需要牢记I2C通讯发送的首字节一定是地址加上读写位。举个例子比如当前设备的I2C地址是0x56,假设我此时要对I2C从设备进行读取,那么我最后一位就给高电平,也就是将0x56整体左移之后加一,此时转化为二进制就是1010 1101。要是写入就将最低位的1改成0即可。
在发送完寻址和读写位之后,我们要进行应答。应答大家可以理解为一种保险,应答成功说明我们之前做的都没错。
应答就是当SCK总线处于高电平的时候要是从机应答成功,那么从机就会将SDA总线拉低,表示从机收到了指令给出了回答。要是应答失败,那么我们此时就可以重复开始条件和寻址位,重新开始I2C协议。要是应答成功那么我们就开始下一个字节的传输。
应答成功之后我们开始下一个字节的传输,这个字节一般是我们需要访问的从机设备的寄存器的地址,比如INA226这款芯片(TI的功率检测芯片,可以读取总线电压,电流和功率大小),假设我们打算对这款芯片进行初始化在成功的寻址加写位之后,我们通过芯片手册知道,该芯片在进行检测之前需要初始化部分寄存器,其中的校准寄存器的地址是05h,此时我们发送的下一个字节就是05h对应的二进制数0x0000 0101。之后再进行校验。
第三步就是在对应的寄存器写入我们需要写入的数据了。假设我们需要写入的数据是一个字节,那么就和之前的写寄存器地址一样,加上校验位最后给一个I2C的结束信号即可。
要是打算写多个字节的数据的话,那么这个时候我们发送完一个字节的数据之后,将发送应答位给低电平,表示我们还想继续传输一个字节的数据,传输多个字节以此类推。最后传输的数据结束了之后我们将应答位置1表示数据传输结束,之后执行I2C的结束信号。如此我们的I2C通讯就结束了。
以上只是对I2C的简单介绍,因为也是本人第一次写文章,文章可能有错误的地方希望大家多多指正。