關於stm32f129的硬件I2C鎖死的重要解決辦法

首先感謝熱心網友的無私奉獻,先描述一下我的問題吧,我用的芯片是STM32F429IGTx並在和MPU9250/6050通信時測試多次軟件復位發生鎖死,硬件電源復位後一切正常,和攝像頭通信的時候並未出現過I2C鎖死的問題。

關於硬件IIC可以參見我之前寫的帖子http://bbs.21ic.com/icview-1034518-1-1.html,寫的也是夠詳細了,這裏發現的另外的一個問題,

硬件iic這裏沒有任何問題,是iic從設備的一些問題,eeprom就沒有類似問題,mpu6050就有這類拉低SDA鎖死的問題,我之前的解決辦法都是對mpu進行斷電覆位才能解決,但是現在發現了一個新的解決辦法。
現象描述我就轉一個博客好了 轉自 “張鵬的博客”
I2C是由Philips公司發明的一種串行數據通信協議,僅使用兩根信號線:SerialClock(簡稱SCL)和SerialData(簡稱SDA)。I2C是總線結構,1個Master,1個或多個Slave,各Slave設備以7位地址區分,地址後面再跟1位讀寫位,表示讀(=1)或者寫(=0),所以我們有時也可看到8位形式的設備地址,此時每個設備有讀、寫兩個地址,高7位地址其實是相同的。

I2C數據格式如下:
無數據:SCL=1,SDA=1;
開始位(Start):當SCL=1時,SDA由1向0跳變;
停止位(Stop):當SCL=1時,SDA由0向1跳變;
數據位:當SCL由0向1跳變時,由發送方控制SDA,此時SDA爲有效數據,不可隨意改變SDA;
當SCL保持爲0時,SDA上的數據可隨意改變;
地址位:定義同數據位,但只由Master發給Slave;
應答位(ACK):當發送方傳送完8位時,發送方釋放SDA,由接收方控制SDA,且SDA=0;
否應答位(NACK):當發送方傳送完8位時,發送方釋放SDA,由接收方控制SDA,且SDA=1。


當數據爲單字節傳送時,格式爲:
開始位,8位地址位(含1位讀寫位),應答,8位數據,應答,停止位。
當數據爲一串字節傳送時,格式爲:
開始位,8位地址位(含1位讀寫位),應答,8位數據,應答,8位數據,應答,……,8位數據,應答,停止位。


需要注意的是:
1,SCL一直由Master控制,SDA依照數據傳送的方向,讀數據時由Slave控制SDA,寫數據時由Master控制SDA。當8位數據傳送完畢之後,應答位或者否應答位的SDA控制權與數據位傳送時相反。
2,開始位“Start”和停止位“Stop”,只能由Master來發出。
3,地址的8位傳送完畢後,成功配置地址的Slave設備必須發送“ACK”。否則否則一定時間之後Master視爲超時,將放棄數據傳送,發送“Stop”。
4,當寫數據的時候,Master每發送完8個數據位,Slave設備如果還有空間接受下一個字節應該回答“ACK”,Slave設備如果沒有空間接受更多的字節應該回答“NACK”,Master當收到“NACK”或者一定時間之後沒收到任何數據將視爲超時,此時Master放棄數據傳送,發送“Stop”。
5,當讀數據的時候,Slave設備每發送完8個數據位,如果Master希望繼續讀下一個字節,Master應該回答“ACK”以提示Slave準備下一個數據,如果Master不希望讀取更多字節,Master應該回答“NACK”以提示Slave設備準備接收Stop信號。
6,當Master速度過快Slave端來不及處理時,Slave設備可以拉低SCL不放(SCL=0將發生“線與”)以阻止Master發送更多的數據。此時Master將視情況減慢或結束數據傳送。


