STM32學習筆記(14)I2C(IIC)介紹

IIC簡介

IIC(又稱I2C):Inter-Integrated Circuit,一種兩線式串行總線,用於連接微控制器及其外圍設備。它是由數據線 SDA 和時鐘 SCL 構成的串行總線,可發送和接收數據。(半雙工通信)在 CPU 與被控 IC 之間、 IC 與 IC 之間進行雙向傳送, 高速 IIC 總線一般可達 400kbps 以上。

I2C 總線在傳送數據過程中共有三種類型信號, 它們分別是:開始信號、結束信號和應答信號。

IIC協議

空閒狀態

I2C總線總線的SDA和SCL兩條信號線同時處於高電平時,規定爲總線的空閒狀態。此時各個器件的輸出級場效應管均處在截止狀態,即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。(即使用時會接上拉電阻,所以雙高電平表示無信號)

起始信號與結束信號

  • 起始信號:當SCL爲高期間,SDA由高到低的跳變,啓動信號是一種電平跳變時序信號,而不是一個電平信號。(類似邊沿觸發)
  • 停止信號:當SCL爲高期間,SDA由低到高的跳變。(同上)

在這裏插入圖片描述

應答信號ACK

**發送器每發送一個字節(8個位)就在時鐘脈衝9期間釋放數據線(半雙工),由接收器反饋一個應答信號。**應答信號爲低電平時,規定內有效應答位(ACK簡稱應答位),表示接收器已經成功地接收了該字節,應答信號爲高電平時,規定爲非應答位(NACK),一般表示接收器接收該字節沒有成功。

**對於反饋有效應答位ACK的要求是,接收器在第9個時鐘脈衝之前的低電平期間將SDA線拉低,並且確保在該時鐘的高電平期間爲穩定的低電平。**如果接收器是主控器,則在它收到最後一個字節後,發送一個NACK信號,以通知被控發送器結束數據發送,並釋放SDA線,以便主控接收器發送一個停止信號P.

在這裏插入圖片描述

數據有效性

I2C總線進行數據傳送時,時鐘信號爲高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才允許變化。

即。數據在SCL的上升沿到來之前就需準備好。並在在下降沿到來之前必須穩定。

在這裏插入圖片描述

數據的傳送

在I2C總線上傳送的每一位數據都有一個時鐘脈衝相對應(或同步控制),即在SCL串行時鐘的配合下,在SDA上逐位地串行傳送每一位數據。數據位的傳輸是邊沿觸發。(參考ACK應答信號的圖片)

邊沿觸發:在數字電平變化的電壓上升沿或下降沿到一定閥值時就產生觸發。邊沿觸發一般時間短,邊沿觸發一般時間都是us級的,響應要快的,

傳輸過程

在這裏插入圖片描述

相關配置

標題初始化IIC(這裏用IO口模擬)

void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;//初始化結構體
	
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB時鐘
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 輸出高
}

發送起始信號

void IIC_Start(void)
{
	SDA_OUT();     //SDA線輸出(半雙工需要)
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//當SCL爲高期間,SDA由高到低的跳變
	delay_us(4);
	IIC_SCL=0;//準備發送或接收數據 (數據傳輸需要拉低)
}	  

發送結束信號

void IIC_Stop(void)
{
	SDA_OUT();//SDA線輸出
	IIC_SCL=0;
	IIC_SDA=0;
 	delay_us(4);
	IIC_SCL=1;
	IIC_SDA=1; //當SCL爲高期間,SDA由低到高的跳變
	delay_us(4);							   	
}

應答信號ACK

接收ACK

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:輸入的數據
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;//接收應答失敗,停止傳輸
		}
	}
	IIC_SCL=0; 	   
	return 0;  ////接收應答成功,可繼續傳輸
} 

發送ACK

u8 IIC_Read_Byte(unsigned char ack)//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;
}

void IIC_Ack(void)   //發送ACK
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}

void IIC_NAck(void)   //發送NACK
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					

發送一個字節

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低時鐘開始數據傳輸
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;//IIC_SDA此時等於相應位
        txd<<=1; 	  
		delay_us(2);   
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	  

例子

//在AT24C16指定地址讀出一個數據
//ReadAddr:開始讀數的地址  
//返回值  :讀到的數據
u8 AT24C16_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	   //發送寫命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//發送高地址
		IIC_Wait_Ack();		 
	}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //發送器件地址0XA0,寫數據 	 

	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);		   
    IIC_Stop();//產生一個停止條件	    
	return temp;
}
//在AT24C16指定地址寫入一個數據
//WriteAddr  :寫入數據的目的地址    
//DataToWrite:要寫入的數據
void AT24C16_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{				   	  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	    //發送寫命令
		IIC_Wait_Ack();
		IIC_Send_Byte(WriteAddr>>8);//發送高地址
 	}else
	{
		IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //發送器件地址0XA0,寫數據 
	}	 
	IIC_Wait_Ack();	   
    IIC_Send_Byte(WriteAddr%256);   //發送低地址
	IIC_Wait_Ack(); 	 										  		   
	IIC_Send_Byte(DataToWrite);     //發送字節							   
	IIC_Wait_Ack();  		    	   
    IIC_Stop();//產生一個停止條件 
	delay_ms(10);	 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章