萬變不離其宗之I2C要點總結

[導讀] 前文大體總結了單片機串口的一些值得注意的要點,本文來梳理一下I2C總線的一些應用要點。這個題目有點大,對於I2C其實不少地方也沒講清楚,只爲了與前文造成系列,若是你們有補充歡迎留言。說了些閒話,進入正題吧。編程

I2C以前世此生

\(I^2C\)(Inter-Integrated Circuit),是一種同步、多主、多從、分組交換、單端、串行計算機總線,由飛利浦半導體(如今的NXP半導體)在1982年發明。它普遍用於在短距離、板內通訊中將低速外設集成電路附加處處理器和微控制器上。\(I^2C\)也能夠寫成I2C或IIC。微信

自2006年10月10日起,實施I2C協議不須要任何許可費用。 可是,得到恩智浦分配的I2C從設備地址須要付費。一些競爭者,如西門子(後來的英飛凌技術,如今的英特爾移動通訊)、NEC、德州儀器TI、意法半導體(之前的SGS-Thomson)、摩托羅拉(後來的飛思卡爾,如今與NXP合併)、Nordic半導體和Intersil,自20世紀90年代中期以來已經將發佈了不少兼容的I2C標準的芯片。
異步

模式

自Version 4以後 I2C支持下面幾種模式:測試

  • 雙向總線
    • standard-mode(Sm): ≤100 Kbit/S
    • Fast-Mode(Fm):≤400 Kbit/S
    • Fast-mode Plus(Fm+):≤1Mbit/S
    • High-speed mode (Hs-mode): ≤ 3.4 Mbit/s
  • 單向總線:
    • Ultra Fast-mode (UFm): ≤ 5 Mbit/s

好處

I2C標準能帶來些啥好處呢?ui

  • 簡單的2線串行I2C總線最小化互連,節省PCB布板走線空間;
  • 徹底集成的I2C總線協議消除了地址解碼器。
  • I2C總線的多主控能力容許終端用戶設備經過外部鏈接到裝配線進行快速測試和校準。
  • 標準支持普遍,大量無鉛封裝I2C總線兼容集成芯片進一步下降了空間需求。

其餘子集

系統管理總線(SMBus),由Intel在1995年定義,是I2C的一個子集,定義了更嚴格的用法。SMBus的一個目的是促進健壯性和互操做性。所以,現代I2C系統合併了來自SMBus的一些策略和規則,有時同時支持I2C和SMBus,只須要經過命令或輸出引腳使用最小限度的從新配置。spa

TWI(雙線接口)或TWSI(雙線串行接口)本質上是在Atmel和其餘供應商的各類系統芯片處理器上實現的同一總線。翻譯

I2C拓撲結構

從概念上,I2C總線有兩根線SDA/SCL就能夠連一堆芯片,實現不少的應用。鏈接拓撲極簡!
設計

好比這樣一個系統:3d

  • LCD顯示
  • ADC採樣
  • EEPROM/FRAM 非易失存儲
  • 溫度採集
  • .....

接下來看看各模式下,鏈接拓撲圖:調試

標準速度/快速模式

高速模式拓撲

混速模式拓撲

工做原理

若是使用IO口模擬I2C總線,或者使用FPGA實現I2C接口,深入理解I2C時序波形無疑是重點中的重點!即便使用內置的I2C控制器外設實現一個I2C總線編程,在調試底層時或者踩坑過程當中,深刻理解時序波形原理,也是很是必要的!

時序圖

I2C的時序圖以下:

  • START事件:能夠聯想一下UART的起始位,這個用於通知I2C通訊的發起。用一句話描述就是在SCL常高時,採集到SDA高到低跳變,這就是啓動事件。

  • 數據有效性SDA線上的數據必須在時鐘的高週期保持穩定。數據線的高或低狀態只能在SCL線上的時鐘信號低時改變。每一個傳輸的數據位產生一個時鐘脈衝。

  • ACK:確認信號ACK的定義以下:發送器在ACK時鐘脈衝期間釋放SDA線,所以接收器能夠將SDA線拉低,並在此時鐘脈衝的高電平期間保持穩定的低電平(見上圖)。須嚴格遵循電氣的創建保持時間,使用時須要用示波器去嚴格測試信號是否能知足這些參數。

  • NACK:當在第九個時鐘脈衝期間SDA保持高電平時,這被定義爲「NACK」信號。 以後主機能夠產生中止條件以停止傳輸,或產生重複的開始條件以開始新的傳輸。 致使NACK產生的條件有五個:

    1. 總線上沒有報文中所包含地址的接收器,所以沒有設備響應應答。
    2. 接收器沒法執行接收或發送操做,好比它正在執行某些實時功能,而且還沒有準備好與主機進行通訊。
    3. 在傳輸過程當中,接收器收到應用協議不理解的數據或命令。
    4. 在傳輸期間,接收器沒法再接收更多有效數據字節。好比程序或者芯片內置緩衝區已經滿了
    5. 主接收器用NACK通知從發送器結束傳輸。這是何意呢?好比主設備已經接受到足夠多的數據,不但願從設備發送更多的數據時,就能夠NACK從設備,這樣從設備就會中止發送

