本節主要講述IIC的使用及源碼實現。 IIC數據有效性:只有在SCL線為低時,SDA線才允許改變電平。 初始巨集定義如下: IIC時序圖(取自DS3231數據手冊): 先看前一部分(開始與結束): 從時序圖可以看出來,開始信號為SCL為高時,SDA由高變低;結束信號為SCL為低時,SDA由低變高。 發 ...
本節主要講述IIC的使用及源碼實現。
IIC數據有效性:只有在SCL線為低時,SDA線才允許改變電平。
初始巨集定義如下:
1 /**************巨集定義***************/ 2 #define DS_SCL BIT6 //DS_SCL = P1.6 3 #define DS_SDA BIT5 //DS_SDA = P1.5 4 #define DS_SCL_IN P1DIR &= ~DS_SCL 5 #define DS_SCL_OUT P1DIR |= DS_SCL 6 #define DS_SCL_L P1OUT &= ~DS_SCL 7 #define DS_SCL_H P1OUT |= DS_SCL 8 #define DS_SDA_IN P1DIR &= ~DS_SDA 9 #define DS_SDA_OUT P1DIR |= DS_SDA 10 #define DS_SDA_L P1OUT &= ~DS_SDA 11 #define DS_SDA_H P1OUT |= DS_SDA 12 #define DS_SDA_BIT P1IN & DS_SDA 13 #define DS_SDA_IN_H P1REN |= DS_SDA
IIC時序圖(取自DS3231數據手冊):
先看前一部分(開始與結束):
從時序圖可以看出來,開始信號為SCL為高時,SDA由高變低;結束信號為SCL為低時,SDA由低變高。
1 /******************************************* 2 函數名稱:DS_Start 3 功 能:I2C起始數據 4 參 數:無 5 返回值 :無 6 ********************************************/ 7 void DS_Start(void) 8 { 9 DS_SDA_OUT; //設置IO口方向 10 DS_SCL_OUT; 11 DS_SCL_H; //SCL、SDA先拉高 12 Delay_us(5); //delay應該有個最小時間要求 13 DS_SDA_H; 14 Delay_us(5); 15 DS_SDA_L; //SDA拉低表明開始 16 Delay_us(5); 17 DS_SCL_L; //SCL拉低,數據傳輸準備就緒 18 Delay_us(5); 19 }
1 /******************************************* 2 函數名稱:DS_Stop 3 功 能:I2C終止數據 4 參 數:無 5 返回值 :無 6 ********************************************/ 7 void DS_Stop(void) 8 { 9 DS_SDA_OUT; //設置IO口方向 10 DS_SCL_OUT; 11 DS_SCL_L; //先把SCL、SDA拉低,給結束信號做準備 12 Delay_us(5); 13 DS_SDA_L; 14 Delay_us(5); 15 DS_SCL_H; //SCL拉高 16 Delay_us(5); 17 DS_SDA_H; //當SCL為高,SDA由低變高為結束信號 18 Delay_us(1); 19 }
發送數據與讀數據:
由圖可知,開始信號後,應把SCL拉低,準備數據傳輸,SDA電平變化後,再拉高SCL,發送數據(最高位先發送),迴圈8次(一個位元組)。
讀取函數同理,只不過過程是反的,SDA是輸入,主機做接收端。
1 /******************************************* 2 函數名稱:DS_Send_Byte 3 功 能:I2C發送數據 4 參 數:data 5 返回值 :data 6 ********************************************/ 7 void DS_Send_Byte(u8 data) 8 { 9 DS_SDA_OUT; //設置IO口輸出方向 10 DS_SCL_OUT; 11 DS_SCL_L; //SCL為低時,允許SDA改變電平 12 unsigned char temp; 13 for(temp=0x80;temp!=0;temp>>=1) 14 { 15 if(temp&data==0) //高位先發 16 { 17 DS_SDA_L; 18 } 19 else 20 { 21 DS_SDA_H; 22 } 23 Delay_us(5); 24 DS_SCL_H; //SCL拉高 發送數據 25 Delay_us(5); 26 DS_SCL_L; 27 } 28 }
1 /******************************************* 2 函數名稱:DS_Read 3 功 能:I2C接收數據 4 參 數:data 5 返回值 :data 6 ********************************************/ 7 unsigned char DS_Read(void) 8 { 9 unsigned char temp; 10 unsigned char data; 11 12 DS_SDA_IN; //主機做接收,SDA線為輸入 13 DS_SDA_IN_H ; 14 for(temp=0x80;temp!=0;temp>>=1) 15 { 16 DS_SCL_H; //SCL拉高,保證接收數據時SDA不改變 17 Delay_us(5); 18 if(DS_SDA_BIT==1) 19 { 20 data|=temp; //高電平保留 21 } 22 else 23 { 24 data&=~temp; 25 } 26 DS_SCL_L; //SCL拉低,數據準備 27 } 28 return data; 29 }
主機應答信號與非應答信號:
主機接收完從機數據後,要發送應答或者非應答信號。
1 /******************************************* 2 函數名稱:DS_Ack 3 功 能:I2C發送應答信號 4 參 數:無 5 返回值 :無 6 ********************************************/ 7 void DS_Ack(void) 8 { 9 DS_SDA_OUT; //主機為接收端 發送應答信號 10 DS_SCL_L; //SCL拉低,允許SDA改變電平 11 DS_SDA_L; //SDA低電平為應答 12 DS_SCL_H; 13 Delay_us(5); 14 DS_SCL_L; 15 }
1 /******************************************* 2 函數名稱:DS_NAck 3 功 能:I2C發送非應答信號 4 參 數:無 5 返回值 :無 6 ********************************************/ 7 void DS_NAck(void) 8 { 9 DS_SDA_OUT; //此時,相當於主機在接收數據,是被動方 10 DS_SCL_L; //SCL拉低,允許SDA改變電平 11 DS_SDA_H; //SDA高電平為非應答 12 DS_SCL_H; 13 Delay_us(5); 14 DS_SCL_L; 15 }
主機接收從機應答信號:
從機在接收主機數據後,從機要發送一個應答信號,主機判斷此應答信號為應答信號或者非應答信號,做下一步處理。
/******************************************* 函數名稱:DS_Get_Ack 功 能:I2C接收應答信號 參 數:無 返回值 :ack ********************************************/ uchar DS_Get_Ack(void) { unsigned char ack; DS_SDA_IN; //釋放數據線,準備接收應答 DS_SDA_IN_H ; //SDA線輸入上拉 DS_SCL_H; //SCL線拉高 if(DS_SDA_BIT==1) { ack=0;//無應答信號 } else { ack=1;//有應答信號 } DS_SCL_L; Delay_us(5); }
下麵是DS3231傳輸時序圖:
由圖可知,在發送開始信號後,DS3231接收的第一個位元組的前7位是從機地址,即DS3231地址,第八位為讀/寫操作。
從DS3231的數據手冊中可以看出來,DS3231的地址為1101000,所以主機操作的寫地址為0XD1,讀地址為0XD0。(不要寫反)讀寄存器指針的方式是開始信號-寫器件地址-寫寄存器地址-開始信號-接收數據-停止。
1 /******************************************* 2 函數名稱:DS3231_WriteByte 3 功 能:I2C匯流排給DS3231發送單位元組 4 參 數:WriteAddr DataToWrite 5 返回值 :無 6 ********************************************/ 7 void DS3231_WriteByte(u8 WriteAddr,u8 DataToWrite) 8 { 9 DS_Start(); 10 DS_Send_Byte(0XD1); //發送器件地址 11 DS_Get_Ack(); 12 DS_Send_Byte(WriteAddr); //發送首地址 13 DS_Get_Ack(); 14 DS_Send_Byte(DataToWrite);//發送數據 15 DS_Get_Ack(); 16 DS_Stop(); 17 Delay_ms(1); 18 }
1 /******************************************* 2 函數名稱:DS3231_ReadByte 3 功 能:I2C匯流排從DS3231接收單位元組 4 參 數:ReadAddr DataToRead 5 返回值 :Data 6 ********************************************/ 7 uchar DS3231_ReadByte(u8 ReadAddr) 8 { 9 uchar R_Data=0; 10 DS_Start(); 11 DS_Send_Byte(0XD0);//讀地址 12 DS_Ack(); 13 DS_Send_Byte(ReadAddr); 14 DS_Ack(); 15 DS_Start(); 16 DS_Send_Byte(0XD1); 17 R_Data=DS_Read(); 18 Delay_us(5); 19 DS_NAck(); 20 DS_Stop(); 21 return R_Data; 22 }
1 /******************************************* 2 函數名稱:Readtime 3 功 能:讀取DS3231時間 4 參 數:R_tmpdate 5 返回值 :無 6 ********************************************/ 7 void Read_RTC() 8 { unsigned char rtc_address[6]={0x00,0x01,0x02,0x04,0x05,0x06}; 9 unsigned char R_tmpdate[6]; 10 unsigned char i,*p; 11 p=rtc_address; //地址傳遞 12 for(i=0;i<6;i++) //分6次讀取 秒分時日月年 13 { 14 R_tmpdate[i]=DS3231_ReadByte(*p); 15 p++; 16 } 17 }
1 void ModifyTime(uchar yea,uchar mon,uchar da,uchar hou,uchar min,uchar sec) 2 { 3 uchar temp=0; 4 5 DS3231_WriteByte(0x06,temp);//修改年 6 7 DS3231_WriteByte(0x05,temp);//修改月 8 9 DS3231_WriteByte(0x04,temp);//修改日 10 11 DS3231_WriteByte(0x02,temp);//修改時 12 13 DS3231_WriteByte(0x01,temp);//修改分 14 15 DS3231_WriteByte(0x00,temp);//修改秒 16 }