IIC总线通信协议(含底层细节)

IIC

IIC:集成电路总线(Inter-Integrated Circuit)
快速:400kbit/s 高速:3.4Mbit/s
速度由 SCL 决定,上升沿斜率受上拉电阻和等效电容影响。

1、物理层

请添加图片描述
两线式串行总线,可发送和接收数据。
数据线:SDA
时钟线:SCL
同步(带时钟)串行 半双工(发送或接收)通信方式 。
IIC 的 GPIO 为开漏模式,支持线与功能。开漏模式,不提供输入电流,保护其他设备;利用上拉电阻,实现于其他设备的电压匹配(都是高电平)。
开漏模式无法输出高电平(当输出为高电平时,输出端口处于高阻态),所以需要外部上拉两条线上接有上拉电阻,保证空闲状态处于稳定的高电平
可以连接多个设备(保证设备地址不同),支持多主机多从机连接模式,上图可认为是一个主机(启动数据传输,并产生时钟信号的设备),多个从机(被主机寻址的数据)。
多主机则采用仲裁模式。
传输速率 快速:400kbit/s 高速:3.4Mbit/s

2、协议层

1、空闲状态
:两条信号线同时处于高电平。两个线都接有上拉电阻,保证其空闲状态处于稳定高电平

2、起始信号(START)
SCL时钟线为高时,SDA数据线由高到低的跳变。

3、终止信号(STOP)
SCL时钟线为高时,SDA数据线由低到高的跳变。
请添加图片描述
4、数据的有效性
SCL高电平时,SDA传输的数据稳定有效。想变化(传输数据时)需要在SCL低电平时准备好数据(数据在SCL的上升沿到来之前需要准备好,并在下降沿到来之前保持稳定)。
总结:SCL线的高电平用于传输有效数据,在高电平期间数据被读取或写入。

5、应答信号ACK
主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答。
应答信号为低电平时为有效应答位(从机的SDA向主机发送低电平)。
当发送高电平时,发送方会产生上述的停止信号来结束数据的发送。
SDA是一根线,线上被所有主机和从机占着,不是一个主机一直霸占着线,所以从机可以发信号。

请添加图片描述
图解:SCL高电平期间,主机SDA从高到低跳变,为起始信号S准备开始传输数据;SCL高电平,传输数据稳定,低电平期间发送变化(传输其他值)以此串行传输完一个字节后;从机ACK可以在SCL高电平期间,发出ACK应答,表示可继续传输数据。当发送高电平(非ACK应答)时,发送方会产生的停止信号来结束数据的发送。

6、数据传输
在SCL串行时钟下,SDA上逐位串行传输每一位的数据。数据位的传输时边沿触发。

3、总线寻址方式

按照从机地址位数可以分为7bits或10bits
在这里插入图片描述
D1-D7地址位;D0控制数据方向位,0:向从机写数据,1:向从机读数据
当主机发送7位地址到数据线上后,从机将会与其进行地址配对。数据传输方向则被D0位控制。7位从机地址一般可由4位固定地址和3位可编程地址组成,那么因此可得到,一共可挂载寻址8个从机(3位的二进制为8)。理论上可挂载2^8=128个设备(地址0x00不用),所以是127个设备,但是由于驱动有限,不可能挂那么多。

4、数据传输(详解)

IIC在SDA线上传输的是广义的数据(包括地址数据和数据信号),起始信号后,必须发送地址数据。
1、主机向从机发送数据,数据传送方向在整个传送过程中不变请添加图片描述
解释:灰色块为主机产生的信号,首先主机产生起始信号S,然后第一个数据发送从机地址,因为是发送数据,最低控制位为0;红色块为SDA(从机)发送ACK应答,则表示可继续传输数据,最后当主机接收到非ACK时或者即便从机发送ACK时,数据已经发完,那么最后主机产生终止信号P结束传输。

2、主机(在第一个字节即:地址后)从从机中读取数据
请添加图片描述
解释:主机产生起始信号S,第一个数据为发送从机地址,最低控制位为1,表示读取数据。从机发送ACK应答,然后从机通过SDA线(SDA线是共用的)发送数据给主机,此时主机产生ACK应答,从机则继续发送数据,当主机产生非ACK应答时,主机再发送停止信号P结束传输。

3、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读写方向位正好相反
请添加图片描述
解释:主机产生起始信号S开始通信,第一个字节(7位从机地址+0)主机向从机发送地址,从机产生ACK应答,主机发送数据,那么此时从机若发送ACK应答,可以继续进行传输数据。
此时主机若需要读取从机数据时,则起始信号和从机地址都被重复产生一次后,从机产生ACK应答,然后向主机发送数据,主机接收到数据后,发送非ACK,表示不需要再接收数据,发送停止信号S。

5、IIC内部协议(细节)

在这里插入图片描述
上图为设备内部引脚图连接与SDA和SCL线。表明SCL和SDA线都可由主从设备任意一方控制,且两线都有发送引脚和接收引脚。
主芯片通过一根 SDA 线既可以把数据发给从设备,也可以从SDA 上读取数据,连接SDA线的引脚里面必然有两个引脚(发送引脚和接收引脚)。

