I2C通信与串口通信的使用场景对比
串口通信,发送数据要占一个引脚,接收数据也要占一个引脚,如果单片机要通信的设备不多,可以用,但如果要通信的设备很多,就要占很多个引脚,单片机引脚一共就那么多,不可能全拿来通信,所以在通讯设备很多的情况下,串口通信不再适合,而I2C却可以
I2C介绍以及数据帧
I2C介绍
I2C通信是总线型的,一个设备发送信息,其他设备都可以接收到,类似于在教师中老师说一句话,同学们都能听到,设备发送信息需要加上地址,对应地址的设备会与主机建立通信,而其他设备不予理会。就好像是老师说::“xxx,起来回答问题”,这句话所有同学都能听到,但只有对应的同学会站起来回答问题。I2C通信更特别一些,这里面分为主机和从机,只有主机才能主动发送信息,给一个设备发送数据(写数据)或者要求设备给自己发送数据(读数据),不存在从机之间的通信。(也就是只有主->从或者从->主,没有从->从),SCL是控制线,全程由主机控制电平的高低,从机根据SCL的电平活动,SDA是数据线,高电平是1,电平是0,在SCL为高电平时,表示正在进行数据传输,SDA应该保持不动,保证数据发送完成。SCL为低电平时,SDA在这个时间切换为下一位数据(切换电平)。

