IIC原理超详细讲解—值得一看[通俗易懂]

IIC原理超详细讲解—值得一看[通俗易懂]文章目录IIC简介IIC的物理层IIC的高阻态IIC物理层总结:IIC的协议层IIC总线时序图初始(空闲)状态开始信号:停止信号数据有效性应答信号IIC数据传送数据传送格式IIC发送数据IIC读数据:以AT24C02为例子软件IIC和硬件IICIIC简介    IIC(Inter-IntegratedCircuit)总线是一种由NXP…

大家好,又见面了,我是你们的朋友全栈君。

IIC 简介

        IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。

PS: 这里要注意IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI
在这里插入图片描述

IIC的物理层

IIC一共有只有两个总线: 一条是双向的串行数据线SDA,一条是串行时钟线SCL

  • SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的

  • SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxNfPPim-1586571859509)(C:\Users\48013\AppData\Roaming\Typora\typora-user-images\image-20200331212342296.png)]

IIC主要特点:

通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。**

  • IIC主设备功能:主要产生时钟,产生起始信号和停止信号

  • IIC从设备功能:可编程的IIC地址检测,停止位检测

  • IIC的一个优点是它支持多主控(multimastering), 其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

  • 支持不同速率的通讯速度,标准速度(最高速度100kHZ),快速(最高400kHZ)

  • SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。

  • IIC是半双工,而不是全双工 ,同一时间只可以单向通信

  • 为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。这一点在等下我们会讲解

IIC的高阻态

漏极开路(Open Drain)即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻

高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可做开路理解

我们知道IIC的所有设备是接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰到总线,怎么办呢?

为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备。

在这里插入图片描述

IIC器件地址: 每一个IIC器件都有一个器件地址,有的器件地址在出厂时地址就设定好了,用户不可以更改,比如OV7670的地址为0x42。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址是由硬件链接确定的,所以一IIC总线最多能连8个EEPROM芯片。

IIC物理层总结:

I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,SCL和SDA被上拉电阻Rp拉高,使SDA和SCL线都保持高电平。

I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和uart通信为全双工。

主机和从机的概念

主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。

  • 发布主要命令的称为主机

  • 接受命令的称为从机

半双工和全双工:

IIC的协议层

I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

  • 开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
  • 结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
  • 应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

IIC 总线时序图

img

下面我们来详细的介绍下IIC的通信协议流程:

初始(空闲)状态

因为IIC的 SCL 和SDA 都需要接上拉电阻,保证空闲状态的稳定性

所以IIC总线在空闲状态下SCL 和SDA都保持高电平

代码:

void IIC_init()       //IIC初始化

{ 
   

       SCL=1; //首先把时钟线拉高

       delay_us(4);//延时函数

       SDA=1; //在SCL为高的情况下把SDA拉高

       delay_us(4); //延时函数

}

开始信号:

SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。

在这里插入图片描述

代码表示:

//产生IIC起始信号
//1.先拉高SDA,再拉高SCL,空闲状态
//2.拉低SDA
void IIC_Start()         //启动信号

{ 
   
       SDA=1; //确保SDA线为高电平
       delay_us(5);
       SCL=1;  //确保SCL高电平
       delay_us(5);
       SDA=0; //在SCL为高时拉低SDA线,即为起始信号
       delay_us(5);
       SCL=0;   //钳住I2C总线,准备发送或接收数据 
    
}

停止信号

停止信号:SCL保持高电平。SDA由低电平变为高电平。

在这里插入图片描述

//产生IIC停止信号
//1.先拉低SDA,再拉低SCL
//2.拉高SCL
//3.拉高SDA
//4.停止接收数据
void IIC_Stop(void)
{ 
   

	IIC_SCL=0;
	IIC_SDA=0;    //STOP:当SCL高时,数据由低变高
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;    //发送I2C总线结束信号
	delay_us(4);							   	
}

在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。

在这里插入图片描述

数据有效性

IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

也就是在IIC传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输

在这里插入图片描述

img

应答信号

每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,

应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答

  • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

在这里插入图片描述

**每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。

应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,

在这里插入图片描述

//主机产生应答信号ACK
//1.先拉低SCL,再拉低SDA
//2.拉高SCL
//3.拉低SCL## 标题
void I2C_Ack(void)
{ 
   
   IIC_SCL=0;   //先拉低SCL,使得SDA数据可以发生改变
   IIC_SDA=0;   
   delay_us(2);
   IIC_SCL=1;
   delay_us(5);
   IIC_SCL=0;
}


//主机不产生应答信号NACK
//1.先拉低SCL,再拉高SDA
//2.拉高SCL
//3.拉低SCL
void I2C_NAck(void)
{ 
   
   IIC_SCL=0;   //先拉低SCL,使得SDA数据可以发生改变
   IIC_SDA=1;   //拉高SDA,不产生应答信号
   delay_us(2);
   IIC_SCL=1;
   delay_us(5);
   IIC_SCL=0;
}

