I2C總線通信


1、I2C總線簡介

  I2C總線是一種由PHILIPS公司開發的兩線串行通訊總線,用於連接為控制器及其外圍設備。

  I2C串行通訊總線由兩條線組成:

  時鍾線SCL。

  數據線SDA。

  時鍾線SCL用來同步數據的傳輸,數據線SDA用來傳輸或讀取數據。

  I2C總線通信設備之間常用連接方式如下:

   總線一般是指多個設備共用的信號線,比如上圖中的SCL總線和SDA總線,這兩個總線組成了I2C通訊總線,可以在這兩個總線上掛載多個設備。每一個連接到I2C總線上的設備都有一個獨立設備地址,主機通過設備地址識別響應的設備並與之通信。

  IC2總線一般需要使用上拉電阻接到電源端,這是因為I2C的設備的內部結構一般是開漏的,在平常狀態時為高組態,狀態時不確定的,通過上拉電阻將其上拉到高電平,使用這種方式也可以提高I2C總線的驅動能力。

  I2C總線有三種傳輸模式:

  標准模式:通訊速率為100Kbit/s。

  快速模式:通訊速率為400Kbit/s。

  高速模式:通訊速率為3.4Mbit/s。

  一般使用標准或快速這兩個模式進行通訊,這是應該目前大多數I2C設備並不支持高速模式,具體使用哪種模式可以根據I2C的設備的規格資料進行查詢。

2、I2C通訊協議

  I2C通訊協議包含起始信號、設備地址和讀寫信號、讀或寫數據信息、響應或非響應信號、結束信號。

  起始信號:

  起始信號由主機產生,主機通過在SCL為高電平的時候將SDA從輸出高電平變為輸出低電平來觸發一個I2C的起始信號,如下圖:

   主機在產生完起始信號之后,通過拉低SCL時鍾線使總線處於空閑狀態。主機產生起始信號的程序如下:

1 void I2C_START(void) 2 { 3     I2C_SDA(1); 4     I2C_SCL(1); 5     I2C_DELAY(2); 6     I2C_SDA(0); 7     I2C_DELAY(2); 8     I2C_SCL(0); 9 }

  程序首先將SDA和SCL輸出高電平,然后通過給SDA輸出低電平觸發起始信號,最后拉低SCL使總線處於空閑狀態(也有一種說法是鉗住總線)。

   停止信號:

  當不需要再進行I2C通訊時,主機產生一個停止信號來結束I2C通訊。主機通過在SCL為高電平時將SDA從輸出低電平變為輸出高電平來觸發一個I2C結束信號,如下圖:

   主機產生停止信號的程序如下:

1 void I2C_STOP(void) 2 { 3     I2C_SCL(0); 4     I2C_SDA(0); 5     I2C_DELAY(2); 6     I2C_SCL(1); 7     I2C_DELAY(2); 8     I2C_SDA(1); 9 }

  程序首先將SCL和SDA輸出低電平使總線處於空閑狀態,然后使SCL輸出高電平,最后再將SDA從輸出低電平變為輸出高電平,這樣不僅產生了一個停止信號,並且還釋放了總線(SCL和SDA都為高電平)。