時鐘同步與仲裁

  • 時鐘同步:兩個主機能夠同時開始在空閒總線上進行傳輸,而且必須有一種方法來肯定控制總線並完成其傳輸的方法。 這是經過時鐘同步和仲裁完成的。 在單主機系統中,不須要時鐘同步和仲裁。

    時鐘同步是經過I2C接口中SCL線的線與實現的。啥意思呢?

    • 當SCL從高到低的過渡時,總線上的主機開始計數其低電平時間,且一旦主機時鐘變爲低,它就會將SCL保持在該狀態,直到變爲高狀態爲止。
    • 可是,若是另外一個主機時鐘仍在其低週期內,則此時鐘從低到高的轉變不會改變SCL線的狀態。 因此,SCL線由主機以最長的低電平週期保持爲低電平低電平週期較短的主機在此期間進入高電平等待狀態
    • 上面的話很差理解?看看線與的本質是,啥叫與呢? 好比C=A&B,只要其中一個變量A/B爲低,那麼C就必然爲0,好比下圖中,即使CLK1(爲其中一個主機)爲高了,但奈何另外一主機的CLK2任然爲低啊?因此SCL線上測出來就是低。
    • 當全部相關的主機都計數完低電平週期後,時鐘線被釋放並變爲高電平。 這樣,主時鐘和SCL線的狀態之間就沒有區別,全部主時鐘都開始計數其高電平週期。 第一個完成其高電平週期的主機將SCL線再次拉低。

    這裏的幾句話須要劃重點去理解,這就是I2C總線的核心之核心工做原理:線與!

  • 仲裁:仲裁與同步相似,僅在系統中使用多個主機時纔會涉及到,從站不參與仲裁過程。首先要理解一下仲裁是幹啥的?所謂仲裁就是在多主機模式下,哪個主機能獲取介質的訪問權限,得到權限的主機才能夠傳輸I2C通訊報文。 只有在總線空閒時,主機才能夠開始傳輸。 兩個主機能夠在START的最小保持時間內產生START條件這種狀況會致使總線上出現有效的START條件。 而後須要仲裁以肯定哪一個主機將完成其傳輸。

    仲裁是一位一位地進行。 節點發送1個位後,回讀比較總線上所呈現的數據與本身發送的是否一致。是,繼續發送;不然,退出競爭。SDA線的仲裁能夠保證I2C總線系統在多個主節點同時企圖控制總線時通訊正常進行而且數據不丟失。總線系統經過仲裁只容許一個主節點能夠繼續佔據總線

上圖顯示了兩個主機的仲裁程序。 實際使用中鏈接到總線的主機數量可能會更多。 當主機產生的DATA1的內部數據電平與SDA線上的實際電平之間存在差別時,DATA1輸出將關閉。 從而主機1退出競爭,沒有得到總線的控制權。

  • 時鐘延長:時鐘延長經過將SCL線保持爲低電平來暫停事務。 直到再次釋放高電平,事務才能繼續。 時鐘延長是可選的,實際上,大多數從設備不包括SCL驅動能力,所以它們沒法延長時鐘。

    爲啥要設計這樣一個機制呢?我的理解是爲了加強系統的健壯性而設計的:

    • 在字節傳輸級別,設備可能可以以快速速率接收數據字節,但須要更多時間來存儲接收到的字節或準備另外一個要發送的字節。此時,從機能夠在接收和確認字節後將SCL線保持爲LOW,以強制主機進入等待狀態,直到從機爲握手過程當中的下一個字節傳輸作好準備。

    • 在位級別上,諸如微控制器之類的設備能夠經過延長每一個時鐘的LOW週期來減慢總線時鐘。 任何主機的速度都將根據該設備的內部工做速率進行調整。

    地址及R/W位:

    地址及R/W

    • 7位地址:分讀寫兩種狀況

  • 10位地址:分讀寫兩種狀況

  • 保留地址

從設備地址 讀寫位 描述
0000 000 0 廣播地址
0000 000 1 啓動字節
0000 001 X CBUS地址
0000 010 X 預留給不一樣的總線格式
0000 011 X 預留將來擴展使用
0000 1XX 1 Hs-mode 主代碼
1111 1XX 1 設備ID
1111 0XX X 10位從地址

通用廣播地址

通用廣播地址用於同時尋址鏈接到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字節0000 0001
  • ACK
  • 重複START事件

在須要訪問總線的主機發送了START事件以後,發送START字節(0000 0001)。 另外一個單片機/DSP能夠以低採樣率對SDA線進行採樣,直到檢測到START字節中的七個零之一爲止。 在SDA線上檢測到此LOW電平後,微控制器能夠切換到更高的採樣率,以找到重複的START事件,而後將其用於同步。

總線復位

在異常狀況下,若是時鐘SCL被拉爲LOW了,則有哪些辦法能夠對總線復位呢?

  • 則優選的作法是如I2C設備具備硬件復位輸入,則使用硬件復位信號來複位總線。
  • 若是I2C設備沒有硬件復位輸入信號,若是硬件設計能夠考慮用MOSFET控制設備電源,從新通電以激活強制性的內部上電覆位(POR)電路。
  • 還有一種作法是主機發送9個時鐘SCL脈衝。 使總線保持低電平的設備應在這九個時鐘內的某個時間釋放它。這個具體怎麼作呢?主設備初始化I2C總線時,能夠冗餘加9個SCL脈衝以復位I2C總線,或者檢測到SDA長時間被拉低後,能夠以控制IO高低翻轉的方式控制SCL產生9個脈衝
//可能須要先關閉I2C控制器,若是是使用I2C控制器外設實現的
//I2C_SCL根據不一樣硬件進行移植,delay
#define I2C_SCL P10
void 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標準,I2C總線看似簡單卻極爲複雜,本文總結了規格書中一些要點,也並不全面。在複雜應用場景中,還須要多多踩坑、填坑並加以總結。前文談到了對於技術要點儘可能總結、歸納以及提煉,這裏想提醒的是一些技術要點的標準每每是最爲嚴謹、也最爲全面的總結。具體使用時,可多多研讀。

I2C VERSION6規格書可至www.i2c-bus.org下載,也能夠至公衆號後臺回覆I2C6,可直接領取。
文章出自微信公衆號:嵌入式客棧,版權全部,嚴謹商用,更多內容,請關注本人公衆號