cortex_m3_stm32嵌入式學習筆記(二十):IIC實驗(I2C串行總線)

ARM 755瀏覽
IIC(Inter- Integrated Circuit)總線是一種由 PHILIPS 公司開發的兩線式串行總線,用于連接微控制器及其外圍設備。它是由數據線SDA 和時鐘SCL構成的串行總線,可發送和接收數據。在 CPU 與被控 IC 之間、 IC 與 IC 之間進行雙向傳送, 高速 IIC 總線一般可達 400kbps 以上。

ALIENTEK MiniSTM32 開發板板載的 EEPROM 芯片型號為 24C02。該芯片的總容量是 256個字節,該芯片通過 IIC 總線與外部連接,我們本章就通過 STM32 來實現 24C02 的讀寫。目前大部分 MCU 都帶有 IIC 總線接口, STM32 也不例外。但是這里我們不使用 STM32的硬件 IIC 來讀寫 24C02,而是通過軟件模擬。STM32 的硬件 IIC 非常復雜,更重要的是不穩定,故不推薦使用。所以我們這里就通過模擬來實現了。有興趣的讀者可以研究一下 STM32的硬件 IIC。

科普EEPROM,或寫作E2PROM,全稱電子抹除式可復寫只讀存儲器?(英語:Electrically-Erasable Programmable Read-Only Memory),是一種可以通過電子方式多次復寫的半導體存儲設備,可以在電腦上或專用設備上擦除已有信息,重新編程。相比EPROM,EEPROM不需要用紫外線照射,也不需取下,就可以用特定的電壓,來抹除芯片上的信息,以便寫入新的數據。

I2C 總線在傳送數據過程中共有三種類型信號, 它們分別是:開始信號、結束信號和應答信號。
開始信號: SCL 為高電平時, SDA 由高電平向低電平跳變,開始傳送數據。
結束信號: SCL 為高電平時, SDA 由低電平向高電平跳變,結束傳送數據。
應答信號:接收數據的 IC 在接收到 8bit 數據后,向發送數據的 IC 發出特定的低電平脈沖,表示已收到數據。 CPU 向受控單元發出一個信號后,等待受控單元發出一個應答信號, CPU 接收到應答信號后,根據實際情況作出是否繼續傳遞信號的判斷。若未收到應答信號,由判斷為受控單元出現故障。

這些信號中,起始信號是必需的,結束信號和應答信號,都可以不要。

24C02 的 SCL 和 SDA 分別連在 STM32 的 PC12 和 PC11 上的,連接關系如圖24.2.1 所示:

? ? ?
在工程中添加兩個源文件分別是 myiic.c 和 24cxx.c,myiic.c 文件存放 iic 驅動代碼, 24cxx.c 文件存放 24C02 驅動代碼

myiic.c

#include "24cxx.h" //初始化IIC接口 void AT24CXX_Init(void) { 	IIC_Init(); } //在AT24CXX指定地址讀出一個數據 //ReadAddr:開始讀數的地址   //返回值  :讀到的數據 u8 AT24CXX_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; } //在AT24CXX指定地址寫入一個數據 //WriteAddr  :寫入數據的目的地址     //DataToWrite:要寫入的數據 void AT24CXX_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);	  } //在AT24CXX里面的指定地址開始寫入長度為Len的數據 //該函數用于寫入16bit或者32bit的數據. //WriteAddr  :開始寫入的地址   //DataToWrite:數據數組首地址 //Len        :要寫入數據的長度2,4 void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len) {  	 	u8 t; 	for(t=0;t<Len;t++) 	{ 		AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff); 	}												     }  //在AT24CXX里面的指定地址開始讀出長度為Len的數據 //該函數用于讀出16bit或者32bit的數據. //ReadAddr   :開始讀出的地址  //返回值     :數據 //Len        :要讀出數據的長度2,4 u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len) {  	 	u8 t; 	u32 temp=0; 	for(t=0;t<Len;t++) 	{ 		temp<<=8; 		temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); 	 				    	} 	return temp;												     } //檢查AT24CXX是否正常 //這里用了24XX的最后一個地址(255)來存儲標志字. //如果用其他24C系列,這個地址要修改 //返回1:檢測失敗 //返回0:檢測成功 u8 AT24CXX_Check(void) { 	u8 temp; 	temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX			    	if(temp==0X55)return 0;		    	else//排除第一次初始化的情況 	{ 		AT24CXX_WriteOneByte(255,0X55); 	    temp=AT24CXX_ReadOneByte(255);	   		if(temp==0X55)return 0; 	} 	return 1;											   }  //在AT24CXX里面的指定地址開始讀出指定個數的數據 //ReadAddr :開始讀出的地址 對24c02為0~255 //pBuffer  :數據數組首地址 //NumToRead:要讀出數據的個數 void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) { 	while(NumToRead) 	{ 		*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);	 		NumToRead--; 	} }   //在AT24CXX里面的指定地址開始寫入指定個數的數據 //WriteAddr :開始寫入的地址 對24c02為0~255 //pBuffer   :數據數組首地址 //NumToWrite:要寫入數據的個數 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) { 	while(NumToWrite--) 	{ 		AT24CXX_WriteOneByte(WriteAddr,*pBuffer); 		WriteAddr++; 		pBuffer++; 	} }