數據采樣

  主機發送啟動信號之后,就可以向從機設備傳輸數據了。在傳輸數據時,每經過一個SCL時鍾周期傳輸一個bit的數據,如果是主機寫數據到從機,那么主機通過SDA在一個SCL時鍾周期內傳輸一個bit給從機;如果是主機從從機中讀取數據,那么主機通過SDA在一個SCL時鍾周期內讀取一個bit的數據。

  從I2C的起始信號和結束信號的介紹中可以看到,在SCL為高電平時,SDA電平的變換會觸發起始信號或停止信號,所以在數據的傳輸過程中,不能在SCL為高電平時改變SDA的電平狀態,也就是說SDA線上的數據應該在SCL為高電平的時候保持穩定。如下圖:

  I2C總線通訊時數據的采樣發生在SCL時鍾周期的高電平時間段,數據的改變發生在SCL時鍾周期的低電平時間段。也就是說數據是在SCL為高電平的時候傳輸或讀取,在SCL為低電平的時候根據傳輸的數據改變SDA的電平狀態。

  I2C總線在傳輸數據時,一般是先發送高位字節的數據,最后發送低位字節的數據。

  主機向從機發送一個字節的程序如下:

 1 uint8_t I2C_WRITE_BYTE(uint8_t dat)  2 {  3  uint8_t i;  4     
 5     I2C_SCL(0);  6     I2C_SDA(0);//釋放總線
 7     I2C_DELAY(1);  8     
 9     for(i = 0;i < 8;i ++) 10  { 11         if(dat & 0x80) 12  { 13             I2C_SDA(1); 14  } 15         else
16  { 17             I2C_SDA(0); 18  } 19         dat <<= 1; 20         I2C_DELAY(4); 21         I2C_SCL(1); 22         I2C_DELAY(8); 23         I2C_SCL(0); 24  } 25        
26     i = I2C_WAIT_ACK(); 27     
28     return i; 29 }

  發送程序第11~18行是根據要傳輸的數據dat來決定SDA的電平狀態,這是在SCL為低電平的時候處理的,在SCL為高電平的時候保持SDA的輸出狀態不變。第26行是等待從機反饋應答信號,在后面會講到應答信號的作用。第28行是返回的參數用來判斷是否收到應答信號,如果沒有收到應答信號則退出。

  主機從從機讀取一個字節的程序如下:

 1 uint8_t I2C_READ_BYTE(uint8_t ack)  2 {  3  uint8_t i;  4  uint8_t dat;  5     
 6     I2C_SCL(0);  7  I2C_SDA_IN();  8     I2C_SDA(0);  9     I2C_DELAY(1); 10     
11     for(i = 0;i < 8;i ++) 12  { 13         I2C_SCL(1); 14         I2C_DELAY(4); 15         dat <<= 1; 16         if(RD_I2C_SDA != 0) 17  { 18             dat |= 0x01; 19  } 20         I2C_SCL(0); 21         I2C_DELAY(4); 22  } 23     
24  I2C_SDA_OUT(); 25     
26     if(ack != 0) 27  { 28  I2C_ACK(); 29  } 30     else
31  { 32  I2C_NOT_ACK(); 33  } 34 
35     return dat; 36 }

  接收數據程序的第6~8行是將SDA數據線轉換為輸入狀態。第15~19行是在SCL為高電平的時候讀取SDA的電平狀態,即接收到一個bit的數據。在接收完8個字節的數據后,第24行將SDA數據線轉換為輸出狀態。第26~33行是根據函數輸入的參數來決定接收到一個字節的數據之后反饋給從機的是應答信號還是非應答信號。最后一行是返回接收到的數據。

  IC2設備地址和數據方向:

   I2C總線上的每個從機設備都有一個屬於自己的獨立的設備地址,主機在發送起始信號之后,需要發送從機的設備地址來查找需要通訊的從機設備,因為總線上的I2C從機設備的地址是獨立的,所以不會造成多個總線上的設備在傳輸數據時相互干擾。

  主機在發送設備地址的同時,還需要發送一個數據讀寫方向位,用來決定接下來的操作是向從機傳輸數據還是從從機讀取數據。數據讀寫方向位一般包含在設備地址中發送給從機設備,如下圖。

   bit7~bit1位是從機的設備地址,最低位bit0是數據讀寫方向位。

  當bit0位為0時,表示后面的操作是主機向從機發送數據。

  當bit0位為1時,表示后面的操作是主機從從機讀取數據。

  關於I2C設備的地址需要注意的是,一般I2C設備在規格資料說明里給出的設備地址都是不包含數據讀寫方向位的,需要自己根據設備地址進行轉換。比如說SHTC3溫濕度傳感器,在它的數據手冊中給出的設備地址是0x70,在發送設備地址和讀寫方向位時,並不是發送0x70(最低位為0表示寫)或0x71(最低位為1表示讀),而是要經過以下變換得出的,首先將0x70左移一個bit,得到0xE0,再用0xE0的最低位來表示數據的讀寫方向位,即在給SHTC3發送寫操作時應該發送0xE0(最低位為0表示寫);給SHTC3發送讀操作時應該發送0xE1(最低位為1表示讀)。

   響應信號:

   在I2C總線通訊中,在傳輸完一個字節之后,接收數據的設備(主機和從機都有可能接收數據)會在第9個SCL時鍾周期反饋一個應答信號或非應答信號給發送數據的設備,也就是說在I2C通訊中,數據的傳輸由8bit的數據和1bit的響應信號組成,即每一次傳輸會產生9個SCL時鍾周期信號。如下圖:

   響應信號包括應答信號(ACK)、非應答信號(NACK):

  應答信號(ACK):在第9個SCL時鍾周期,數據發送端將SDA的控制權釋放轉換為輸入狀態,數據接收端控制SDA的輸出,通過SDA數據線輸出一個低電平來產生一個應答信號,數據發送端根據SDA數據線的電平狀態來識別響應信號。

  非應答信號(NACK):在第9個SCL時鍾周期,數據發送端將SDA的控制權釋放轉換為輸入狀態,數據接收端控制SDA的輸出,通過SDA數據線輸出一個高電平來產生一個非應答信號,數據發送端根據SDA數據的電平狀態來識別響應信號。

  所以說應答信號和非應答信號是根據SDA數據在第9個SCL時鍾周期時的電平狀態來決定的,如果在第9個SCL時鍾周期時SDA為低電平,那么就是應答信號;如果SDA是高電平,那么就是非應答信號。

  應答信號和非應答信號的作用如下:

    當主機向從機發送數據時:

  在正常的I2C通訊中,主機每發送一個字節的數據給從機后,從機都會反饋一個應答信號給主機,主機可以通過從機反饋的響應信號來判斷與從機的通訊是否正常。如果發送完一個數據之后,從機反饋的是一個應答信號則說明數據傳輸成功,通訊正常;如果發送完一個數據之后,從機反饋的是一個非應道信號說明數據沒有傳輸成功,通訊異常,可能是從機正在進行其它操作或異常,這時候主機應當發送一個停止信號來停止發送數據。

  一般主機在傳輸完一個數據給從機后,都會等待從機反饋應答信號,如果超時沒有識別到應答信號,則說明收到的是非應答信號,程序如下:

 1 uint8_t I2C_WAIT_ACK(void)  2 {  3  uint8_t Wait_cnt;  4     
 5     I2C_SCL(0);  6  I2C_SDA_IN();  7     I2C_SDA(0);  8     I2C_DELAY(2);  9     I2C_SCL(1); 10     
11     Wait_cnt = 255; 12     
13     while(RD_I2C_SDA != 0) 14  { 15         Wait_cnt--; 16         if(Wait_cnt == 0) 17  { 18             break; 19  } 20  } 21 
22     I2C_SCL(0); 23  I2C_SDA_OUT(); 24     
25     if(Wait_cnt == 0) 26  { 27  I2C_STOP(); 28  } 29 
30     return Wait_cnt; 31 }

  以上是主機等待從機應答信號的程序,第5~9行首先在SCL為低電平的時候將SDA數據線切換為輸入狀態,然后將SCL輸出高電平產生第9個SCL時鍾周期。第13~20行是等待應答信號,如果SDA位低電平,說明識別到了應答信號,直接通過break退出循環;如果一直識別不到應答信號,當Wait_cnt的值被自減到0后退出循環。第22~23行是將SDA數據線切換為輸出狀態。第25~28行是根據Wait_cnt來決定是否要產生停止信號,如果Wait_cnt等於0說明收到的是非應答信號,這時候通訊已經異常,所以產生一個停止信號來結束通訊;如果Wait_cnt不等於0說明收到的應答信號,通訊正常。函數通過返回Wait_cnt的值來指示通訊是否正常。

  當主機從從機讀取數據時:

  當主機讀取從機的數據時,每讀取完一個字節的數據時,主機通過反饋一個應答信號或非應答信號給從機,用來通知從機是繼續傳輸數據還是停止傳輸數據。

  如果主機在讀取完一個從機發送的數據之后,產生一個應答信號給從機,那么從機會繼續發送下一個數據給主機。

  如果主機在讀取完一個從機發送的數據之后,產生一個非應答信號給從機,那么從機則會停止發送數據。

  也就是說,在主機讀取從機的過程中,從機時根據主機反饋的響應信號來決定是否要傳輸下一個數據的。

  在主機讀取從機數據時,主機產生的應答信號程序如下: 

