I2C詳解


1 I2C接口簡介

I2C全稱:Inter-Integrated Circuit,是一種同步、半雙工的通信總線。

同步:發送接收端要嚴格同步,一般有同步時鍾線。
半雙工:I2C只有一條數據線,所以master發數據與收數據不能同時進行。

I2C通信速率:

模式 速率
標准模式 100 kbps
快速模式 400 kbps
高速模式 3.4 Mbps

I2C誕生的背景:
最初的嵌入系統是使用內存映射(memory-mapped I/O)的方式來互連微控制器和外圍設備的。要實現內存映射,設備必須並行連入微控制器的數據線和地址線,也就意味着:如果要連接一款新的外圍設備,需在設計芯片時候確定好。所以很不靈活並且成本高。

1982年,Philips實驗室開發了I2C,方便CPU與外設之間通信。

1.1 I2C原理簡介

我理解的是:I2C設計時的理念是:信號線盡量少並且速率要盡量高
信號線少,可以減少引腳占用,這對早期的芯片(引腳很少)的很重要。
當然,如果單純說減少信號線,1-wire總線只使用1根線通信(比如DS18B20、DHT11等都是使用這種協議),但是1-wire總線是異步通信,所以1-wire總線速率不可能太高(1-wire總線傳輸速率一般為16.3Kbit/s,最大可達142 Kbit/s,通常情況下采用100Kbit/s以下的速率傳輸數據)。

標准的I2C需要兩根信號線:

SCL(Serial Clock):時鍾線,時鍾都是有master提供的
SDA(Serial Data):雙向數據線,發數據或者收數據(收發不能同時)

I2C多master多slave示意圖:
I2C多master多slave示意圖
圖中是2個master+2個slave的示意,同一時刻只有一個master與一個slave通信。

若想實現這個效果:
1 多個master-slave 時鍾、數據線連在一起,需要實現信號的“線與”邏輯(所以SDA、SCL 被設計為漏極開路結構,外加上拉電阻實現“線與”)
2 需要實現 “時鍾同步”和“總線仲裁”,引腳在輸出信號的同時還能對引腳上的電平進行檢測,檢測是否與剛才輸出一致,為 “時鍾同步”和“總線仲裁”提供硬件基礎。
3 I2C在讀寫時需要帶上設備地址,這樣不使用多的信號線就可指定特定的slave(而SPI通信需要多的片選線)

1.2 I2C的讀寫

I2C的寫過程:
I2C寫過程

  1. Master發起START
  2. Master發送I2C addr(7bit)和W(寫)操作0(1bit),等待ACK
  3. Slave發送ACK
  4. Master發送reg addr(8bit),等待ACK
  5. Slave發送ACK
  6. Master發送data(8bit),即要寫入寄存器中的數據,等待ACK
  7. Slave發送ACK
    第6步和第7步可以重復多次,即順序寫多個寄存器
  8. Master發起STOP結束傳輸

I2C的讀過程:
I2C讀過程

  1. Master發起START
  2. Master發送I2C addr(7bit)和r(讀)操作1(1bit),等待ACK
  3. Slave發送ACK
  4. Slave發送data(8bit),即寄存器里的值
  5. Master發送ACK
    第7步和第8步可以重復多次,即順序讀多個寄存器
  6. 當master接收完想要的數據后,由Master發送NACK,告知slave停止發送數據
  7. Master發送STOP結束傳輸

但實際上,I2C讀過程之前需要知道讀取的寄存器地址。所以讀過程之前需要master寫寄存器的操作,告知slave后面讀取寄存器的起始地址。
完整I2C讀過程 = 寫過程 + 讀過程,其中涉及到讀寫方向的轉變。
完整I2C讀過程

1.3 I2C的幾個狀態

