IIC 即Inter-Integrated Circuit(集成电路总线),是半双工,这种总线类型是由飞利浦半导体公司在八十年代初设计出来的,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。这种方式简化了信号传输总线接口。
A.数据的有效性
在时钟的高电平周期内,SDA线上的数据必须保持稳定,数据线仅可以在时钟SCL为低电平时改变。
如下图所示:
B.起始和结束条件
起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件;
结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件。
注意:起始和终止信号都是由主机发出的,连接到IIC总线上的器件,若具有IIC总线的硬件接口,则很容易检测到起始和终止信号。总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态,对起始条件和结束条件的描述如下图所示。
C.应答
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,如下图所示。
D.数据帧格式
IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。{这里小编在驱动MPU6050模块的时候,就犯过这样的错误,它写的MPU6050从机地址是0x68,因为发送从机地址的时候,要加一位读写方向位,因为刚开始应该是向这个MPU6050里写从机里某个寄存器的地址,所以应该是7位地址 0x68(1101000)+二进制位0=11010000)也就是0xD0,表示要向该IIC设备里写东西,然后再紧接着写入IIC设备里的寄存器地址,而我直接写入了0x68,导致出错},每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传输过程中,可以有以下几种组合方式:
[1] 主机向从机发送数据,数据传送方向在整个传送过程中不变:
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答(低电平), A非表示非应答(高电平)。S表示起始信号,P表示终止信号。
[2]主机在第一个字节后,立即从从机读数据:
[3]在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相:
一般情况下,[3]是比较常见的,比如MPU6050模块,
发送起始信号
等待从机应答
写一个从机地址+0(表示写),
等待从机应答
发送一个字节的MPU6050加速度存储寄存器地址,
等待从机应答
再发送一次起始信号
等待从机应答
写一个从机地址+1(表示读)
等待从机应答
读取MPU6050传感器数据
主机非应答
E.IIC信号的模拟
主机可以采用不带IIC总线接口的单片机,如80C51、AT89C2051等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。即使是含有IIC硬件的单片机也有一定的缺陷,所以一般也会模拟IIC的时序。现将具体时间截图如下:
F.具体的程序代码如下(仅供参考):
#define IIC_SDA_OUT
#define IIC_SDA_H
#define IIC_SDA_L
#define IIC_SDA_IN
#define IIC_SDA_IS_H
#define IIC_SCL_OUT
#define IIC_ SCL H
#define IIC SCL _L
void iic_delay_nus(uchar n) {//单片机时钟频率为12MHz
for(; n!=0; n–)
nop();
}//延时时间2+1*n+2=4+n us(n>0)
void delay_5ms(uchar k) {
for(;k!=0;k–)
for(int m=4996; m!=0; m–)
nop();
}
void Start()//IIC开始
{
IIC_SDA_OUT;
IIC_SCL_OUT;
IIC_SDA_H;
IIC_SCL_H;
iic_delay_nus(1);
IIC_SDA_L;
iic_delay_nus(10);
IIC_SCL_L;
}
void Stop()//IIC 结束
{
IIC_SDA_OUT;
IIC_SCL_OUT;
IIC_SCL_L;
IIC_SDA_L;
IIC_SCL_H;
iic_delay_nus(10);
IIC_SDA_H;
iic_delay_nus(10);
}
void iic_sendack(void){
IIC_SDA_OUT;
IIC_SDA_L;
iic_delay_nus(10);
IIC_SCL_H;
iic_delay_nus(1);
IIC_SCL_L;
}
void iic_sendnack(void){
IIC_SDA_OUT;
IIC_SDA_H;
iic_delay_nus(10);
IIC_SCL_H;
iic_delay_nus(1);
IIC_SCL_L;
}
bool iic_getack(void){
bool ack = false;
IIC_SDA_IN;
iic_delay_nus(10);
IIC_SCL_H;
iic_delay_nus(1);
if(!IIC_SDA_IS_H){
ack=true;
}
IIC_SCL_L;
return ack;
}
void iic_sendbyte(uchar val){
uchar i;
IIC_SDA_OUT;
for(i=8;i!=0;i–){
if(val&bit(7))
IIC_SDA_H;
else
IIC_SDA_L;
iic_delay_nus(10);
IIC_SCL_H;
iic_delay_nus(1);
IIC_SCL_L;
val << =1;
}
}
uchar iic_getbyte(void){
uchar i;
uchar val=0;
IIC_SDA_IN;
for(i=8;i!=0;i–){
iic_delay_nus(10);
IIC_SCL_H;
iic_delay_nus(1);
val << =1;
if(IIC_SDA_IS_H)
val |= bit(0);
IIC_SCL_L;
}
return val;
}
void Write_iic(uchar addr,uchar dat) //IIC 向从设备的指定地址写入数据
{
Start();
iic_sendbyte(0xa0);
iic_getack();
iic_sendbyte(addr);
iic_getack();
iic_sendbyte(dat);
iic_getack();
delay_5ms(1);
Stop();
}
uchar Read_Current()//IIC从从设备的当前地址读取数据
{
uchar d;
Start();
iic_sendbyte (0xa1);
d= iic_getbyte ();
iic_sendnack ();
Stop();
return d;
}
uchar Random_Read(uchar addr) // IIC从从设备的任意地址读取数据
{
Start();
iic_sendbyte (0xa0);
iic_getack();
iic_sendbyte (addr);
iic_getack();
Stop();
delay_5ms(1);
return Read_Current();
}
void write_iic_nbyte(uchar *data, uchar numBytes) // IIC 向从设备写入nbyte数据
{
iic_start();
iic_sendbyte(0xa0);
iic_getack();
for(uchar j = 0; j < numBytes; j++){
iic_sendbyte(data[j]);
iic_getack();
}
delay_5ms(1);
iic_stop();
}
void read_iic_nbyte(uchar *data, uchar numBytes) // IIC 向从设备读出nbyte数据
{
iic_start();
iic_sendbyte(0xa1);
iic_getack();
for(uchar j = 0; j < numBytes; j++){
data[j] = iic_getbyte();
if(j==numBytes-1){
iic_sendnack();
}
else{
iic_sendack();
}
}
delay_5ms(1);
iic_stop();
}
技术交流加微信:WX229340241