I2C死鎖原因及解決方法

 死鎖總線表現爲:SCL爲高,SDA一直爲低

 

現象:單片機採用硬件i2c讀取E2PROM,當單片機復位時,會有概率出現再無法與E2PROM通信,此時SCL爲高,SDA一直爲低

原因:當單片機正在和E2PROM通信,如果主正好發生打算髮第9個時鐘,此時SCL爲高,而從開始拉低SDA爲低做準備(作爲ACK信號),等待主SCL變低後,從再釋放SDA爲高。如果此時正好單片機復位,主SCL還沒來得及變低,直接變成高電平,此時從還在等待SCL變低,所以一直拉低SDA;而主由於復位,發現SDA一直爲低,也在等待從釋放SDA爲高。因此主從都進入一個相互等待的死鎖狀態。

解決方法:最好的方法是採用模擬i2c. 但由於已經配置成硬件i2c,程序改爲上電或復位改成發9個SCL時鐘信號,使從好釋放SDA。

 

 

 最近發現單片機(硬件I2C實現)讀取E2PROM時候,單片機復位可能會引起i2C死鎖,表現爲SCL爲高,SDA一直爲低,後發現是E2PROM從設備拉死i2c總線,從設備斷電之後,SDA變高,上電後通信正常。後來通過拉低SCL信號線,SDA就會自動變成高電平,i2c總線恢復。後查看一篇文章,講的不錯,特摘錄如下:

 

    在正常情況下,I2C總線協議能夠保證總線正常的讀寫操作。但是,當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總線進入死鎖狀態。

 

SCL爲高,SDA一直爲低原因

從:正常時序下:SDA信號是在SCL爲低的狀態下改變,即從應答SDA爲低電平時,此時SCL應爲爲低電平(即從設備是先拉低SDA信號,等待主設備SCL由高變低,“取走”ACK信號後,從再釋放SDA爲高)。但如果此時時序被打亂,例如單片機i2c通信時突然復位,SCL突然變高,則從設備SDA一直爲低,等待SCL變低。

主:SDA被從拉低,故主認爲i2c總線佔用,一直等待SDA變高

這樣主從進入一個相互等待的死鎖過程。

 

 方法

    最好用模擬I2C實現,則不會死鎖

    (1)儘量選用帶復位輸人的I2C從器件。

    (2)將所有的從I2C設備的電源連接在一起,通過MOS管連接到主電源,而MOS管的導通關斷由I2C主設備來實現。
    (3)在I2C從設備設計看門狗的功能。

    (4)在I2C主設備中增加I2C總線恢復程序。每次I2C主設備復位後,如果檢測到SDA數據線被拉低,則控制I2C中的
SCL時鐘線產生9個時鐘脈衝
(針對8位數據的情況),這樣I2C從設備就可以完成被掛起的讀操作,從死鎖狀態中恢復過來。
這種方法有很大的侷限性,因爲大部分主設備的I2C模塊由內置的硬件電路來實現,軟件並不能夠直接控制SCL信號模擬
產生需要時鐘脈衝。

  (5)在I2C總線上增加一個額外的總線恢復設備。這個設備監視I2C總線。當設備檢測到SDA信號被拉低超過指定時間
時,就在SCL總線上產生9個時鐘脈衝,使I2C從設備完成讀操作,從死鎖狀態上恢復出來。總線恢復設備需要有具有編程
功能,一般可以用單片機或CPLD實現這一功能。

  (6)在I2C上串人一個具有死鎖恢復的I2C緩衝器,如Linear公司的LTC4307如圖2所示:LTC4307是一個雙向的I2C
總線緩衝器,並且具有I2C總線死鎖恢復的功能。LTC4307總線輸人側連接主設備,總線輸出側連接所有從設備。當LTC4307
檢測到輸出側SDA或SCL信號被拉低30ms時,就自動斷開I2C總線輸人側與輸出側的連接.並且在輸出側SCL信號上產生16個時鐘脈衝來釋放總線
。當總線成功恢復後,LTC4307會再次連接輸人輸出側,使總線能夠正常工作。


stm32讀取mpu6050,i2c死鎖處理:

if(I2C1->SR2&0x0002)
{
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIOB->BRR  = GPIO_Pin_6;//scl reset
delay();
GPIOB->BSRR = GPIO_Pin_6;//scl set
delay();
GPIOB->BSRR = GPIO_Pin_7;//sda set
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章