上節幾個圖描述了讀寫數據流,圖中的幾個狀態需要與實際信號一一對應起來。
1 總線空閑狀態:
SDA和SCL同時處於高電平時,規定為總線的空閑狀態。此時各個器件的輸出級場效應管均處在截止狀態,即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。
2 總線START:
SCL為高電平時,SDA由高電平向低電平跳變,開始傳送數據。
3 總線STOP:
SCL為高電平時,SDA由低電平向高電平跳變,結束傳送數據。
4 總線Restart:
SCL為高電平時,SDA由高電平向低電平跳變,本質上也是START信號,用在完整I2C讀過程中的讀階段,在首次發送停止信號之前,master通過發送Restart信號,可以轉換與當前slave的通信模式(從寫模式到讀模式),或是切換到與另一個slave通信。
5 數據階段:
在IIC總線上傳送的每一位數據都有一個時鍾脈沖相對應(或同步控制),即在SCL串行時鍾的配合下,在SDA上逐位地串行傳送每一位數據。進行數據傳送時,在SCL呈現高電平期間,SDA上的電平必須保持穩定。只有在SCL為低電平期間,才允許SDA上的電平改變狀態。簡單的說就是,數據在SCL下降會被采樣,所以SDA需要在SCL為高電平時保持穩定。
6 ACK與NACK信號:
IIC總線上的所有數據都是以8位字節傳送的,發送器每發送一個字節,就在第9個時鍾脈沖期間釋放數據線,由接收器反饋一個應答信號。應答信號為低電平時,規定為有效應答位(ACK簡稱應答位),表示接收器已經成功地接收了該字節;應答信號為高電平時,規定為非應答位(NACK),一般表示接收器接收該字節沒有成功。

這段話再說細一點:
在寫階段,master寫了一字節數據,在第9個時鍾脈沖期間釋放數據線,由slave反饋應答信號,ACK(低)表示數據成功接收,NACK(高)表示該字節沒有接收成功;
在讀階段,master向slave收數據,slave寫了一字節數據,在第9個時鍾脈沖期間釋放數據線,由master反饋應答信號,ACK(低)表示數據成功接收,NACK(高)表示該字節沒有接收成功。

還有一種特殊情況:當master決定不再接收數據時,應向slave發送NACK信號,高速slave不再發送。
以下情況會導致出現NACK位:

  1. 接收器沒有發送機響應的地址,接收端沒有任何ACK發送給發送器
  2. 由於接收器正在忙碌處理實時程序導致接無法接收或者發送
    傳輸過程中,接收機器別不了發送機的數據或命令
  3. 接收器無法接收
  4. master接收完成讀取數據后,要發送NACK結束告知slave。
    當master接收到slave的NACK信號后,可以STOP這次傳輸,也可以重新START。
    所以:NACK並不只是表示字節沒有成功接收,也可以表示master告訴slave不再需要發送數據

1.4 I2C通信中的幾個問題

1 總線沖突和總線仲裁

I2C總線上的仲裁分兩部分:SCL線的同步和SDA線的仲裁。
SDA仲裁:
這段話摘自《IIC總線解析》,見參考。

假如在某IIC總線系統中存在兩個主器件節點,分別記為主器件1和主器件2,其輸出數據分別為DATA1和DATA2,它們都有控制總線的能力,這就存在着發生總線沖突(即寫沖突)的可能性。
假設在某一瞬間兩者相繼向總線發出了啟動信號,鑒於:I2C總線的“線與”特性,使得在數據線SDA上得到的信號波形是DATA1和DATA2兩者相與的結果
在總線被啟動后,主器件1企圖發送數據“101……”,主器件2企圖發送數據“100……”。
兩個主器件在每次發出一個數據位的同時都要對自己輸出端的信號電平進行抽檢,只要抽檢的結果與它們自己預期的電平相符,就會繼續占用總線,總線控制權也就得不到裁定結果。
主器件1的第3位期望發送“1”,也就是在第3個時鍾周期內送出高電平。在該時鍾周期的高電平期間,主器件1進行例行抽檢時,結果檢測到一個不相匹配的電平“0”,這時主器件1只好決定放棄總線控制權(主器件1切為slave模式,不再產生時鍾,等主器件2完成傳輸,總線恢復空閑狀態時,又重新啟動主器件1的傳輸);因此,主器件2就成了總線的惟一主宰者,總線控制權也就最終得出了裁定結果,從而實現了總線仲裁的功能。
從以上總線仲裁的完成過程可以得出:仲裁過程主器件1和主器件2都不會丟失數據;各個主器件沒有優先級別之分,總線控制權是隨機裁定的。
系統實際上遵循的是“低電平優先”的仲裁原則,將總線判給在數據線上先發送低電平的主器件,而其他發送高電平的主器件將失去總線控制權。
SCL時鍾同步:
如果在某一I2C總線系統中存在兩個主器件節點,分別記為主器件1和主器件2,其時鍾輸出端分別為CLK1和CLK2,它們都有控制總線的能力。
假設在某一期間兩者相繼向SCL線發出了波形不同的時鍾脈沖序列CLK1和CLK2,在總線控制權還沒有裁定之前這種現象是可能出現的。
鑒於IIC總線的“線與”特性,使得時鍾線SCL上得到的時鍾信號波形,既不像主器件1所期望的CLK1,也不像主器件2所期望的CLK2,而是兩者進行邏輯與的結果(注意數據在SCL為高電平時有效,所以為SCL為低電平並不影響SDA仲裁)。
CLKI和CLK2的合成波形作為共同的同步時鍾信號,一旦總線控制權裁定給某一主器件,則總線時鍾信號將會只由該主器件產生。

