1. I2C通信
- I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
- 两根通信线:SCL(Serial Clock,串行时钟线)、SDA(Serial Data,串行数据线)
- 同步,半双工
- 带数据应答
- 支持总线挂载多设备(一主多从、多主多从)
2. 硬件电路
- 所有I2C设备的SCL连在一起,SDA连在一起
- 设备的SCL和SDA均要配置成开漏输出模式
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
3. I2C时序基本单元
- 起始条件:SCL高电平期间,SDA从高电平切换到低电平
- 终止条件:SCL高电平期间,SDA从低电平切换到高电平
在I2C总线处于空闲状态时,SCL和SDA均由外挂的上拉电阻拉高至高电平状态。当主机需要进行数据收发时,SCL处于高电平,SDA产生一个下降沿,当从机捕获到这个信号时,就会进行复位,等待主机指令。在SDA下降沿后,主机需要把SCL拉低,一方面是占用这个总线,另一方面也是为了方便基本单元的拼接。
- 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
- 接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
- 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
- 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
4. I2C时序
I2C从机地址:在同一条I2C总线里,挂载的每个设备地址必须不同。从机设备地址,在I2C协议标准中分为7位地址和10位地址。其中7位地址比较简单且应用范围广,每个I2C设备出厂时,厂商会为其分配一个7位地址,可以在芯片手册中查到。比如MPU6050的7位地址是1101 000,一般不同型号的芯片地址都是不同的,相同型号的芯片地址是相同的。如果需要同时用到多个相同器件,一般器件地址的最后几位是可以在电路中改变的, 比如MPU6050地址的最后一位,就可以由板子上的AD0引脚确定,该引脚接低电平,那么它的地址为1101 000,接高电平,那么它的地址为1101 001。一般I2C的从机设备地址,高位由厂商确定,低位可以由引脚来灵活切换。这样就可以保证,即使相同型号的芯片挂载在同一个总线上,也可以通过切换地址低位的方式保证每个设备的地址不同。
4.1 指定地址写
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
实测波形图分析:
- 空闲状态SCL和SDA均为高电平。随后主机需要给从机写入数据时,首先,SCL高电平期间拉低SDA,产生起始条件(Start, S)。
- 在起始条件后紧跟着的程序,必须是发送一个字节的时序,字节的内容必须是从机地址(7位)+读写位(1位)。发送从机地址就是确定通信的对象,发送读写位就是确认接下来需要写入还是读出。发送过程中,SCL低电平期间,SDA变换数据,SCL高电平期间,从机读取SDA(绿色竖线标明从机读到的数据)。比如图示波形中,主机寻找的从机地址为1101 000,这个就是MPU6050的地址。最低位表示读写位,0表示在之后的时序中,主机要进行写入操作,1表示在之后的时序中,主机要进行读出操作。
- 紧跟着的单元需要是接收从机的应答位(Receive Ack, RA)。在此时刻,主机释放SDA,从机拉低SDA。应答结束后SDA产生上升沿,是从机释放SDA产生的,从机交出了SDA的控制权。
- 由于之前读写位给了0,所以应答结束后,需要继续发送一个字节。第二个字节就可以送到指定设备的内部了,从机设备可以自己定义第二个字节和后续字节的用途。一般第二个字节可以是寄存器地址或者是指令控制字等,比如MPU6050定义的第二个字节为寄存器地址,AD转换器定义的第二个字节可能为指令控制字,存储器定义的第二个字节可能为存储器地址。图示波形发送数据为0001 1001(0x19),在MPU6050中表示要操作0x19地址下的寄存器。
- 随后同样是从机应答。主机收到的应答位为0,表示收到了从机的应答。
- 主机再发送一个字节,这个字节就是主机想要写入到0x19地址寄存器的内容。比如图示发送了数据0xAA,就表示在0x19地址下写入0xAA。
- 随后是接收应答位。
- 如果主机不需要继续传输,就可以产生停止条件(Stop, P)。在停止条件之前,先位低SDA,为后续SDA的上升沿作准备,然后释放SCL,再释放SDA。
这样一个完整的数据帧就拼接完成了。总结就是对于指定从机地址为1101 000的设备,在其内部0x19地址的寄存器中,写入0xAA这个数据。
4.2 当前地址读
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
如果主机想要读取从机的数据,就可以执行下图时序:
- SCL高电平期间拉低SDA,产生起始条件。
- 起始条件后,主机必须首先调用发送一个字节,进行从机的寻址和指定读写标志位。图示波形的寻址目标为1101 000的设备,读写标志为1,表示主机接下来要读取数据。
- 接收从机应答位。
- 从机应答后,数据的传输方向变为从机到主机,SDA的控制权交给从机。主机调用接收一个字节的时序,进行接收操作。从机可以在SCL低电平期间写入SDA,主机在SCL高电平期间读取SDA。由于主机没有指定要读哪个地址,从机就会返回当前指针指向的寄存器的值0x0F。假设刚刚调用了指定地址写的时序,在0x19的位置写入了0xAA,那么指针就会+1,移动到0x1A的位置,此时再调用当前地址读的时序,返回的就是0x1A地址下的值,如果再调用一次,返回的就是0x1B地址下的值。
由于当前地址读并不能指定读的地址,所以这个时序使用较少。
4.3 指定地址读
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
实测波形图分析:
- 首先是起始条件,随后发送一个字节进行寻址,指定从机地址1101 000,读写标志位0代表要进行写操作。
- 经过从机应答后,第二个字节,用来指定地址。从机接收到这个数据之后,它的寄存器指针就指向了0x19这个位置。
- 随后再来一个起始条件,就是重复起始条件(Start Repeat, Sr)。因为指定读写标志位只能是跟着起始条件的第一个字节,所以如果想要切换读写方向,只能再来一个起始条件,然后重新寻址并且指定读写标志位。此时读写标志位1表示开始读数据。
- 从机应答后,主机接收一个字节。这个字节就是0x19地址下的数据。
- 如果想只读一个字节就停止,在读完一个字节后,一定要给从机发送非应答(Send Ack, SA)。非应答就是该主机应答时,主机不把SDA拉低,从机读到SDA为1,代表主机没有应答,从机就会释放总线,把SDA控制权交还给主机。如果主机想连续读取多个字节,就需要在最后一个字节给非应答,而之前的所有字节都要给应答。