等待应答信号:

//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
char IIC_Wait_Ack(void)
{ 
   
	u8 ucErrTime=0;
	 
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(IIC_SDA)
	{ 
   
		ucErrTime++;
		if(ucErrTime>250)
		{ 
   
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 
	return 0;  
} 

IIC数据传送

数据传送格式

SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK, 此时才认为一个字节真正的被传输完成 ,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。

img

IIC写数据:

img

多数从设备的地址为7位或者10位,一般都用七位。
八位设备地址=7位从机地址+读/写地址,

再给地址添加一个方向位位用来表示接下来数据传输的方向,

  • 0表示主设备向从设备(write)写数据,

  • 1表示主设备向从设备(read)读数据

IIC的每一帧数据由9bit组成,

如果是发送数据,则包含 8bit数据+1bit ACK,

如果是设备地址数据,则8bit包含7bit设备地址 1bit方向
在这里插入图片描述

在起始信号后必须传送一个从机的地址(7位) 1~7位为7位接收器件地址,第8位为读写位,用“0”表示主机发送数据(W),“1”表示主机接收数据 (R), 第9位为ACK应答位,紧接着的为第一个数据字节,然后是一位应答位,后面继续第2个数据字节。

IIC发送一个字节数据:

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答 

//IIC_SCL=0;
//在SCL上升沿时准备好数据,进行传送数据时,拉高拉低SDA,因为传输一个字节,一个SCL脉冲里传输一个位。
//数据传输过程中,数据传输保持稳定(在SCL高电平期间,SDA一直保持稳定,没有跳变)
//只有当SCL被拉低后,SDA才能被改变
//总结:在SCL为高电平期间,发送数据,发送8次数据,数据为1,SDA被拉高,数据为0,SDA被拉低。
//传输期间保持传输稳定,所以数据线仅可以在时钟SCL为低电平时改变。
void IIC_Send_Byte(u8 txd)
{ 
                           
    u8 t;   
    SDA_OUT();         
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    { 
                 
        //IIC_SDA=txd&0x80; //获取最高位
        //获取数据的最高位,然后数据左移一位
        //如果某位为1,则SDA为1,否则相反
        if(txd&0x80)
            IIC_SDA=1;
        else
            IIC_SDA=0;
        txd<<=1;       
        delay_us(2);   
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0;    
        delay_us(2);
    }     
}       
或者:
        //IIC_SDA=txd&0x80; //获取最高位
        //获取数据的最高位,然后右移7位,假设为 1000 0000 右移7位为 0000 0001 
        // 假设为 0000 0000 右移7位为 0000 0000 
        //如果某位为1,则SDA为1,否则相反
        IIC_SDA=((txd&0x80)>>7);
        txd<<=1;


IIC读取一个字节数据:


//读1个字节,ack=1时,发送ACK,ack=0,发送nACK 
u8 IIC_Read_Byte(unsigned char ack)
{ 
   
	unsigned char i,receive=0;
	SDA_IN();        //SDA设置为输入
    for(i=0;i<8;i++ )
	{ 
   
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();        //发送nACK
    else
        IIC_Ack();         //发送ACK 
    return receive;
}

IIC发送数据

在这里插入图片描述
在这里插入图片描述

Start: IIC开始信号,表示开始传输。
DEVICE_ADDRESS:: 从设备地址,就是7位从机地址
R/W: W(write)为写,R(read)为读
ACK: 应答信号
WORD_ADDRESS : 从机中对应的寄存器地址 比方说访问 OLED中的 某个寄存器
DATA: 发送的数据
STOP: 停止信号。结束IIC

主机要向从机写数据时:

  1. 主机首先产生START信号
  2. 然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方 向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)
  3. 主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
  4. 这时候主机等待从机的应答信号(A)
  5. 当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号
  6. 当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号,
  7. 主机产生停止信号,结束传送过程。

IIC读数据:

在这里插入图片描述
主机要从从机读数据时

  1. 主机首先产生START信号
  2. 然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令,
  3. 这时候主机等待从机的应答信号(ACK)
  4. 当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,
  5. 当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设 置成接收模式开始读取数据,
  6. 这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,表示不在接收数据
  7. 主机进而产生停止信号,结束传送过程。

以AT24C02为例子

24C02是一个2K Bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。
在这里插入图片描述
A0,A1,A2:硬件地址引脚
WP:写保护引脚,接高电平只读,接地允许读和写
SCL和SDA:IIC总线

以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:
在这里插入图片描述
芯片的寻址:
AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。A2~A0为000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。

也就是说如果是
写24C02的时候从器件地址为10100000(0xA0);
读24C02的时候,从器件地址为10100001(0xA1)。

片内地址寻址:

芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。

对应的修改 A2A1A0 三位数据即可
在这里插入图片描述

向AT24C02中写数据
在这里插入图片描述
操作时序:

  1. MCU先发送一个开始信号(START)启动总线
  2. 接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
  3. 等待应答信号(ACK)
  4. 发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储>在哪个位置,此刻写的就是哪个地址。
  5. 发送要存储的数据第一字节、第二字节、…注意在写数据的过程中,E2PROM每个字节都会>回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。
  6. 发送结束信号(STOP)停止总线

注意:
在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。

写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以

从AT24C02中读数据

读当前地址的数据
在这里插入图片描述
2、读随机地址的数据
在这里插入图片描述

  1. MCU先发送一个开始信号(START)启动总线
  2. 接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
    注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。
  3. 发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。
  4. 重新发送开始信号(START)
  5. 发送设备读操作地址(DEVICE ADDRESS)对E2PROM进行读操作 (0xA1)
  6. E2PROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据
  7. 如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。发送结束信号(STOP)停止总线

3、连续读数据
在这里插入图片描述
E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。不应答发送结束信号后终止传输。


//IIC发送数据
//address 要写入的地址
//date 要写入的数据
void write_add(uchar address,uchar date)
{ 
   
        IIC_Start();
        IIC_Send_Byte(0xA0);
       	delay(2);
        IIC_Send_Byte(address);
        delay(2);
        IIC_Send_Byte(date);
       delay(2);
        IIC_Stop();
}
//IIC读取数据
// address 要读取数据的地址
uchar read_add(uchar address)  //指定地址读一个字节数据
{ 
   
	uchar add;
	IIC_Start();
	IIC_Send_Byte(0xA0);
	delay(2);
	IIC_Send_Byte(address);
	delay(2);
	IIC_Start();
	IIC_Send_Byte(0xA1);
	delay(2);
	dd=IIC_Read_Byte();
	 IIC_Stop();
	return add;
}

软件IIC和硬件IIC

IIC分为软件IIC和硬件IIC

软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。

硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。

硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。

在这里插入图片描述
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/130294.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • POJ培训计划2253_Frogger(最短/floyd)

    POJ培训计划2253_Frogger(最短/floyd)

  • Kafka简明教程「建议收藏」

    Kafka简明教程「建议收藏」作者:柳树之www.jianshu.com/p/7b77723d4f96Kafka是啥?用Kafka官方的话来说就是:Kafkaisusedforbuildingreal-timedatapipelinesandstreamingapps.Itishorizontallyscalable,fault-tolerant,wickedfast,an…

    2022年10月17日
  • Python文本情感分析_Python数据分析实战

    Python文本情感分析_Python数据分析实战本文由来为了赚足学分丰富假期生活,初衷是分析老师对学生作业的评价和学生对老师的评价的。本来这个任务是在N多天前就应该完成了,无奈本人懒癌晚期+拖延症不想治疗,不是因为火烧眉毛就绝对没有今天的文章。本文旨在记录自己的学习过程,就这样,开干啦!序幕既然题目是“基于情感词典的文本情感分析”,那么情感词典就是必不可少的了。对于情感词典的要求:要包含积极的词语和消极的词语、每一种类的数量要足够多、包含足够广…

  • 机器学习之隐马尔可夫模型

      本文主要是学习笔记,一方面是为了加强理解,感觉在做笔记过程中理解起来更简单,另一方面为了加强记忆,建立大脑关于‘隐马尔可夫模型’的神经网络1.模型场景在介绍隐马尔可夫模型

    2021年12月30日
  • Protostuff序列化和反序列化使用说明

    Protostuff序列化和反序列化使用说明google原生的protobuffer使用起来相当麻烦,首先要写.proto文件,然后编译.proto文件,生成对应的.java文件,鄙人试了一次,发现真的很麻烦。而protostuff的官方网站(http://www.protostuff.io/documentation/runtime-schema/),对于智商比较低的小编来说也略显生涩,于是鄙人就根据项目中用到的protostuff,撰写此文,以方便自己和他人加深印象和学习。

  • sql语句 模糊查找like

    sql语句 模糊查找like模糊查找:like语法形式:字段like’要查找字符’说明:1、like模糊查找用于对字符类型的字段进行字符匹配查找。2、要查找的字符中,有两个特殊含义的字符:%,_:2.1:%含义是:代表0或多个的任意字符2.2:_含义是:代表1个任意字符2.3:这里的字符都是指现实中可见的一个“符号”,而不是字节。3、语法:like’%关键字%’SELECT*FROMstudentWHERENAMELIKE’张%’;–…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号