2 I2C總線死鎖

正常I2C通信肯定不會出現死鎖的,死鎖即是I2C在某一特殊時刻出現了異常。
死鎖的表現:SCL一直為高,SDA一直為低

場景1:在寫階段的ACK階段,SDA被slave拉低作為ACK,master突然復位了,SCL也被復位為高電平,這樣:master檢查到SDA為低,會認為I2C總線被占用,會一直等待SCL和SDA信號變為高電平,而slave不知道master異常復位,還在傻傻等待SCL拉低,然后結束ACK信號,這樣master與slave互相等待,造成死鎖。
場景2:當I2C進行讀操作時,slave應答后輸出數據,如果在這個時刻master異常復位而此時slave輸出的數據位正好為0,也會導致I2C總線進入死鎖狀態。
總的說來:SDA為低時,期待一個SCL的下降沿驅動,但是master由於異常復位導致SCL一直為高,這樣就產生了死鎖。

解決死鎖的方法:
最好用模擬I2C實現,則不會死鎖

將從機的電源設計為可控,當發生總線死鎖的時將從機復位
可以在從機的程序中加入監測功能,如果總線長時間被拉低則釋放對總線的控制
在主機中增加I2C總線恢復程序。每次主機復位后,如果檢測到SDA被拉低,則控制SCL產生<=9個時鍾脈沖(針對8位數據的情況),每發送一個時鍾脈沖就檢測SDA是否被釋放,如果SDA已經被釋放就再模擬產生一個停止信號,這樣從機就可以完成被掛起的讀寫操作,從死鎖狀態中恢復過來。這種方法有一定的局限性,因為大部分主機的I2C模塊由內置的硬件電路來實現,軟件並不能夠直接控制SCL信號模擬產生需要的時鍾脈沖。

1.5 I2C與SPI對比

  1. I2C是單雙工(只有SDA一根數據線),標准SPI是全雙工(有MOSI和MISO兩根數據線)。
  2. SPI沒有指定的流控制,沒有應答機制確認是否收到數據。
  3. I2C時序很多方面規定死了(如:只支持8bit數據,只支持優先發送MSB,在SCL為高電平數據有效等),而SPI則更“靈活”。

2 軟件代碼

2.1 GPIO模擬I2C



2.2 Linux I2C

3 I2C擴展

問題:兩個嵌入式芯片怎么通過I2C通信?
如果被控器需要延遲下一個數據字節開始傳送的時間,則可以通過把時鍾線SCL電平拉低並且保持,使主控器進入等待狀態。一旦被控器釋放時鍾線,數據傳輸就得以繼續下去,這樣就使得被控器得到足夠時間轉移已經收到的數據字節,或者准備好即將發送的數據字節。

帶有CPU的被控器在對收到的地址字節做出應答之后,需要一定的時間去執行中斷服務子程序,來分析或比較地址碼,其間就把SCL線鉗位在低電平上,直到處理妥當后才釋放SCL線,進而使主控器繼續后續數據字節的發送。(需要從機能夠拉住SCL,用gpio模擬較方便)

4 參考

  1. RVMCU課堂「14」: 手把手教你玩轉RVSTAR—I?C總線通信篇
  2. I2C詳解
  3. 【接口時序】6、IIC總線的原理與Verilog實現
  4. IIC總線解析
  5. 解讀I2C協議和讀寫流程


免責聲明!

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



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