1 void I2C_ACK(void) 2 { 3     I2C_SCL(0); 4     I2C_SDA(0);//發送低電平,響應從機
5     I2C_DELAY(2); 6     I2C_SCL(1); 7     I2C_DELAY(2); 8     I2C_SCL(0); 9 } 

  在SCL為高電平的時候,SDA輸出一個低電平來表示應答信號。

  在主機讀取從機數據時,主機產生的非應答信號程序如下:

1 void I2C_NOT_ACK(void) 2 { 3     I2C_SCL(0); 4     I2C_SDA(1);//發送低電平,響應從機
5     I2C_DELAY(2); 6     I2C_SCL(1); 7     I2C_DELAY(2); 8     I2C_SCL(0); 9 }

  在SCL為高電平的時候,SDA輸出一個高電平來表示非應答信號。

  等待應答信號的程序在主機發送一個字節的函數I2C_WRITE_BYTE中用到;主機產生應答信號或非應答信號的程序在主機讀取從機數據函數I2C_READ_BYTE中用到。

3、I2C通訊的讀寫過程

  主機寫數據到從機的通訊過程如下圖:

  圖中灰色部分是主機產生的,白色部分是從機產生的。

  主機產生一個起始信號S,然后發送一個字節的從機設備地址SLAVE ADDRESS和寫數據方向位R/W=0給從機,從機收到設備地址和R/W數據之后產生一個應答信號給主機,然后主機再發送一個字節的數據給從機,從機每收到一個數據都會產生一個應答信號給主機,當主機不需要再發送數據給從機或收到從機的非應答信號的時候產生一個停止信號P結束通訊。

  主機由從機中讀取數據的通訊過程如下圖:

  圖中灰色部分是主機產生的,白色部分是從機產生的。

  主機產生一個起始信號S,然后發送一個字節的從機設備地址SLAVE ADDRESS和讀數據方向位R/W=1給從機,從機收到設備地址和R/W數據之后產生一個應答信號給主機,然后主機開始讀取從機發送過來的數據,主機每收到一個數據都會產生一個應答信號或非應答信號給從機,如果希望從機繼續發送數據則反饋應答信號給從機;如果希望從機停止發送數據則反饋一個非應答信號給從機。當主機不需要再讀取從機的數據時產生一個停止信號P結束通訊。

  I2C通訊的復合格式如下圖:

   在I2C通訊過程中,可能需要先發送一個地址或命令給從機后再讀取從機的數據,比如讀取AT24C02的數據時,需要先發送需要讀取的地址,然后再讀取數據。主機需要先發送一個地址信息給從機,然后再讀取數據,由於發送地址信息是一個寫的過程,而讀取數據是一個讀的過程,這樣就組成了復合通訊格式。

  主機首先發送一個起始信號S,然后發送一個字節的從機設備地址SLAVE ADDRESS和寫數據方向位R/W=0給從機,等待從機反饋應答信號,再發送一個地址數據給從機后等待應答,然后再發送一個重復起始信號Sr,Sr信號之后發送一個字節的從機設備地址SLAVE ADDRESS和讀數據方向位R/W=1給從機,以讀取從機的數據,當讀取完數據之后,主機產生一個結束信號P來停止通訊。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM