首頁>技術>

1.前奏

很久沒有更新部落格了,hiahia,之前忙著考研,現在卻忙著找工作,哎咦呦~,但也沒事,研沒考上也可以好好學學我感興趣的東西,用這些來找工作也挺好,做自己喜歡的事情,研究生還是想考,等快到暑假再看吧。這段時間重新把以前學的東西又鞏固了一遍,和我想的一樣,以前的基礎果然不牢。這次感覺花的時間挺久的就是IIC協議了,經過這次自己敲程式碼寫,對協議的理解更加深了。所以想分享給大家,主要是程式碼,雖然網上有很多,但是就是想把自己的程式碼分享出來,當然,其中也有一些走過的小誤區,可以給大家做一個參考,也可以讓自己以後回寫協議的時候有一個回憶參考。

2.IIC協議簡單說明

開始訊號:SCL為高電平時,SDA為下降沿;停止訊號:SCL為高電平時,SDA為上升沿;應答訊號:傳送完一個位元組後,當SCL為高電平時,讀出的SDA為低電平;傳送資料:把SDA資料一位一位準備好,SCL為上升沿時一位一位地寫入資料,方向為高位低位;接收資料:SCL為下降沿時一位一位地讀出資料,讀取資料的方向為高位低位

3.程式碼分享與說明及小誤區分享

我這裡的程式碼因為是我自己寫的,有的地方想盡量寫簡單,所以才會犯了我除錯過程中的一些錯誤,這裡面大家當做參考就好,如有和大家一樣的錯誤,那就真是英雄所見略同啦,這裡用的主控晶片是STM32F103ZET6。開始訊號:

//下降沿為開始訊號void IIC_Start(void){	 SDA_OUT();	 SDA=1;	 SCL=1;	 delay_us(3);	 SDA=0;	 delay_us(3);	 SCL=0;}

小說明:這裡的第二第三句SDA=1;SCL=1;最好不要變。誤區解讀:若是SCL=1;SDA=1;我們不能確定在SCL=1之前SDA是高電平還是低電平,假如是低電平,那麼這兩條語句之後,SDA就會產生一個上升沿。

停止訊號:

//上升沿為停止訊號void IIC_End(void){	 SDA_OUT();	 SDA=0;	 SCL=1;	 delay_us(3);	 SDA=1;	 delay_us(3);	 SCL=0;	 delay_us(1);}

小說明:同理,這裡的第二第三句SDA=0;SCL=1;不能變。誤區解讀:若是SCL=1;SDA=0;我們不能確定在SCL=1之前SDA是高電平還是低電平,假如是高電平,那麼這兩條語句之後,SDA就會產生一個下降沿。這裡我就是因為把這兩句調換了,所以才一直接受不到資料,除錯了很久,不知道是哪的問題,後來還是走在路上買東西一直想才終於搞明白了,然後才有了想法寫一篇部落格,hiahia~,開心開心。

應答訊號:

//返回值為0表示應答成功,1表示非應答uint8_t IIC_Ack(void){	 uint8_t count;     SDA_IN();	 SCL=0;	 delay_us(3);	 SCL=1;	 delay_us(2);	 while(Read_SDA==1)	 {		 count++;		 if(count>250)		 {			 IIC_End();			 return 1;		 }	 }	 SCL=0;    //釋放時鐘線	 return 0;}

小說明:這裡定義了一個count,為的就是防止SDA接收不到低電平程式卡炸在這裡。第三句SCL=0; delay_us(3);是先釋放匯流排,等待SDA的低電平訊號,但是親測過,不加這兩句也可以。

傳送資料:

//上升沿寫入資料,從高位到低位寫資料void IIC_Send_One_Byte(uint8_t data){	 uint8_t i;	 SCL=0;	 SDA_OUT();	 for(i=0;i<8;i++)     {	    if((data&0x80)>>7)				SDA=1;			else				SDA=0;			data<<=1;			delay_us(3);			SCL=1;			delay_us(3);			SCL=0;			delay_us(2);	   }	}

