I2C七宗罪之第一罪(七宗罪第一罪是什么)
七宗罪,七個天主教的罪過,簡稱七宗罪。宗為來源、根源的意思。
天主教教義中提出“按若望格西安和教宗格里高利一世分辨出教徒常遇到的重大惡行”。“重大”在這里的意思在于這些惡行會引發(fā)其他罪行的發(fā)生,罪行按嚴重程度遞增依次為傲慢、嫉妒、憤怒、懶惰、貪婪、淫欲和暴食。
I2C第一罪
有些工程師聊起I2C的時候,會有不屑一顧的感覺, “切,不就是兩根線嗎,一個時鐘,一個數(shù)據(jù)”!每每碰到這些人,就會有一種感覺,兄dei,你真是坑沒有踩夠啊!
下面是我按照本人工作時間先后順序,列出碰到過的I2C的問題。有一點我要強調一下,我所列出的問題,不是從書上看來或者從哪里杜撰來的,每一個問題都是來自于不同的公司以及不同的項目,由本人親歷并且解決好的問題,希望對年輕的工程師有所幫助!
2005年, 在L公司(想想當年的L公司,那可是風光的很。)的光網絡系統(tǒng)上有一塊叫LKAXXX的線卡上出現(xiàn)了一個怪事,說到這里,肯定有人一看到MPC860, 就忍不住笑了, 一看就是上了年紀的大叔,哎,歲月不饒人吶 ^_^,我剛剛從學校里畢業(yè)的時候就是MPC860差不多誕生的時候。
先來描述一下問題:
1. 經常當有人按下Reset button后發(fā)現(xiàn)系統(tǒng)起不來了,UART console打印了一半就死在那里;
2. 再次按下復位后,癥狀一樣, 不管怎么按復位按鈕系統(tǒng)都死,UART打印一點點信息掛在那里;
3. 多次按復位按鈕無效后,斷電重啟后OK;
4. 大部分情況,同一塊板子按復位按鈕后是OK的,但是少數(shù)情況是Fail的。
這時候很多就會說,這不是很簡單,讓軟件用調試器加斷點跟蹤啊,說對了,我們firmware工程師還是很牛逼的,很快就告訴我們問題出在I2C上, 我們用示波器測量在死機情況下的I2C信號,發(fā)現(xiàn)SDA數(shù)據(jù)信號一直是低電平,怎么復位都沒有用,一直是低,只有斷電重啟后,SDA才變高。那么原因初步定位了,正式因為I2C-SDA被強行拉低,才導致系統(tǒng)起不來,而斷電后SDA被釋放了,系統(tǒng)也就正常了。
我們知道I2C是open drain的,肯定是被什么芯片給拉了啊,這個很容易想到,不應該是CPU, 因為CPU已經被復位了啊, 那么懷疑的對象就到了和CPU相連的Device上, 乍一看下面的原理圖, 我Kao,這是連連看么?還能有比這個更加簡單的事情嗎?
那么究竟是什么原因導致SDA被EEPROM拉低了呢?
我們再來看一張圖:
我們看到這里對EEPROM的處理比較特殊, 在絕大部分的原理圖里面,我從來都看不到,就是把EEPROM的電源加一個開關:
1. 當復位為低電平時,EEPROM的VDD連到低,斷電;
2. 當復位為高電平時,EEPROM的VDD重新連到外部的VDD,恢復供電。
這時候聰明的小伙伴們已經悟出來, Kao!剛剛上面的LKAXXX的板子,如果我們每次按下復位按鈕復位CPU的時候,通過按鈕產生的復位信號High-Low-High由這里的開關電路把EEPROM的電源斷開一會兒,是不是EEPROM就不會去把SDA拉低了啊,Bingo, 對了。
可是又有人說了,這不是增加成本嘛,還有啊,也沒有看見有人這么干過啊,哈哈,對了,只有日本人才會這么用一根筋的設計方法,我們中國人永遠找到更好的解決辦法,我們繼續(xù)往下走。
請看下面這張圖,有沒有很熟悉?
這是一個I2C的讀操作,順序如下:
1. Master發(fā)出start;
2. Master發(fā)出地址和讀命令;
3. Slave給出ACK然后發(fā)出數(shù)據(jù)data7-0,這一共8拍由slave來驅動I2C SDA,(這里要記住SCL一直是由master來驅動的,當然后面也有特殊情況,這個我們留在后面的七宗罪里面詳述),8拍的時間(假設100K的速率,周期為10us)是80us。
停停停停停, 搞笑的事情來了,如果在這80us的時間里面,有人按下了復位按鈕, 會怎樣 ?
舉個“栗子”
假設四個人打麻將,我的上家把牌打出來后,就該輪到我出牌了,可是這時候來了個電話,我去接電話了, 等我回到牌桌后,我忘記了剛剛輪到我出牌,我以為上一把結束了,直接把麻將推倒洗牌了,這時候等著我的下家可就不干了,人家等著我出牌好胡呢。然后我這重來的舉動惹惱了人家,道歉也好,賠不是也罷,人家不接受,不玩了!得嘞,這局麻將是玩不下去了。
類比上面I2C的情況,想象一下:
1. 80us的時候 EEPROM Slave正在配合CPU輸出讀的數(shù)據(jù),人家玩得正high呢;
2. 這時候CPU收到一個復位指令,而且是強行復位,類似于接電話;
3. 等到CPU復位完了,完全忘記了剛剛EEPROM正在drive數(shù)據(jù),甚至有可能EERPROM已經傳完8bit數(shù)據(jù),正在CPU的NAK(看上圖),這事擱誰身上能受得了;
4. 這種情況CPU看起來也是沒有辦法,誰叫我們按下了復位按鈕了呢。還有一種情況就是CPU自己不厚道,比如去執(zhí)行優(yōu)先級中斷程序后,回來也忘記了別人(EEPROM)正在等待自己繼續(xù)剛才的工作。
這里肯定會有人問,不是復位了嗎? 請仔細看上面的原理圖, EEPROM是沒有復位管腳的,也就是說, 我們按下復位按鈕時, CPU復位了,但是EEPROM沒有復位,它的狀態(tài)機還在等待輸出數(shù)據(jù)給CPU或者等待CPU的NAK指令以便結束當前的這筆操作,這和上面打麻將的例子是一回事。
那么請問剛剛的SDA被拉低是怎么回事呢?很簡單, 上圖的ACK就是低電平,或者Slave drive的data bit7-0其中有高有低啊。
那么除了上面給EEPROM的電源加開關的方式,我們還有上面辦法來解決這個問題呢?
我們繼續(xù)看圖:
先來想想剛剛打麻將的事情,如果我打完麻將回來后,直接牌友每個人100塊錢,別人肯定是樂意繼續(xù)陪你玩的,很簡單的辦法解決了問題。如上圖, 我們讓軟件工程師在代碼里面做了下修改:
1. 當檢測到SDA被拉低后;
2. 軟件就持續(xù)發(fā)送9個時鐘;
3. 在發(fā)9個clock的過程中SDA會變高變低;
4. 當Device發(fā)完所有數(shù)據(jù)后,SDA被釋放;
5. 此時狀態(tài)機到達NAK的phase時,SDA釋放變高,產生了一個NAK;
6. 注意此時9個時鐘不一定用完,EEPROM就把到達NAK phase把SDA釋放了,但是CPU是不知道的,他會一直發(fā)完9個時鐘;
7. 最后CPU再發(fā)送一個stop把整個讀操作結束掉;
The 9 clock pulses make the hanging device’s state machine move to the next state after each clock pulse while the SDA released (not pulled down) which will cause a NACK when the state machine will move to the ACK phase. The NACK will force the device to go to idle mode(意思和我上面的步驟一樣)。
1. Master tries to assert a logic 1 on the SDA line;
2. Master still see a logic 0 and then generate a clock pulse on SCL;
3. When device come to NAK phase, then master will generate SDA high which;is a NAK, but master does not know, until master send all the 9 clocks;
4. Master Generate a stop condition。
上面講的是I2C讀操作被中斷導致死機的情況,下面聊聊I2C寫操作被中斷的情況,解決辦法“簡單粗暴”一些, 大家想想為什么。
這里一樣還是只發(fā)9個時鐘,在9個時鐘的過程中,device就可能發(fā)出一個ack,CPU看到ACK后,再發(fā)一個stop結束本次操作:
1. Master tries to assert a logic 1 on the SDA line;
2. Master still see a logic 0 and then generate 9 clock pulse on SCL;
3. Generate a stop condition。
這里要注意,寫操作被中斷時,發(fā)出的9個clock一定是等到最后一個時鐘發(fā)完, device才被釋放。而讀操作則不一定,有可能發(fā)了第一個時鐘時device就被釋放了只是CPU不知道,到第9個時鐘時stop。
文章結束,給大家留一個思考題,為什么這里一定是9個時鐘呢?歡迎評論區(qū)留言,想明白了,這篇文章你就看懂了。