myiic.h

#ifndef __MYIIC_H #define __MYIIC_H #include "sys.h" #include "delay.h" //IO方向設置 #define SDA_IN()  {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;} #define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;} //IO操作函數	  #define IIC_SCL    PCout(12)//SCL #define IIC_SDA    PCout(11)//SDA	  #define READ_SDA   PCin(11)//輸入SDA  //IIC所有操作函數 void IIC_Init(void);//初始化IIC的IO口				  void IIC_Start(void);//發送IIC開始信號 void IIC_Stop(void);//發送IIC停止信號 void IIC_Send_Byte(u8 txd);//IIC發送一個字節 u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節 u8 IIC_Wait_Ack(void);//IIC等待ACK信號 void IIC_Ack(void);//IIC發送ACK信號 void IIC_NAck(void);//IIC不發送ACK信號 void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data); u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	   #endif

24cxx.c

#include "24cxx.h" //初始化IIC接口 void AT24CXX_Init(void) { 	IIC_Init(); } //在AT24CXX指定地址讀出一個數據 //ReadAddr:開始讀數的地址   //返回值  :讀到的數據 u8 AT24CXX_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; } //在AT24CXX指定地址寫入一個數據 //WriteAddr  :寫入數據的目的地址     //DataToWrite:要寫入的數據 void AT24CXX_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);	  } //在AT24CXX里面的指定地址開始寫入長度為Len的數據 //該函數用于寫入16bit或者32bit的數據. //WriteAddr  :開始寫入的地址   //DataToWrite:數據數組首地址 //Len        :要寫入數據的長度2,4 void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len) {  	 	u8 t; 	for(t=0;t<Len;t++) 	{ 		AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff); 	}												     }  //在AT24CXX里面的指定地址開始讀出長度為Len的數據 //該函數用于讀出16bit或者32bit的數據. //ReadAddr   :開始讀出的地址  //返回值     :數據 //Len        :要讀出數據的長度2,4 u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len) {  	 	u8 t; 	u32 temp=0; 	for(t=0;t<Len;t++) 	{ 		temp<<=8; 		temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); 	 				    	} 	return temp;												     } //檢查AT24CXX是否正常 //這里用了24XX的最后一個地址(255)來存儲標志字. //如果用其他24C系列,這個地址要修改 //返回1:檢測失敗 //返回0:檢測成功 u8 AT24CXX_Check(void) { 	u8 temp; 	temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX			    	if(temp==0X55)return 0;		    	else//排除第一次初始化的情況 	{ 		AT24CXX_WriteOneByte(255,0X55); 	    temp=AT24CXX_ReadOneByte(255);	   		if(temp==0X55)return 0; 	} 	return 1;											   }  //在AT24CXX里面的指定地址開始讀出指定個數的數據 //ReadAddr :開始讀出的地址 對24c02為0~255 //pBuffer  :數據數組首地址 //NumToRead:要讀出數據的個數 void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) { 	while(NumToRead) 	{ 		*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);	 		NumToRead--; 	} }   //在AT24CXX里面的指定地址開始寫入指定個數的數據 //WriteAddr :開始寫入的地址 對24c02為0~255 //pBuffer   :數據數組首地址 //NumToWrite:要寫入數據的個數 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) { 	while(NumToWrite--) 	{ 		AT24CXX_WriteOneByte(WriteAddr,*pBuffer); 		WriteAddr++; 		pBuffer++; 	} }