小說明:先把SDA上的資料準備好,然後利用SCL上升沿傳送,注意是先高位再低位傳送就可以,逐位資料程式碼傳送的演算法也可以自己再想想。

接收資料:

//下降沿讀出資料,從高位到低位讀資料uint8_t IIC_Read_One_Byte(void){     uint8_t data=0;	 uint8_t i;	 SDA_IN();	 for(i=0;i<7;i++)	 {		  SCL=1;	      delay_us(3);		  data|=Read_SDA;		  data<<=1;	      SCL=0;		  delay_us(3);	 }	 return data;}

誤區解讀:這裡注意i<7,之前想當然地以為接收8位資料就是i<8了,後來在自己的腦海中模擬了一下這段程式才想明白。

向AT24C02指定地址寫一個位元組:

extern uint8_t num;void AT24C02_Write_One_Byte(uint8_t addr,uint8_t data){   IIC_Start();	 IIC_Send_One_Byte(0xA0);		 if(IIC_Ack()==0)	 {	    IIC_Send_One_Byte(addr);		  if(IIC_Ack()==0)			{			   IIC_Send_One_Byte(data);				 if(IIC_Ack()==0)				 {				    IIC_End();					  USART1_Send(num);				 }			}	 }	 delay_ms(10);  //可以註釋掉}

小說明:這裡按照AT24C02的寫位元組操作來就好了

向AT24C02指定地址讀一個位元組:

uint8_t AT24C02_Read_One_Byte(uint8_t addr){	 uint8_t data;   IIC_Start();	 IIC_Send_One_Byte(0xA0);		 if(IIC_Ack()==0)	 {	    IIC_Send_One_Byte(addr);		  if(IIC_Ack()==0)			{			   IIC_Start();				 IIC_Send_One_Byte(0xA1);				 if(IIC_Ack()==0)				 {				     data=IIC_Read_One_Byte();					   IIC_End();				 }			}	 }	 delay_ms(10);  //可以註釋掉	 return data;}

小說明:這裡按照AT24C02的讀指定地址操作來就好了

其他函式:

#define SCL PBout(6)#define SDA PBout(7)#define Read_SDA PBin(7)//轉變SDA的輸入輸出模式#define SDA_IN()  {GPIOB->CRL&=0x0fffffff;GPIOB->CRL|=0x80000000;}#define SDA_OUT() {GPIOB->CRL&=0x0fffffff;GPIOB->CRL|=0x30000000;}void AT24C02_Init(void){	 GPIO_InitTypeDef GPIO_Struct;		 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  		 GPIO_Struct.GPIO_Mode=GPIO_Mode_Out_PP;   	 GPIO_Struct.GPIO_Pin=GPIO_Pin_6;           //IIC SCL	 GPIO_Struct.GPIO_Speed=GPIO_Speed_50MHz;	 GPIO_Init(GPIOB,&GPIO_Struct);		 GPIO_Struct.GPIO_Pin=GPIO_Pin_7;           //IIC SDA 	 GPIO_Init(GPIOB,&GPIO_Struct);		 GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); }int main(void){     AT24C02_Init();     delay_init();     key_init();	 uart_init(115200);	 num=AT24C02_Read_One_Byte(0);	 USART1_Send(num);	 while(1)	 {       if(key0==0)  //°´¼ü°´ÏÂ	   {	      delay_ms(10);          if(key0==0)		  {			   num+=2;				 AT24C02_Write_One_Byte(0,num);		  }while(!key0);								}				 	 }}	
4.實驗現象:

小說明:按一次key0,num的值加2,然後顯示在串列埠上位機上,一直連續重複出現的AC、AE、B2、B6我按過復位鍵之後顯示的,由此可以檢驗AT24C02可以斷電之後儲存原來的資料,程式碼磨問題啦~

8
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Makefile檔案入門