在这里插入图片描述
在这里插入图片描述
① 当某一个芯片不想影响 SDA 线时,那就不驱动这个三极管
② 想让SDA 输出高电平,双方都不驱动三极管 (SDA 通过上拉电阻变为高电平)不然就会变为高阻态。
③ 想让SDA 输出低电平,就驱动三极管

ACK应答信号为低电平的原因?
数据传输,当主设备发送完8位以后,第9位为ACK应答信号,此时主设备不驱动三极管,而要发应答信号时,只有当从设备驱动三极管,即当A=0;B=1时,此时输出为SDA为低电平。
为何SCL 也要使用上拉电阻?
在第 9 个时钟之后,如果有某一方需要更多的时间来处理数据,它可以一直驱动三极管把 SCL 拉低。当SCL为低电平时候,大家都不应该使用 IIC 总线,只有当SCL从低电平变为高电平的时候,IIC总线才能被使用。当它就绪后,就可以不再驱动三极管,这是上拉电阻把SCL 变为高电平,其他设备就可以继续使用I2C总线了。

6、IIC应用场景

传感器网络:I²C总线常用于连接各种传感器,如温度传感器、湿度传感器、光传感器等。传感器可以通过I²C总线与微控制器或其他主设备通信,从而实现数据采集和监测。
外围设备:I²C总线用于连接各种外围设备,如实时时钟(RTC)、EEPROM、LCD显示屏、扩展IO芯片等。通过I²C总线,主控制器可以方便地与这些外围设备进行通信和控制。
显示和音频设备:I²C总线常用于连接显示器和音频设备,如液晶显示屏、OLED显示屏、音频编解码器等。通过I²C总线,主控制器可以向这些设备发送控制命令和数据,实现显示和音频输出。

7、示例:STM32和EEPROM通信

下面以正点原子STM32F407与EEPROM(24C02)进行IIC通信为例,讲解部分代码

//产生IIC起始信号  SCL时钟线为高时,SDA数据线由高到低的跳变
void IIC_Start(void)
{
	SDA_OUT();     //SDA线输出 即主机输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;		//1~0跳变 起始信号
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	 
//产生IIC停止信号  SCL时钟线为高时,SDA数据线由低到高的跳变
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1;  
	IIC_SDA=1;//0~1跳变 发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入   则为从机输入发送信号
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)   //READ_SDA这个的值是由从机,即24c02发送信号过来 ,若为1则不应答,若为0则应答 这个是实际的应答
	{
		ucErrTime++;
		if(ucErrTime>250) //超时响应 则终止
		{
			IIC_Stop();
			return 1;
		}
	} 	
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
}
//产生ACK应答  程序模拟的ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答  1,有应答  0,无应答			  
void IIC_Send_Byte(u8 txd) //txd发送的数据
{                        
	u8 t;   
	SDA_OUT(); 	    
	IIC_SCL=0;//拉低时钟开始数据传输
	for(t=0;t<8;t++) //以此从最高位读取数据到SDA线上
	{              
		IIC_SDA=(txd&0x80)>>7; //以此取最高位 右移7位取出送入数据线
		txd<<=1; //以此左移一位  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
	}	 
}
//读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++;   //读取从机SDA上的数据,该位为1则++, 依次从最低位开始获取  
		delay_us(1); 
	}					 
	  if (!ack)
	      IIC_NAck();//发送NACK
	  else
	      IIC_Ack(); //发送ACK   
	  return receive;
}
//在AT24CXX指定地址写入一个数据  注意地址需要发两次
//第一次是片外寻址,第二次是片内地址
//WriteAddr  :写入数据的目的地址    
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{				   	  	    																 
	IIC_Start();  //开始信号
	IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 	 
	IIC_Wait_Ack();	  //等待ACK应答 
	IIC_Send_Byte(WriteAddr%256);   //发送低地址
	IIC_Wait_Ack(); 	 										  		   
	IIC_Send_Byte(DataToWrite);     //发送字节							   
	IIC_Wait_Ack();  		    	   
	IIC_Stop();//产生一个停止条件 
	delay_ms(10);	 
}

//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
	IIC_Start();  
	IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据 	 
	//写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。
	IIC_Wait_Ack(); 
	IIC_Send_Byte(ReadAddr%256);   //发送低地址
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(0XA1);           //进入接收模式			   
	IIC_Wait_Ack();	 
	temp=IIC_Read_Byte(0);		 		//0表示不需要ACK应答,读完数据就完事儿了  
	IIC_Stop();//产生一个停止条件	    
	return temp;
}

主函数 通过按键对AT24C02芯片利用IIC通信进行写或者读操作

	while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY1_PRES)//KEY1按下,写入24C02
		{
			LCD_Fill(0,170,239,319,WHITE);//清除半屏    
 			LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");
			AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
			LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示传送完成
		}
		if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
		{
 			LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");
			AT24CXX_Read(0,datatemp,SIZE);
			LCD_ShowString(30,170,200,16,16,"The Data Readed Is:  ");//提示传送完成
			LCD_ShowString(30,190,400,16,16,datatemp);//显示读到的字符串
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;//提示系统正在运行	
			i=0;
		}		   
	}

以上是我对IIC通信的粗略理解,参考了正点原子的标准库视频、韦东山Linux应用IIC视频,这边推荐看普中51单片机视频中对IIC的经典讲解,有问题可以互相交流。谢谢!(视频均来自B站)

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值