在實際應用中,並沒有強制規定數據接收方必須對於發送的8位數據做出迴應,尤其是在Master和Slave端都是用GPIO軟件模擬的方法來實現的情況下,編程者可以事先約定數據傳送的長度,slave不檢查NACK,有時可以起到減少系統開銷的效果。但是如果slave方是硬件i2c要求一定要標準的NACK,master方是GPIO軟件模擬i2c並沒有正確的發送NACK,就會出現“slave收不到stop”導致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總線進入死鎖狀態。
方法
    (1)儘量選用帶復位輸人的I2C從器件。
    (2)將所有的從I2C設備的電源連接在一起,通過MOS管連接到主電源,而MOS管的導通關斷由I2C主設備來實現。
    (3)在I2C從設備設計看門狗的功能。
    (4)在I2C主設備中增加I2C總線恢復程序。
        每次I2C主設備復位後,如果檢測到SDA數據線被拉低,則控制I2C中的SCL時鐘線產生9個時鐘脈衝(針對8位數據的情況,“9個clk可以激活”的方法來自NXP的文檔,NXP(Philips)作爲I2C總線的鼻祖,這樣的說法是可信的),這樣I2C從設備就可以完成被掛起的讀操作,從死鎖狀態中恢復過來。
        這種方法有很大的侷限性,因爲大部分主設備的I2C模塊由內置的硬件電路來實現,軟件並不能夠直接控制SCL信號模擬產生需要時鐘脈衝。
        或者,發送I2C_Stop條件也能讓從設備釋放總線。
        如果是GPIO模擬I2C總線實現,那麼在I2C操作之前,加入I2C總線狀態檢測 I2C_Probe ,如果總線被佔用,則可嘗試恢復總線,待總線釋放後,再進行操作。要保證I2C操作最小單元的完整性,不被其他事件(中斷、高優先級線程,等)打斷。
  (5)在I2C總線上增加一個額外的總線恢復設備。這個設備監視I2C總線。當設備檢測到SDA信號被拉低超過指定時間時,就在SCL總線上產生9個時鐘脈衝,使I2C從設備完成讀操作,從死鎖狀態上恢復出來。總線恢復設備需要有具有編程功能,一般可以用單片機或CPLD實現這一功能。
  (6)在I2C上串人一個具有死鎖恢復的I2C緩衝器,如Linear公司的LTC4307是一個雙向的I2C總線緩衝器,並且具有I2C總線死鎖恢復的功能。LTC4307總線輸入側連接主設備,總線輸出側連接所有從設備。當LTC4307檢測到輸出側SDA或SCL信號被拉低30ms時,就自動斷開I2C總線輸入側與輸出側的連接.並且在輸出側SCL信號上產生16個時鐘脈衝來釋放總線。當總線成功恢復後,LTC4307會再次連接輸入輸出側,使總線能夠正常工作。
    好了,現象這裏人家已經描述了很清楚了,很多人都說硬件iic是內部控制,沒法執行操作控制scl引腳輸出脈衝,其實可以做到的,在進行iic配置的時候,首先對scl硬件進行配置成開漏,而不是複用開漏,這樣就掌握了scl引腳的主動權,控制其輸出9個時鐘脈衝,然後再配置成複用開漏,再配置iic就可以了,這就是先解決從設備的潛在問題,再進行使用,思路已經說的很清楚了,下面貼一個示例代碼。
RCC_APB1PeriphClockCmd(SENSORS_I2C_RCC_CLK, ENABLE);
RCC_APB1PeriphClockCmd(SENSORS_I2C_SCL_GPIO_CLK, ENABLE);
	
/** 加入一段釋放總線的操作,9個SCL時鐘脈衝,防止從器件一些極端情況拉低佔用總線不釋放導致死鎖 **/
  GPIO_InitStructure.GPIO_Pin =  SENSORS_I2C_SCL_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_Init(SENSORS_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);  
  for(i = 0; i < 10; i++) 			//產生9個SCL時鐘脈衝
  {
	GPIO_SetBits(SENSORS_I2C_SCL_GPIO_PORT, SENSORS_I2C_SCL_GPIO_PIN);
	Delay_us(100);
	GPIO_ResetBits(SENSORS_I2C_SCL_GPIO_PORT, SENSORS_I2C_SCL_GPIO_PIN);
	Delay_us(100);
  } 	
   /* Enable I2Cx clock */
  RCC_APB1PeriphClockCmd(SENSORS_I2C_RCC_CLK, ENABLE);

  /* Enable I2C GPIO clock */
  RCC_AHB1PeriphClockCmd(SENSORS_I2C_SCL_GPIO_CLK | SENSORS_I2C_SDA_GPIO_CLK, ENABLE);


這裏循環進行了10次,實際算下脈衝能保證一定有9個。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章