1. 程式人生 > >I2C從機掛死分析和解決方法

I2C從機掛死分析和解決方法

I2C幾乎是嵌入系統中最為通用序列匯流排,MCU周邊的各種器件只要對速度要求不高都可以使用。優點是相容性好(幾乎所有MCU都有I2C主機控制器,沒有也可以用IO模擬),管腳佔用少,晶片實現簡單。I2C協議雖然簡單,實際使用過程中小毛病還不少。今天先來看一個平日最為常見的問題:I2C從機掛死。

很多事情不難而且經常碰到,每次自認為懂了但最終讓你站出來說清楚的時候卻總是不能自圓其說,很難受。所以我決定寫部落格的時候就想盡量把內容寫清楚詳細甚至是透徹,希望讓每一個閱讀博文的同學都能看得明明白白,學會一點小知識。如果還有不清楚的可以留言交流.

I2C規範與特性

I2C是什麼,我相信99%的同學能點到這篇博文對I2C也有了一定的瞭解,這裡附上一份I2C鼻祖NXP (前Philips半導體)的一份權威手冊:

I2C-bus specification and user manual v.6

描述一下I2C最重要的幾個特性,為了後面描述問題和解決方案作一些鋪墊。

  1. I2C是由兩根線(時鐘SCL + 資料SDA)組成的多主多從串行同步通訊匯流排。
  2. 規範要求接入I2C的器件,SCL時鐘和SDA資料線都必須是雙向開漏結構的,通過總線上的上拉電阻拉到邏輯高電平。這樣的結構可以實現線與(&)功能。
  3. 一般情況下I2C的SDA只有在SCL為低電平的時候才能改變,為高電平的時候需要保持。對應到晶片設計上則是上升沿取樣,下降沿變化。

  4. 兩個例外情況由主機發出的匯流排起始條件START(SCL為高時SDA由高變低)和停止條件STOP(SCL為高時SDA由低變高)

掛死 = 掛了 + 宕機

掛死這個詞應該來源於英文hangs : To cause (a computer system) to halt so that input devices, such as the keyboard or the mouse, do not function.

前面提到因為線與&結構,是I2C匯流排設計上最關鍵的特徵,用了這種結構才能實現

  • 多主機仲裁同步
  • 慢從機同步快主機

因為這個特性,只要總線上任何一個器件拉低了SDA或者SCL,其他器件都無法拉高它們,看到的都是低電平。如果有器件不釋放匯流排,則整個總線上的通訊都會被暫停,我們成為I2C bus hangs

:I2C匯流排掛死

因為I2C主機一般是可程式設計的器件,受我們控制,如果主機主動拉低了匯流排,我們可以通過除錯程式碼瞭解原因,也可以很方便的通過復位I2C外設或者復位晶片來退出這種狀態。而I2C從機往往不帶RESET引腳,如果掛死了匯流排即使整個系統復位都無法解除,僅重新上下電才可以恢復。很多系統上是不可接受的,因此我們需要更加小心的處理I2C從機掛死的情況,下面分析也是針對I2C從機掛死來寫的。

SDA掛死

先來看下哪些情況下I2C從機會需要拉低SDA線。

  1. 主機向從機寫資料或地址時,從機如果發出ACK應答,則會第9個CLK的期間拉低SDA
  2. 主機讀資料的時候,從機會在bit為0時對應的CLK期間拉低SDA

那什麼情況I2C從機又可能鉗住SDA線呢?我們先來看一個典型的I2C主機發起對某一器件地址讀操作,讀到的資料為10011000b,MSB在先也就是0x98。在圖中地址位元組第9個CLK期間從機拉低SDA表示對地址進行應答,在返回的資料位元組的第2,3,6,7,8幾個CLK器件從機拉低SDA輸出邏輯0電平。



根據上面講的I2C協議SCL為高的時候,SDA電平應保持,而等到SCL為低後(也就是下降沿後)才能發生改變。如果在上面幾個CLK的前半個週期SCL拉高後主機不再拉低呢?從機會有什麼動作?YES,從機會持續拉低著SDA,直到見到下一個他應該輸出高電平的下降沿。

最常見的情況就是主機在通訊的過程中產生了復位。由於復位動作通常會立刻執行,外設狀態機都恢復到預設狀態,也就發不出完整的CLK了。那麼等到主機復位完成回來後,SCL為高,SDA被從機拉低。主機無法發起START起始條件,不能開始下一次與從機的通訊,這稱為SDA掛死。

要想辦法恢復,我們先得知道從機什麼時候會釋放SDA。由於剛剛的SCL下降沿沒有給出來,恢復匯流排要做的第一件事情就是在想辦法用GPIO在SCL線上模擬一個下降沿,讓從機狀態機繼續走下去。只發一個下降沿並不一定能將SDA釋放,因為我們並不清楚當主機復位異常發生時刻從機到底處於圖中哪一個狀態,所以需要逐個CLK去探測,直到見到SDA被釋放了,我們才終止並且傳送STOP條件告訴從機這次坑爹的通訊結束了。

網上通常的傳授的方法是模擬9個連續的CLK,但是我更喜歡上面的方法,一是速度快,二是具備可確定性。傳送9個CLK我主要擔心從機在最後一個CLK時又拉低了SDA,還是需要用到上面的方法來釋放。

通過模擬幾種情形來實際體會一下(從機對SDA的操作紅色表示):

如果在地址位元組第9個CLK拉高後主機復位。在模擬的第一個時鐘低電平期間就可以看到SDA的釋放,隨後主機先拉低SDA,再模擬一個STOP結束條件。



在資料位元組第2個CLK拉高後主機復位,在第二個模擬的時鐘低電平期間才看到SDA釋放



在資料位元組第6個CLK拉高後主機復位,在第三個模擬的時鐘低電平期間才看到SDA釋放


通過以上三種情況的分析,想必你已經非常清楚改如何處理了,最後附上一個程式處理流程圖:


SCL掛死

I2C從機主動拉低SCL線在規範中是一個合法的行為,稱之為Clock Stretching(時鐘擴充套件,我一般叫他時鐘同步)。通常是主機請求資料( 收或者發)後從機需要一些時間處理,且沒有多餘Buffer可以接收接或者提供接下來的資料的時候從機則會拉低SCL一段時間直到有新的資料準備好。

SCL掛死(也就是前面所說一直拉低SCL)這種情況在標準I2C從器件上基本不會出現,因為只要晶片還在正常工作buffer總算有準備好的時候,自然就就釋放SCL了。往往是使用使用者使用MCU作為I2C從機時,程式設計上的問題導致MCU無法讀取&填充buffer而導致,重點分析MCU I2C中斷服務程式。

  1. I2C中斷服務程式被意外遮蔽
  2. 中斷服務程式中陷入了一些標誌位查詢的while(flag != xxx)死迴圈
  3. I2C功能系統被意外禁止