24cxx.h

#ifndef __24CXX_H #define __24CXX_H #include "myiic.h"   #include "delay.h" #define AT24C01		127 #define AT24C02		255 #define AT24C04		511 #define AT24C08		1023 #define AT24C16		2047 #define AT24C32		4095 #define AT24C64	    8191 #define AT24C128	16383 #define AT24C256	32767   #define EE_TYPE AT24C02 u8 AT24CXX_ReadOneByte(u16 ReadAddr);//指定地址讀取一個字節 void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);//指定地址寫入一個字節 void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址開始寫入指定長度的數據 u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);//指定地址開始讀取指定長度數據 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);//從指定地址開始寫入指定長度的數據 void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);//從指定地址開始讀出指定長度的數據 u8 AT24CXX_Check(void);//檢查器件 void AT24CXX_Init(void);//初始化IIC #endif

代碼有點長。。但其實配好了用到一般只有讀和寫兩個函數如下:

//在AT24CXX里面的指定地址開始讀出指定個數的數據 //ReadAddr :開始讀出的地址 對24c02為0~255 //pBuffer  :數據數組首地址 //NumToRead:要讀出數據的個數 void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) { 	while(NumToRead) 	{ 		*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);	 		NumToRead--; 	} }   //在AT24CXX里面的指定地址開始寫入指定個數的數據 //WriteAddr :開始寫入的地址 對24c02為0~255 //pBuffer   :數據數組首地址 //NumToWrite:要寫入數據的個數 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) { 	while(NumToWrite--) 	{ 		AT24CXX_WriteOneByte(WriteAddr,*pBuffer); 		WriteAddr++; 		pBuffer++; 	} }

參數解釋的很明白也很好理解。。

主函數

#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "lcd.h" #include "key.h" #include "myiic.h" #include "24cxx.h" const u8 TEXT_Buffer[]={"MY IIC TEST"}; #define SIZE sizeof(TEXT_Buffer) void init(void) { 	NVIC_Configuration(); 	delay_init(); 	uart_init(9600); 	LED_Init(); 	KEY_Init(); 	LCD_Init(); 	AT24CXX_Init(); 	POINT_COLOR=RED;//設置字體為紅色  	LCD_ShowString(60,40,200,24,24,"IIC TEST");	 	LCD_ShowString(60,70,200,16,16,"~~~~~~~~~");	 	LCD_ShowString(60,90,200,16,16,"BY---yh"); 	LCD_ShowString(60,110,200,16,16,"2015/1/26");	 	LCD_ShowString(60,130,200,16,16,"WK_UP:Write  KEY0:Read");	 	while(AT24CXX_Check())//檢測不到24c02 	{ 		LCD_ShowString(60,150,200,16,16,"24C02 Check Failed!"); 		delay_ms(500); 		LCD_ShowString(60,150,200,16,16,"Please Check!      "); 		delay_ms(500); 		LED0=!LED0;//DS0閃爍 	} 	LCD_ShowString(60,150,200,16,16,"24C02 Ready!"); 	POINT_COLOR=BLUE; } int main(void) { 	u8 key; 	u16 i=0; 	u8 datatemp[SIZE]; 	init(); 	while(1) 	{ 		key=KEY_Scan(0); 		if(key==WK_UP_PRES)//寫 		{ 			LCD_Fill(0,170,239,319,WHITE);//清除半屏      			LCD_ShowString(60,170,200,16,16,"Start Write 24C02...."); 			AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE); 			LCD_ShowString(60,170,200,16,16,"24C02 Write Finished!");//提示傳送完成 		} 		if(key==KEY0_PRES)//KEY0 按下,讀取字符串并顯示 		{  			LCD_ShowString(60,170,200,16,16,"Start Read 24C02.... "); 			AT24CXX_Read(0,datatemp,SIZE); 			LCD_ShowString(60,170,200,16,16,"The Data Readed Is:  ");//提示傳送完成 			LCD_ShowString(60,190,200,16,16,datatemp);//顯示讀到的字符串 		} 		i++; 		delay_ms(10); 		if(i==20) 		{ 			LED0=!LED0; 			i=0; 		} 	} }

七星彩走势图2元网官网