浅谈IIC总线通信协议

IIC

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

物理层

浅谈IIC总线通信协议_第1张图片
两线式串行总线,可发送和接收数据。
数据线:SDA
时钟线:SCL
同步(带时钟)串行 半双工(发送或接收)通信方式 。
IIC 的 GPIO 为开漏模式,支持线与功能,开漏模式无法输出高电平,所以需要外部上拉两条线上接有上拉电阻,保证空闲状态处于稳定的高电平
可以连接多个设备(保证设备地址不同),支持多主机多从机连接模式,上图可认为是一个主机(启动数据传输,并产生时钟信号的设备),多个从机(被主机寻址的数据)。
多主机则采用仲裁模式。

协议层

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

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

3、终止信号(STOP)
SCL时钟线为高时,SDA数据线由低到高的跳变。
浅谈IIC总线通信协议_第2张图片
4、数据的有效性
SCL高电平时,SDA传输的数据稳定有效。想变化(传输数据时)需要在SCL低电平时变化(数据在SCL的上升沿到来之前需要准备好,并在下降沿到来之前保持稳定)。

5、应答信号ACK
主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答。
应答信号为低电平时为有效应答位(从机的SDA向主机发送低电平)。
当发送高电平时,发送方会产生上述的停止信号来结束数据的发送。
SDA是一根线,线上被所有主机和从机占着,不是一个主机一直霸占着线,所以从机可以发信号。
接收器在SCL第9个时钟脉冲之前的低电平期间将SDA线拉低,确保在该时钟的高电平期间从机SDA为稳定的低电平。
浅谈IIC总线通信协议_第3张图片
图解:SCL高电平期间,主机SDA从高到低跳变,为起始信号S准备开始传输数据;SCL高电平,传输数据稳定,低电平期间发送变化(传输其他值)以此串行传输完一个字节后;从机ACK可以在SCL高电平期间,发出ACK应答,表示可继续传输数据。当发送高电平(非ACK应答)时,发送方会产生的停止信号来结束数据的发送。

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

总线寻址方式

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

*数据传输(详解)

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。

下面以正点原子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通信的粗略理解,参考了正点原子的标准库视频,这边推荐看普中51单片机视频中对IIC的经典讲解,有问题可以互相交流。谢谢!(视频均来自B站)

你可能感兴趣的:(stm32,c语言,IIC,通信协议)