数据帧格式
通信过程:主机将SDA拉低发送一个起始位,然后发送一个字节的数据:7位地址+一位R/W命令,0:主机向这个地址的设备发送数据,1:主机从这个地址的设备读取数据,然后释放总线(此时SDA被上拉电阻拉高),从机收到起始位与7位地址后发现与自己的地址相同,于是拉低SDA线,主机检测SDA线,发现是低电平,说明从机已收到请求,二者开始通信,如果第一个字节最后一位(R/W位)为0,则为主机发送一个数据,从机回一个ACK,主机再发,从机再回,发送完成后将SDA拉高也是发送一个停止位,发送结束;如果第一个字节最后一位(R/W位)为1,那就是从机发送主机接收,从机发一个数据,主机收到后若想继续读取数据,则发ACK表明"下一个",这样从机发数据,主机回ACK,最后一个数据收完了,发送NAK(也就是把SDA线拉高)表明我不需要数据了,不要再发送数据了,最后主机再发送一个停止位彻底结束通信
这里起始位与停止位比较特别:SCL为高电平表示数据传输,SDA不可改变,SCL为低电平是数据准备阶段,SDA可以改变,但如果SCL为高电平时,SDA改变了,就说明这是特殊的东西而不是一个数据:从高到低表示起始位,从低到高表示停止位。
补充:主机从机是如何做到一个设备发数据,所有设备(包括主机)读取到的相同的
是通过"线与"来实现的,所有主机主机都为1,则线路为高电平,有一个主机为0,则读取的全都是低电平
在所有设备输出寄存器均为1时,大家全是高阻抗:也就是不接地,上拉电阻那里的电压为VCC,这里铁头山羊解释是好像串联了一个无穷大电阻接地,电压全被分在那个无穷大电阻上了,我自己按初高中理解就是压根不是通路,电阻好像也相当于一个导线了,然后只要有一个设备的输出寄存器为0,它的IO口输出低电平,形成一个回路,电压全部分到上拉电阻上,则那一点的电平为VCC-VCC=0,或者说那点连了地,为0
I2C结构以及各寄存器重要标志位的意义
SR2的:
TRA:
BUSY:总线忙,为0总线空闲,为1总线忙碌(上一次通信还未结束)。一般用于发送起始位之前要等待BUSY为0才能发送,发送停止位之后BUSY为0才算结束
MSL:模式,为0是主模式,为1是从模式
SR1的:
SB:起始位发送完毕
AF:应答错误(没收到ACK)
ADDR:
主模式:寻址成功,若为1表示地址已发送且收到了ACK,为0表示数据没发送完或者没有收到ACK(地址没有发送结束:为0,发送结束但收到的是NACK(没收到ACK)为0,发送结束收到ACK为1)
从模式:收到的地址与自己不匹配为0,匹配为1
TXE:发送数据寄存器为空 RXNE:接收数据寄存器非空、
BTF:
发送模式下:发送数据寄存器和发送移位寄存器均为空则为1,否则为0,即"全空"
接收模式下:接收数据寄存器和接收移位寄存器均为满则为1,否则为0,即"全满"
CR的:
START:写1则产生一个起始位(在SCL为高电平时产生一个下降沿),写0不产生起始位
STOP:写1则产生一个停止位(在SCL为高电平时产生一个上升沿),写0不产生停止位
ACK:写1则产生一个ACK(拉低电平),写0不产生ACK(或者说产生NACK)
收发过程
流程图
发送
接收
整体代码
发送
首先等待总线空闲(检测BUSY位),然后发送起始位,检测到起始位发送完毕后发送地址+R/W命令,释放总线等待寻址成功(等待ADDR,等待过程中用AF检验是否出错(使用AF之前需要先清零),出错则跳转到STOP),寻址成功后发送数据:若发送数据寄存器非空,则等待发送数据寄存器中的值进入移位寄存器后再将新的值放入发送数据寄存器中,空了就装数据,等待期间通过查询AF位确保不出错,若出错则直接跳转STOP,数据全部进入发送数据寄存器后通信未结束,因为发送数据寄存器和发送移位寄存器中的值可能还未发送,所以需要等待二者都空才可以(判断BTF,为1则二者都空,发送完毕),发送过程中还是不断检测AF位,以此来判断是否出错,清除ADDR原因:规定I2C使用之前需要先清除ADDR值,读取SR1和SR2即可清除ADDR值,在寻址成功之后清除即可
接收
在主模式,接收情况下,ACK和停止位对正在传输的数据没有影响,ACK的意思是"再给我一个",正在发来的数据是上一个ACK让从机发来的,也就是说ACK决定的是从机没有发送的但是发送序列第一个数据是否发来,停止位也是如此,如果一个数据正在发来,则会在这个数据接收后产生停止位
首先等待总线空闲(检测BUSY位),然后发送起始位,检测到起始位发送完毕后发送地址+R/W命令,释放总线等待寻址成功(等待ADDR,等待过程中用AF检验是否出错(使用AF之前需要先清零),出错则跳转到STOP),寻址成功后接收数据,将需要读取的数据个数用N表示:
N=1时,只要一个数据,所以发完起始位可以直接发NCAK,也就是不发ACK,即ACK位为0,并产生一个停止位(将停止位置1)等待总线空闲则通信彻底
N=2时,先将ACK置为1(如果不用软件方法将ACK置0,则会一直为1,收到一个数据就会根据当前位自动发送ACK或者NACK),等待RXNE为1,表示已经来了一个数据,此时因为之前将ACK置1,单片机已经自动发了一个ACK,下一个数据一定会发过来,于是立即将ACK置0,并产生一个停止位,下一个数据来了之后,根据ACK自动发一个NACK,表明不需要数据了,然后产生停止位,等待总线空闲则通信彻底结束
N>2时,先将ACK置为1(如果不用软件方法将ACK置0,则会一直为1,收到一个数据就会根据当前位自动发送ACK或者NACK),for循环N-3次:等待RXNE为1,读取数据,还剩下三个数据:等待BTF为1,等待过程中叶一直在发ACK,BTF为1时,表明接收数据寄存器和移位寄存器均满,又收到了两个数据,且还有一个数据因为之前置1的ACK而在路上,数据已经全部发完,于是将ACK置0表明不需要数据了,(在这之前先把数据寄存器的值读走防止数据被覆盖),将停止位置1表明表明数据传输结束,检测BUSY,为0则通信彻底结束
接收数据之前清除ADDR:主模式先寻址成功,ADDR为1,此时若不清除ADDR,则数据传不进来,无法接收数据,不知道这不是之前提到那个规定的原因:规定I2C使用之前需要先清除ADDR值,读取SR1和SR2即可清除ADDR值,在寻址成功之后清除即可