【IIC】通过Arduino的SoftI2C库学习IIC通信
【IIC】通过Arduino的SoftI2C库学习IIC通信
SoftIC库
SoftI2C库是Arduino的一个第三方库,可以通过软件模拟IIC时序与其他设备进行通信。可以实现与Wire库
一样的功能。
SoftI2C库的官方链接:https://www.arduino.cc/reference/en/libraries/softi2c/
相关的API函数如下所示:
IIC基本知识
物理层
IIC通信是通过两条线进行,分别是SDA
和SCL
,为IIC总线。
在物理接连层面,IIC总线都是通过上拉电阻(一般为4.7k)上拉至高电平,所以在空闲期间,IIC总线SDA和SCL的空闲电平为高电平。IIC主从设备通过将自身的接口连接至总线上进行通信。
协议层
在协议层,IIC总线的SDA和SCL通过形成不同的时序进行数据传输。
通过一张图来介绍一下IIC的时序:
1 起始信号
数据发送开始时,由主设备发送起始信号,表示通信开始。起始信号是SCL高电平期间,SDA由高变低(下降沿)。
2 数据信号
在SCL低电平期间,SDA信号允许进行翻转变化,在SCL高电平期间,SDA信号必须保持不变。因为在SCL高电平期间,会对SDA进行采样,进而传输1bit数据。
如上图所示,在传输单byte数据时,高位数据会先传输,低位数据后传输。一共传输8bit。
3 应答信号ACK
在主设备传输了一个字节数据后,主设备将SDA拉高,放弃SDA线的控制权,对应的从设备会在SDA上产生应答信号ACK,即将SDA拉低。当主设备在第九个SCL高电平期间采样到SDA低电平,表示与从设备通信成功;如果未检测到ACK信号,则表示数据传输失败。
4 非应答信号NACK
与应答信号相反的,就是非应答信号NACK,即在第九个SCL高电平期间采样到SDA为高电平,表示设备没有应答。
5 结束信号
结束信号代表通信的结束。对应的时序是在SCL高电平期间,SDA由低变高(上升沿)。
IIC的通信过程都是由上面这5中信号组成的。
记忆小技巧:
在SCL低电平期间,SDA信号可以随意发生变化,并不会影响通信过程;在SCL高电平期间,SDA信号不发生变化,则为传输数据信号;如果发生了变化,就会形成起始信号(SDA由高变低)和结束信号(SDA由低变高)。
以IIC芯片AS5600的数据手册为例,了解一下IIC的写数据和读数据的通信规则:
AS5600数据手册:获取链接
AS5600具体的通信时序如下图所示:
挂载在IIC总线上的从设备都会有一个独立的器件地址(slave address),是一个7位的二进制数,例如AS5600的器件地址固定为0x36。
在通信过程中紧接着起始信号的就是器件地址和1bit的R/W读写标识位(write=0,read=1),组成了一字节的数据,表示向对应的从设备写数据或读数据。
AS5600写数据时序:
- 首先是起始信号S,然后紧接着是器件地址0x36和写标志位0,等待从设备应答。
- 从设备应答后,再发送将要写的寄存器地址(Word address),等待从设备应答。
- 从设备应答后,继续发送要写的数据data。
- 最后发送停止信号P结束通信。
需要注意的是,AS5600支持多数据的写入,多个数据的写入会将寄存器地址递增依次写入。
AS5600读数据时序:
- 首先是起始信号S,然后紧接着是器件地址0x36和写标志位0,等待从设备应答。
- 从设备应答后,再发送将要读的寄存器地址(Word address),从设备应答。
- 主设备重新发送起始信号S,器件地址0x36和读标志位1。从设备应答后,从设备会将指定寄存器地址中的数据返回给主设备。(这个时候主设备每接收一个字节后,会向从设备发送应答信号,直到主设备发送非应答信号NACK,表示不需要再发送数据了,然后主设备发送停止信号P结束通信。)
需要注意的是,AS5600支持多数据的读取,多个数据的读取会将寄存器地址递增依次读取。
使用Arduino SoftI2C库进行IIC通信
#include "SoftI2C.h"
myiic = SoftI2C(0,1); //定义iic对象,sda=0,scl=1
myiic.begin(); //初始化
/****写数据****/
myiic.beginTransmission(0x36); //1 向器件地址为0x36的从设备通信
myiic.write(0x10); //2 向寄存器地址0x10写入数据
myiic.write(0x3f); //3 写如数据0x3f
myiic.endTransmission(); //4 结束通信,发送停止信号
/****读数据****/
myiic.beginTransmission(0x36); //1 向器件地址为0x36的从设备通信
myiic.write(0x10); //2 从寄存器地址0x10读数据
my.iic.requestFrom(0x36,1); //3 请求一个字节的数据,并且停止通信