设计背景
单片机实现与外设通信过程,也就是访问外设寄存器的过程(至少需要两个字节的大小,第一个字节用于指定读/写寄存器,第二个字节用于指定寄存器的地址
单片机与外部模块进行通信,就必须使用某种通信协议来完成访问寄存器了,这时可能会想到串口行不行呢?以下给出4个原因
- 访问寄存器是以对话的方式进行的,不需要即发送又接受,所以将全双工改为半双工(节省一根数据线)
- 串口通信是点对点的,不能实现一主多从
- 串口是异步通信,对时间控制非常依赖,所以改为同步通信
- 串口只有发送接受,但是并不会判断对方是否真的接收到数据了(也就是应答),所以要加入应答
基于以上要求,就使用IIC来作为与外设通信的协议,而且IIC只需要2根线即可实现很多功能,所以采用IIC
IIC硬件规定
因为IIC是同步半双工通信(SCL SDA)
在一主多从的模式下,主机对SCL拥有绝对的控制权,所以不管SCL怎么发都没问题,从机只有在应答或发送数据时才有SDA的控制权
SDA在发送的时候是输出,在接受的时候是输入;如果没有严格控制主机和从机的发送接受顺序,比如当主机发送高电平时,从机也发送了低电平,这样就是电源短路了,这种情况要避免发生
所以规定主机和从机都不能输出高电平,只能输出低电平,这样就杜绝了电源短路了,但是如果双方只能输出低电平的话,那这么通信呢?所以单片机要配置成开漏输出加外置上拉电阻,这样当空闲状态时,SDA为高电平,只有输出低电平,SDA才为低电平
这样做的好处是
1.避免了短路
2.避免了引脚频繁切换,开漏加弱上拉模式同时兼具了输入输出功能,输出高电平就什么都不做,只有输出低电平才工作
3.实现“线与”(一个或多个设备处于低电平,SDA就处于低电平;只有多个设备同时处于高电平,SDA才处于高电平
IIC软件规定
-
起始终止条件
无论起始或终止信号都在SCL高电平期间变化
-
发送数据
主机在SCL低电平期间改变数据,从机在SCL高电平期间读取数据
注:串口是低位先行,IIC是高位先行
-
接受数据
从机在SCL低电平期间改变数据,主机在SCL高电平期间读取数据
注:在接受数据之前要将SDA释放(转为输入模式),因为SDA有“线与"功能,如果此时主机控制SDA发送低电平的话,无论从机发送什么数据,SDA上数据也不会变化
-
应答
在双方发送完数据,对方要回复应答位,才能保证通信的完整性
数据0表示应答,数据1表示不应答
IIC时序
-
指定地址写
时序分为三部分:
第一部分指定设备(7位地址加1位读写位)
第二部分指定设备中要操作寄存器的地址(7位地址加1位读写位)
第三部分指定写数据
注:在主机释放SDA的同时,从机回应低电平,因为”线与“的特性,所以表现在应答位是低电平
-
当前地址读
时序分为两部分:
第一部分指定设备(7位地址加1位读写位)
第二部分读取数据(这时从机拥有SDA控制权)
注:IIC规定,只要指定设备读数据了,紧接着就会发送数据,但是这里并没有指定设备中寄存器的地址,该从哪里开始读呢?这就看当前指针指向那个寄存器了,通常寄存器都被分配在一块线性的区域中,并从头开始,每次读写操作,指针就会+1;例如前面在指定寄存器0x19上读取数据,那现在指针指向寄存器的地址就是0x1A,即每次读写操作,指针都会自动指向下一个寄存器
-
指定地址读
该时序为由上面两个时序组合而成,先是指定地址写,然后当前地址读
具体如下:
1.先指定设备哪个寄存器要进行读写操作
2.然后再重新起始一条数据(因为只有起始数据才能指定读写操作),数据为当前地址读(这样指针就指向了当前设备的寄存器了,然后读取寄存器里的数据