[導讀]本文來梳理一下 I2C 總線的一些要點。這個題目有點大,本文對於 I2C 其實很多地方也沒整清楚,只為了與前文形成系列,如果大家有補充歡迎留言。說了些閒話,進入正題吧。
自 2006 年 10 月 10 日起,實施 I2C 協議不需要任何許可費用。但是,獲得恩智浦分配的 I2C 從設備地址則需要付費。一些競爭者如西門子(後來的英飛凌技術,現在的英特爾移動通信)、NEC、德州儀器 TI、意法半導體(以前的 SGS-Thomson)、摩託羅拉(後來的飛思卡爾,現在與 NXP 合併)、Nordic 半導體和 Intersil,自 20 世紀 90 年代中期以來已經陸續發布了很多兼容的 I2C 標準的晶片。
說到總線,其種類很多,但其目的基本一致,就是一個用於傳遞信息的公共幹線。如晶片內部地址總線、數據總線其對象可能為CPU核與各外設(RAM/ROM/外設控制器電路等);晶片級總線I2C,SPI等,設備級總線如RS422/RS485/HART/CAN/Ethernet/Fieldbus.
自 Version 4 之後 I2C 支持下面幾種模式:
standard-mode(Sm): ≤100 Kbit/SFast-Mode(Fm):≤400 Kbit/SFast-mode Plus(Fm+):≤1Mbit/SHigh-speed mode (Hs-mode): ≤ 3.4 Mbit/sUltra Fast-mode (UFm): ≤ 5 Mbit/sI2C優勢I2C 標準能帶來些啥好處呢?
簡單的 2 線串行 I2C 總線最小化互連,節省 PCB 布板走線空間;I2C 總線的多主控能力允許終端用戶設備通過外部連接到裝配線進行快速測試和校準。標準支持廣泛,大量無鉛封裝 I2C 總線兼容集成晶片進一步降低了空間需求。其他子集系統管理總線(SMBus),由 Intel 在 1995 年定義,是 I2C 的一個子集,定義了更嚴格的用法。SMBus 的一個目的是促進健壯性和互操作性。因此,現代 I2C 系統合併了來自 SMBus 的一些策略和規則,有時同時支持 I2C 和 SMBus,只需要通過命令或輸出引腳使用最小限度的重新配置。
TWI(雙線接口)或 TWSI(雙線串行接口),本質上是在 Atmel 和其他供應商的各種系統晶片處理器上實現的同一總線。
I2C 拓撲結構從概念上,I2C 總線有兩根線 SDA/SCL 就可以連一堆晶片,實現很多的應用。連接拓撲極簡!
比如這樣一個系統:
接下來看看各模式下,連接拓撲圖:
標準速度/快速模式:
高速模式拓撲:
混速模式拓撲:
工作原理如果使用 IO 口模擬 I2C 總線,或者使用 FPGA 實現 I2C 接口,深刻理解 I2C 時序波形無疑是重點中的重點!即使使用內置的 I2C 控制器外設實現一個 I2C 總線編程,在調試底層時或者踩坑過程中,深入理解時序波形原理,也是非常必要的!
時序圖I2C 的時序圖如下:
START 事件:可以聯想一下 UART 的起始位,這個用於通知 I2C 通信的發起。用一句話描述就是在 SCL 常高時,採集到 SDA 高到低跳變,這就是啟動事件。
數據有效性:SDA 線上的數據必須在時鐘的高周期保持穩定。數據線的高或低狀態只能在 SCL 線上的時鐘信號低時改變。每個傳輸的數據位產生一個時鐘脈衝。
ACK:確認信號 ACK 的定義如下:發送器在 ACK 時鐘脈衝期間釋放 SDA 線,因此接收器可以將 SDA 線拉低,並在此時鐘脈衝的高電平期間保持穩定的低電平(見上圖)。須嚴格遵循電氣的建立保持時間,使用時需要用示波器去嚴格測試信號是否能滿足這些參數。
NACK:當在第九個時鐘脈衝期間 SDA 保持高電平時,這被定義為「NACK」信號。之後主機可以產生停止條件以中止傳輸,或產生重複的開始條件以開始新的傳輸。導致 NACK 產生的條件有五個:
總線上沒有報文中所包含地址的接收器,因此沒有設備響應應答。接收器無法執行接收或發送操作,比如它正在執行某些實時功能,並且尚未準備好與主機進行通信。在傳輸過程中,接收器收到應用協議不理解的數據或命令。在傳輸期間,接收器無法再接收更多有效數據字節。比如程序或者晶片內置緩衝區已經滿了主接收器用 NACK 通知從發送器結束傳輸。這是何意呢?比如主設備已經接受到足夠多的數據,不希望從設備發送更多的數據時,就可以 NACK 從設備,這樣從設備就會停止發送時鐘同步與仲裁時鐘同步:兩個主機可以同時開始在空閒總線上進行傳輸,並且必須有一種方法來確定控制總線並完成其傳輸的方法。這是通過時鐘同步和仲裁完成的。在單主機系統中,不需要時鐘同步和仲裁。
時鐘同步是通過 I2C 接口中 SCL 線的線與實現的。啥意思呢?
這裡的幾句話需要劃重點去理解,這就是 I2C 總線的核心之核心工作原理:線與!
當 SCL 從高到低的過渡時,總線上的主機開始計數其低電平時間,且一旦主機時鐘變為低,它就會將 SCL 保持在該狀態,直到變為高狀態為止。但是,如果另一個主機時鐘仍在其低周期內,則此時鐘從低到高的轉變不會改變 SCL 線的狀態。所以,SCL 線由主機以最長的低電平周期保持為低電平。低電平周期較短的主機在此期間進入高電平等待狀態。上面的話不好理解?看看線與的本質是與,啥叫與呢?比如 C=A&B,只要其中一個變量 A/B 為低,那麼 C 就必然為 0,比如下圖中,即便 CLK1(為其中一個主機)為高了,但奈何另一主機的 CLK2 任然為低啊?所以 SCL 線上測出來就是低。當所有相關的主機都計數完低電平周期後,時鐘線被釋放並變為高電平。這樣,主時鐘和 SCL 線的狀態之間就沒有區別,所有主時鐘都開始計數其高電平周期。第一個完成其高電平周期的主機將 SCL 線再次拉低。仲裁:仲裁與同步類似,僅在系統中使用多個主機時才會涉及到,從站不參與仲裁過程。首先要理解一下仲裁是幹啥的?所謂仲裁就是在多主機模式下,哪一個主機能獲取介質的訪問權限,獲得權限的主機才可以傳輸 I2C 通信報文。只有在總線空閒時,主機才可以開始傳輸。兩個主機可以在 START 的最小保持時間內產生 START 條件這種情況會導致總線上出現有效的 START 條件。然後需要仲裁以確定哪個主機將完成其傳輸。
仲裁是一位一位地進行。節點發送 1 個位後,回讀比較總線上所呈現的數據與自己發送的是否一致。是,繼續發送;否則,退出競爭。SDA 線的仲裁可以保證 I2C 總線系統在多個主節點同時企圖控制總線時通信正常進行並且數據不丟失。總線系統通過仲裁只允許一個主節點可以繼續佔據總線
上圖顯示了兩個主機的仲裁程序。實際使用中連接到總線的主機數量可能會更多。當主機產生的 DATA1 的內部數據電平與 SDA 線上的實際電平之間存在差異時,DATA1 輸出將關閉。從而主機 1 退出競爭,沒有獲得總線的控制權。
時鐘延長:時鐘延長通過將 SCL 線保持為低電平來暫停事務。直到再次釋放高電平,事務才能繼續。時鐘延長是可選的,實際上,大多數從設備不包括 SCL 驅動能力,因此它們無法延長時鐘。
為啥要設計這樣一個機制呢?個人理解是為了增強系統的健壯性而設計的:
在字節傳輸級別,設備可能能夠以快速速率接收數據字節,但需要更多時間來存儲接收到的字節或準備另一個要發送的字節。此時,從機可以在接收和確認字節後將 SCL 線保持為 LOW,以強制主機進入等待狀態,直到從機為握手過程中的下一個字節傳輸做好準備。
在位級別上,諸如微控制器之類的設備可以通過延長每個時鐘的 LOW 周期來減慢總線時鐘。任何主機的速度都將根據該設備的內部工作速率進行調整。
地址及 R/W 位:地址及 R/W:設備地址讀寫位描述0000 0000廣播地址0000 0001啟動字節0000 001XCBUS 地址0000 010X預留給不同的總線格式0000 011X預留未來擴展使用0000 1XX1Hs-mode 主代碼1111 1XX1設備 ID1111 0XXX10 位從地址通用廣播地址通用廣播地址用於同時尋址連接到 I2C 總線的所有設備。但是,如果設備不需要處理廣播數據,則可以通過不發出 ACK 來忽略該地址。如果某設備需要來自通用廣播地址的數據,它將發送 ACK 給該地址並充當從接收器。主機實際上不知道有一個或多個設備響應時確認了廣播數據(不確定有多少個 ACK)。每個能夠處理此數據的從機接收器都會確認第二個字節和隨後的字節。無法處理這些字節的從機將不應答從而忽略。同樣,如果一個或多個從機應答,則主機不會看到未確認的消息。通用廣播地址的含義總是在第二個字節中指定,如下圖:
1.當 B 為 0 時,第 2 字節定義如下:
0000 0110(06h):設備將復位以及設置地址的可編程部分。接收到這個 2 字節命令後,所有支持響應通用廣播地址的設備將復位,並將其地址的可編程部分改寫保存。須採取預防措施以確保設備在施加電源電壓後不會拉低 SDA 或 SCL 線,因為這些低電平會阻塞總線。0000 0100 (04h):收到該命令後設備將通過硬體設置地址的可編程部分。(Write programmable part of slave address by hardware).0000 0000 (00h): 不允許將此代碼用作第二個字節.對於 06h/04h 這兩個命令,有些不太好理解。復位比較好理解。對於設置設備地址的可編程部分可能很多沒有遇到過的朋友則不太好理解。這裡來一個實際晶片的例子,以 Microchip 的 MCP3423/MCP3424 為例進行描述一下,MCP3423/MCP3424 是一顆多通道 ADC 晶片,其晶片引腳如下:
當接收到通用廣播訪問且第 2 字節為 06h 命令後,晶片做兩件事情:
同時鎖住 Adr1/Adr0 的電平作為地址,這兩位地址為晶片地址的可編程部分。當然對於不同的晶片,具體如何實現通用廣播地址的處理則各有不同,只需要認真閱讀晶片的手冊就能獲取相應信息。這裡僅僅就通用廣播地址舉個慄子,方便理解。老實說這個功能好像不太常見,具體有什麼用?我反正沒這麼用過,感覺這功能有點蛋疼(直接用電阻配置好不更省事?)。如果有好的應用實例場景,歡迎留言交流。
2.當 B 為「 1」時,則該 2 字節序列為「硬體通用呼叫」。該報文由 I2C 主設備(例如鍵盤掃描器)發送,可以對其進行編程以發送所需的從地址。由於 I2C 主設備事先不知道該消息必須傳輸到哪個從設備,故利用通用廣播地址及通用呼叫命令並將自身的地址放在高 7 位,從而標識總線上發送通用硬體呼叫的設備 ID。該地址由連接到總線的智能設備識別(比如該智能設備是一個單片機系統),然後該智能設備從硬體主機接收信息。如果硬體主機也可以充當從機,則從機地址與主機地址相同。
所以標準中定義這個功能,可以做些自適應應用,只需要制定出相應協議就可以完成比較靈活的多主通信應用協議。
軟復位如上面描述,當通用廣播地址後面跟 06h 字節,就可以使從設備軟復位。但這個功能並非所有晶片都支持,具體使用的時候需要仔細閱讀晶片手冊是否支持該功能。
須採取預防措施以確保設備在施加電源電壓後不會拉低 SDA 或 SCL 線,因為這些低電平會阻塞總線。
起始 START 字節單片機/DSP 可以用兩種方法連接到 I2C 總線:
有的單片機/DSP 具有片上 I2C 硬體外設,這就可以直接使用。如果沒有或者被其他功能佔用,則可以使用 GPIO 去模擬 I2C 總線時序。用這個方式去實現,則比較消耗 CPU 時間,比如在一個多單片機用 I2C 總線連一起的系統,其中一個單片機 I2C 是用 IO 口模擬的,則快速的硬體設備與依賴軟體輪詢的相對較慢的單片機之間存在速度差異。這個不難想像,因為依靠輪詢則不是硬實時,同時單片機肯定還有其他事物需要處理,那麼檢測 START 條件信號就有可能丟失,導致系統不健壯。那麼 I2C 標準已然考慮這種需求了。
這就是起始字節需要解決的需求,前面介紹的就是起始字節設計的背景。那麼起始字節究竟是怎樣的呢?
START 事件(英文叫 condition,我這樣叫成一個事件有一點軟體原語抽象的意思)在需要訪問總線的主機發送了 START 事件之後,發送 START 字節(0000 0001)。另一個單片機/DSP 可以以低採樣率對 SDA 線進行採樣,直到檢測到 START 字節中的七個零之一為止。在 SDA 線上檢測到此 LOW 電平後,微控制器可以切換到更高的採樣率,以找到重複的 START 事件,然後將其用於同步。
在異常情況下,如果時鐘 SDA 被拉為 LOW 了,則有哪些辦法可以對總線復位呢?
則優選的做法是如 I2C 設備具有硬體復位輸入,則使用硬體復位信號來復位總線。如果 I2C 設備沒有硬體復位輸入信號,如果硬體設計可以考慮用 MOSFET 控制設備電源,重新通電以激活強制性的內部上電復位(POR)電路。還有一種做法是主機發送 9 個時鐘 SCL 脈衝。使總線保持低電平的設備應在這九個時鐘內的某個時間釋放它。這個具體怎麼做呢?主設備初始化 I2C 總線時,可以冗餘加 9 個 SCL 脈衝以復位 I2C 總線,或者檢測到 SDA 長時間被拉低後,可以以控制 IO 高低翻轉的方式控制 SCL 產生 9 個脈衝//可能需要先關閉I2C控制器,如果是使用I2C控制器外設實現的//I2C_SCL根據不同硬體進行移植,delay#define I2C_SCL P10void soft_rst_i2c(void){ I2C_SCL = 1; for(int i=0;i<9;i++) { I2C_SCL = 0; delay(xx); I2C_SCL = 1; delay(xx); }}前面兩種方法是更健壯的方案,如果硬體不支持,可以考慮後一種方法,但後一種方法的前提是拉死 SCL 的設備需要支持這種功能,如果兩端都是自定義開發的則比較靈活了。
總線鎖死,是 I2C 總線系統常踩的坑,有哪些原因會導致鎖死呢?程序不健壯,I2C 的波形不滿足 I2C 規格書要求,或者在外加幹擾情況下導致波形被幹擾。有經驗的同學可能會遇到設備平時工作的好好的,但是做 EMC 測試,常常設備會莫名死機,如果你的設備有 I2C 總線,請記得檢查 I2C 是否被 EMC 幹擾乾死了!
設備 ID設備 ID 欄位是一個可選的 3 字節只讀(24 位)字,提供以下信息:
12 位用於表示製造商名稱,每個製造商唯一(例如,NXP)9 位由製造商分配的晶片標識(例如,PCA9698)3 位表示晶片版本,由製造商分配(例如 RevX)這個對於設計軟體有什麼可以利用的信息呢?比如一個系統可兼容不同廠家的基於 I2C 協議的傳感器,利用這個欄位就可以做設備信息管理。至於怎麼讀取,不同晶片或有不同。
接口電路簡介前面拓撲圖中採用 open-drain 開漏結構。I2C 有的還用集電極開路輸出結構,究其原因是內部是三極體的集電極開路。如下
Ultra Fast-mode在 Rev 4 中還出現了 Ultra Fast-mode,該模式使用 push–pull 推挽定義 I2C 內部硬體接口電路(我把它叫推拉),這個又長什麼樣呢?
這種推挽接口是用在 Ultra Fast-mode(UFm)模式,為啥不繼續採用集電極開路門/漏極開路門呢?因為這兩種硬體已然無法滿足如此高速的通訊波形要求了,推輓輸出可以實現更為快速波形前沿特性以驅動總線電容負載。
對於 Ultra Fast-mode 模式其他如時序波形,報文定義基本一致,這裡不做贅述了。需要提醒的是設備 ID 在該模式下不支持!
容性負載為什麼要特別討論一下總線的容性負載特徵呢?想像中的理想通信波形:
由於容性負載以及充放電常數特性,實際中卻可能是這個樣:
如果實際總線中電阻選取過大,或者容性負載過大(設備節點過大或者布線不合理),也即RC常數過大,甚至可能是這個德行:
那麼參數選取合適時,波形則可能是這樣的:
所以就其本質而言,就是由於驅動接口電路的RC參數影響了波形的時序參數:
實際應用中,一方面電阻需要選取足夠大以降低不必要的電流消耗,另一方面電阻又需要選擇足夠小以滿足對應傳輸速度的波形時序要求。故需要在這一對矛盾體中尋求一個折中平衡!實際項目中先用示波器測測I2C波形非常必要,代碼對了總線可未必如願工作。做底層開發,儘量先硬後軟~~
I2C 總線標準從電氣特性界定了容性負載特徵:
Fast-mode:連接到總線的外部上拉設備必須經過調整,以適應快速模式 I2C 總線較短的最大允許上升時間。對於 200 pF 以內的等效總線負載,每條總線的上拉設備可以是一個電阻。對於 200 pF 至 400 pF 之間的總線負載,上拉設備可以是電流源(最大 3 mA)或開關電阻電路。Fast-mode Plus (Fm+):該模式下設備中的驅動接口電路驅動能力比較強大,可以滿足 Fast-mode Plus 時序規範,並具有與標準模式部件相同的 400 pF 負載。為了與標準模式向後兼容,它們還可以承受標準模式設備的 1μs 上升時間。在僅存在 Fast-mode Plus 部件的應用中,強驅動接口和對緩慢的上升和下降時間的容忍度允許使用較大的總線電容,只要軟體設置好或硬體 IC 實現好,Fast-mode Plus 的最小 LOW 時間和最小 HIGH 時間即可滿足所有要求,並且下降時間和上升時間不超過標準模式的 300ns 上升沿時間和 1μs 下降沿時間規格。可以將總線速度與負載電容進行折衷,總線電容可增加大約十倍。Hs-mode: 高速模式(Hs-mode)器件在 I2C 總線傳輸速度方面實現了飛躍。高速模式設備可以實現高達 3.4Mbit/s 的比特率傳輸速度,但仍然向下兼容,與快速模式增強版、快速模式或標準模式(F/S)設備完全兼容以進行雙向通信總線系統。除了在 Hs 模式傳輸期間不執行仲裁和時鐘同步外,與 F/S 模式系統保持相同的串行總線協議和數據格式。那麼如此高速是如何做到的呢?這裡將個人認為與應用相關的要點翻譯總結下:Hs 模式主設備具有用於 SDAH 信號的漏極開路輸出緩衝器,以及 SCLH 輸出上的漏極開路下拉電路和電流源上拉電路的組合。該電流源電路縮短了 SCLH 信號的上升時間。任何時候僅在 Hs 模式下,僅啟用一個主機的電流源。在多主機系統的 Hs 模式傳輸期間,不執行仲裁以及時鐘同步以提高位處理能力。仲裁過程始終在先前的 F/S 模式下的主代碼傳輸之後完成。Hs 模式主設備生成串行時鐘信號,其佔空比為 50%以減輕建立和保持時間的時序要求。這個項目中可利用示波器檢查波形。具體設計時,可參考規格書電氣特性參數規定以及所選晶片的手冊。編程策略硬體 I2C 控制器:要實現 I2C 總線,如果使用單片機/DSP/SOC 內置了 I2C 控制器,就其本質就是抽象了 I2C 總線的各種事件以寄存器進行控制,最為常見的方式就是將總線事件抽象為異步中斷事件。以 STM32 為例:編程時,比較好的方式就是處理相應的中斷事件。利用內置 I2C 控制器是優選方案。
IO 模擬,如果系統中不存在 I2C 控制器,可利用 IO 口進行模擬,對於實現多設備以及高速模式系統則不推薦這樣做。但在一些 PCB 尺寸受限或者成本受限、單片機引腳很少的系統中還是比較有實用價值的。其編程只需要對照 I2C 時序進行操作即可,難度較小。在實際項目中,需要特別注意 I2C 的上升沿、下降沿波形時間參數是否滿足設計速率要求,可通過配置寄存器以及調整驅動上拉電阻進行調整。對於高速模式則可能需要用電流源進行驅動。另外需要注意的是,I2C 總線鎖死情況處理。
總結一下I2C 總線是一個比較複雜的晶片間總線系統,你或許會用。但是如果不注意標準的很多細節,你可能無法用好!尤其總線上掛很多設備時,系統極可能不健壯!本文主要參考 I2C version 6標準,I2C 總線看似簡單卻極為複雜,本文總結了規格書中一些要點,也並不全面。在複雜應用場景中,還需要多多踩坑、填坑並加以總結。前文談到了對於技術要點儘量總結、概括以及提煉,這裡想提醒的是一些技術要點的標準往往是最為嚴謹、也最為全面的總結。具體使用時,可多多研讀。
I2C VERSION6 規格書可至 www.i2c-bus.org 下載,也可以至公眾號後臺回復 I2C6,可直接領取。