I2C接口是一種使用非常普遍的MCU與外部設備的接口方式,在STM32中也集成了I2C接口,我們也常常使用它來與外圍的傳感器等設備通訊。
最近在我們使用STM32F1VET6讀取壓力和溫濕度傳感器數據時,就是使用I2C接口來實現通訊的。但在使用I2C和STM32F1的標准庫讀取數據時出現了死機的現象。其現象是這樣的,程序可以順利的運行,但I2C沒有數據返回。用示波器查看波形時,發現SCL的電平時鍾為高,而SDA的電平時鍾為低。如果拔掉對應的設備,SCL的波形則恢復正常。接上設備恢復正常,但運行一會現象依舊。
一開始以為是連接的設備有問題,於是換了一台設備,發現依舊如此。難道真的是I2C出現了死鎖現象。那我們看看究竟怎么樣的情況下I2C才會發生死鎖現象呢?
在I2C主設備進行讀寫操作的過程中,主設備在開始信號后控制SCL產生8個時鍾脈沖,然后拉低SCL信號為低電平,在這個時候,從設備輸出應答信號,將SDA信號拉為低電平。如果這個時候主設備異常復位,SCL就會被釋放為高電平。此時,如果從設備沒有復位,就會繼續I2C的應答,將SDA一直拉為低電平,直到SCL變為低電平,才會結束應答信號。而對於I2C主設備來說.復位后檢測SCL和SDA信號,如果發現SDA信號為低電平,則會認為I2C總線被占用,會一直等待SCL和SDA信號變為高電平。這樣,I2C主設備等待從設備釋放SDA信號,而同時I2C從設備又在等待主設備將SCL信號拉低以釋放應答信號,兩者相互等待,I2C總線進人一種死鎖狀態。同樣,當I2C進行讀操作,I2C從設備應答后輸出數據,如果在這個時刻I2C主設備異常復位而此時I2C從設備輸出的數據位正好為0,也會導致I2C總線進入死鎖狀態。
既然判斷了是I2C出現了死鎖,那我們有什么辦法解決了?網上有不少高手提供了多種的解決方案。當然有些方法實現起來不那么容易,因為我們的硬件已經確定,軟件業已經編好,做很大的改變是不現實的。
所以我選擇在出現死鎖時將I2C接口重新配置,並且將對應的GPIO端口配置為輸出,並將電位拉高,然后再沖新配置I2C端口,但現象並未消除。於是我們將I2C的時鍾也重新使能現象消除。這種不斷重置I2C接口的方法雖然能夠使得在死鎖時能夠恢復使用,但不停地解除和配置接口總是感覺並那么理想。
在我們的試驗中我們發現,可以從軟件和硬件的角度來解決這一問題。首先我們來說下軟件方法:在I2C主設備中增加I2C總線恢復程序。每次I2C主設備復位后,如果檢測到SDA數據線被拉低,則控制I2C中的SCL時鍾線產生9個時鍾脈沖(針對8位數據的情況),這樣I2C從設備就可以完成被掛起的讀操作,從死鎖狀態中恢復過來。這種方法有很大的局限性,因為大部分主設備的I2C模塊由內置的硬件電路來實現,軟件並不能夠直接控制SCL信號模擬產生需要時鍾脈沖。其次我們說一下硬件方式:如硬件尚可修改,可以增加一個額外的總線恢復設備或者串入一個具有死鎖恢復的I2C緩沖器都可以從硬件上解決這個問題。其實我們發現用GPIO模擬I2C通訊時,死鎖現象是不會出現的。所以如果硬件不能改變,但軟件容易改變時,我們可以考慮使用GPIO來模擬I2C通訊。