王 輝
(銳捷網(wǎng)絡(luò)股份有限公司,福建 福州 350002)
I2C(Inter-Integrated Circuit)總線是一種由PHILIPS公司開發(fā)的用于連接微控制器及其外圍設(shè)備的總線。它是同步通信的一種特殊形式,具有接口線少、控制方式簡化等優(yōu)點,成為近年來在微電子通信控制領(lǐng)域廣泛采用的一種總線標(biāo)準(zhǔn)。但由于I2C總線是共享性總線,總線上任何一個器件的SDA/SCL信號的異常都會影響整個總線的正常工作,因此I2C協(xié)議有提供了復(fù)位操作來解決問題。但在實際應(yīng)用中還是會有一些異常的情況出現(xiàn),例如操作過程中I2C主機異常復(fù)位,導(dǎo)致操作未完成,這樣總線就有可能出現(xiàn)死鎖的情況。該文就I2C死鎖的各種情況做了深入具體的分析說明,并給出了一種完整、可靠的解決方案。
一次完整的數(shù)據(jù)傳送如圖1所示,I2C是以字節(jié)(8位)為單位進(jìn)行數(shù)據(jù)傳輸?shù)?。在起始位出現(xiàn)后,發(fā)送8位數(shù)據(jù)(首先傳輸?shù)氖菙?shù)據(jù)的最高位),每傳輸8位后必須跟一個應(yīng)答位(ACK)。ACK為0代表有效,反之則說明無人應(yīng)答。數(shù)據(jù)傳輸一般由主機產(chǎn)生的停止位終止。
如果當(dāng)從機需要完成一些其他功能后才能繼續(xù)完成數(shù)據(jù)通信時,從機可以使時鐘SCL保持低電平,迫使主機進(jìn)入等待狀態(tài)。當(dāng)從機準(zhǔn)備好并釋放時鐘線SCL后,數(shù)據(jù)傳輸繼續(xù)[1-2]。
I2C協(xié)議里定義了一個總線復(fù)位的機制,即主機在非正常操作過程中主動在SCL信號線上發(fā)9個時鐘周期,總線上的從機檢測到后,必須主動復(fù)位各自的I2C接口[3]。
這個機制可以解決實際應(yīng)用中從機一直拉低SDA的總線死鎖的情況。但遇到SCL被拉低以及其他復(fù)雜的死鎖,I2C協(xié)議并沒有給出解決的機制。
I2C死鎖有多種故障類型,可以歸納為如下3種情況。
2.1.1 情況1(應(yīng)答位死鎖)
I2C總線讀或?qū)懖僮髦噶顟?yīng)答位死鎖導(dǎo)致。在I2C設(shè)備進(jìn)行讀或?qū)懖僮髦噶畹倪^程中,主設(shè)備在產(chǎn)生開始信號后在SCL線上產(chǎn)生8個時鐘脈沖,然后拉低SCL信號的電平。此時,從設(shè)備輸出應(yīng)答信號,將SDA信號拉為低電平。如果這個時候主控制器突然復(fù)位,導(dǎo)致SCL信號被拉為高電平,而從設(shè)備將SDA一直拉為低電平,直到SCL變?yōu)榈碗娖讲艜Y(jié)束應(yīng)答信號。主、從設(shè)備之間相互等待,進(jìn)入應(yīng)答位死鎖狀態(tài)。
2.1.2 情況2(讀操作死鎖)
當(dāng)I2C主控制器進(jìn)行讀操作時,I2C 從設(shè)備應(yīng)答后輸出數(shù)據(jù)。如果這時主控制器突然復(fù)位,而此時從設(shè)備輸出的數(shù)據(jù)位正好為0(SDA為低電平),主控制器就會將SCL拉為高電平,從設(shè)備將SDA拉為低電平,這樣也會導(dǎo)致I2C總線進(jìn)入死鎖狀態(tài)。
2.1.3 情況3(I2C控制器狀態(tài)異常)
I2C控制器狀態(tài)異常,SCL和SDA電平處于“正?!睜顟B(tài),但主控又無法繼續(xù)操作I2C總線,也無法讀取I2C狀態(tài)寄存器值。這個情況一般都是主設(shè)備沒有發(fā)送STOP命令,進(jìn)而使其I2C控制器進(jìn)入異常狀態(tài)。
針對死鎖,I2C協(xié)議里的推薦是發(fā)送9×CLK,進(jìn)行總線復(fù)位。這個方法是能解決一些死鎖的情況,但針對上述死鎖情況是沒法解決的。例如對應(yīng)答位死鎖狀態(tài),由于I2C正常發(fā)送9×CLK(8個數(shù)據(jù)位+應(yīng)答位),此時發(fā)送8CLK會使I2C總線死鎖位移到下一個字節(jié)的應(yīng)答位,SDA仍然為低電平,即陷入死循環(huán)而無法解鎖。
2.2.1 應(yīng)答位死鎖
應(yīng)答位死鎖進(jìn)入過程如下:主設(shè)備對從設(shè)備進(jìn)行讀寫操作時,進(jìn)行到應(yīng)答位,此時主設(shè)備異常復(fù)位,SCL被主設(shè)備拉高,SDA被從設(shè)備拉低,如圖2所示。
應(yīng)答位是在第9個CLK,主設(shè)備只要將SCL輸出一個低電平,從設(shè)備就會將SDA釋放到高電平,再發(fā)送一個STOP命令,就可將I2C總線解鎖,即發(fā)送1×CLK+STOP就可以將總線解鎖。另外從I2C 協(xié)議可知,I2C總線是以byte+ACK為單位進(jìn)行傳輸?shù)?,?個CLK為一組,如果發(fā)送8×CLK+STOP,則會使I2C進(jìn)入下一個byte的ACK,無法從死鎖中恢復(fù)。由此可知,對應(yīng)答位死鎖狀解鎖,發(fā)送(1+N)×CLK+STOP可以解鎖,N必須是8的整數(shù)倍。
2.2.2 讀操作時死鎖
讀操作時進(jìn)入死鎖的過程如下:主設(shè)備對從設(shè)備正在進(jìn)行讀操作,當(dāng)從設(shè)備輸出的數(shù)據(jù)為0(1到8之間任意bit)時主設(shè)備異常復(fù)位,SCL就會被主設(shè)備拉高,SDA被從設(shè)備拉低,進(jìn)入死鎖狀態(tài)(如圖3所示),也即讀操作有可能在1到8 bit之間的任意bit進(jìn)入死鎖狀態(tài)。當(dāng)?shù)?個bit進(jìn)入死鎖時,解鎖需要8×CLK+STOP。當(dāng)?shù)?個bit進(jìn)入死鎖時,解鎖需要1×CLK+STOP,也即最多需要8×CLK+STOP,最少需要1×CLK+STOP。同時也有驗證CLK數(shù)量大于8時,比如9×CLK+STOP也能正常解鎖??偨Y(jié)來看,N×CLK+STOP(N≥8)可以對所有讀操作時死鎖狀態(tài)進(jìn)行解鎖。
2.2.3 控制器狀態(tài)異常
控制器狀態(tài)異常進(jìn)入死鎖的過程如下:I2C總線的SCL異常被拉低,或者配置為GPIO模式發(fā)送命令過程中,主設(shè)備主動或者被動停止繼續(xù)發(fā)現(xiàn)CLK,進(jìn)而使主設(shè)備的I2C控制器進(jìn)入異常狀態(tài)。
出現(xiàn)異常,一般可從主機I2C控制器內(nèi)部狀態(tài)寄存器的信息分析出來。通常I2C控制器的邏輯都是用I2C CORE IP來實現(xiàn)的[4],這樣從State Reg來獲取控制器的運行狀態(tài),當(dāng)狀態(tài)碼為00時就代表總線出現(xiàn)異常狀態(tài)(bus error)。此時直接發(fā)送一個STOP命令可以使控制器跳出bus error狀態(tài),恢復(fù)正常。這個方法我們在Cavium/NXP等系列的CPU平臺測試驗證均有效[5-6]。
通過上述分析,該文針對不同的死鎖情況給出了對應(yīng)的解決方案,見表1。但在實際應(yīng)用中,由于并不知道死鎖發(fā)生的具體情況,如果每次都要把這3種解決方案都試一遍,軟件實現(xiàn)起來有些復(fù)雜,因此需要找到歸一化的解決方案。
表1 死鎖解決方案匯總
第2節(jié)的3種解鎖方案可以歸結(jié)為9×CLK+STOP這一種。對于這個方案是否真的能解決問題,該文做了驗證,發(fā)現(xiàn)該方案對上述死鎖均可有效解鎖,只是在寫操作應(yīng)答位死鎖時,雖然也能解鎖,但可能會存在向從設(shè)備誤寫數(shù)據(jù)的風(fēng)險。
對讀操作應(yīng)答位死鎖狀態(tài),發(fā)送CLK對其進(jìn)行解鎖時,主設(shè)備知道此時讀取的數(shù)據(jù)是無效的,可以選擇忽略這些數(shù)據(jù)。
基于CAVIUM cpu的板卡,我們對從設(shè)備EEPROM即24C02(地址為0x57),偏移為0,寫數(shù)據(jù)0x5a,在ACK之后加上9×CLK+STOP,此時9×CLK發(fā)送時SDA的數(shù)據(jù)不會被寫入EEPROM內(nèi)部[7],如圖4所示。
因此說明第2節(jié)歸納總結(jié)的方案存在缺陷。接下來分析如何改進(jìn)。
針對對寫操作應(yīng)答位死鎖解鎖無法寫入的情況,從EEPROM的內(nèi)部流程可知,EEPROM會把接收到的數(shù)據(jù)先緩存到內(nèi)部的Buffer中,當(dāng)接收到STOP條件后,才會往內(nèi)部寫。所以解鎖過程中發(fā)送9個CLK后又直接發(fā)STOP命令,就會被EEPROM認(rèn)為是無效的結(jié)束操作,沒法啟動內(nèi)部寫的動作。因此發(fā)完9個CLK后,要先發(fā)送START命令形成新開始,再發(fā)送STOP命令,就不會有誤操作。
而針對控制器狀態(tài)異常,同樣可利用START命令使從設(shè)備重新開始這一特性,在解鎖命令之前加入命令START,從設(shè)備就不會對START命令之前主設(shè)備發(fā)送的異常操作進(jìn)行應(yīng)答。
綜合來看,解決各種死鎖的歸一化解鎖命令應(yīng)該如下:START+9×CLK+START+STOP。使用這個解鎖命令分別針對所有故障現(xiàn)象模擬測試的驗證情況見表2。
表2 改進(jìn)方案驗證記錄
因此,START+9×CLK+START+STOP對讀或?qū)憫?yīng)答位死鎖狀態(tài)、讀操作死鎖狀態(tài)和控制器狀態(tài)異常情況均可有效解鎖,也即對所有情況均可有效。因此,該文總結(jié)了實現(xiàn)的邏輯流程圖[8],如圖5所示。
該文在介紹了I2C基本協(xié)議后,全面地分析了各種I2C死鎖的各種原因及其對應(yīng)解決方案,并在此基礎(chǔ)上提煉了一個完整,可靠的解決方案。通過試驗驗證,說明此方案可以有效得提高I2C總線的魯棒性。