中斷和主循環(huán)不登對 系統(tǒng)一直休眠久睡
你永遠(yuǎn)也喚不醒一個裝睡的人,但是你可以一巴掌呼醒他。
本文引用地址:http://2s4d.com/article/201905/400938.htm可如果一個嵌入式系統(tǒng)休眠之后,就猶抱琵琶半遮面,千呼萬喚醒不來了呢?筆者擔(dān)綱開發(fā)的中控鎖模塊就醒不過來了。
1
這個問題已經(jīng)折磨我整整兩天了,搞得我心力憔悴。合作伙伴一天幾個電話,恨不得從手機里面直接跳過來當(dāng)面質(zhì)問我:“為什么喚不醒,為什么死睡?”
平時對我愛答不理的領(lǐng)導(dǎo)也一改常態(tài),轉(zhuǎn)而死死地盯著我。老實厚道的我呢,自然也十分緊張且惶恐,因為問題十有八九確實出在我寫的代碼上。
關(guān)鍵是這個問題出現(xiàn)在馬上大批量供貨的前夜,太不是時候了!我們給某車廠供貨的中控鎖模塊已經(jīng)走到了小批量供貨階段,前期供貨20套倒是無驚無險,沒發(fā)現(xiàn)什么問題,按照流程,接下來要供貨100套。
如果阿彌陀佛的話,就再供貨200套,再沒有什么問題,就進(jìn)入大批量供貨階段了。到時,合作伙伴開始穩(wěn)定盈利,領(lǐng)導(dǎo)政績、獎金到手,不會爭功只會攬過的我呢,揮一揮手退到幕后,深藏功與名,一切就萬事大吉,只待歲月靜好了??墒?,恰恰在供貨100套期間,忘了求菩薩保佑,結(jié)果就出事了。
一天,車廠的生產(chǎn)線上下線了裝配了我們的中控鎖模塊的50輛新車,本來一切順利,在生產(chǎn)線上操作中控鎖沒什么問題,匹配學(xué)習(xí)了遙控鑰匙,也能用遙控正常操作。但是到了下線后車廠人員要把新車開回倉庫時,突然發(fā)現(xiàn)有幾臺車用遙控鑰匙、機械鑰匙死活也喚醒不了了!!
事關(guān)重大,車廠立馬通知了中控鎖的供貨商—我們的合作伙伴,合作伙伴立馬找到了我們(中控鎖的開發(fā)商)領(lǐng)導(dǎo),領(lǐng)導(dǎo)立馬找到了我。我也立即給自己上緊了發(fā)條,進(jìn)入戰(zhàn)斗狀態(tài)。
2
眼看到手的政績和獎金要飛,領(lǐng)導(dǎo)心急火燎,早被晾到一邊的我卻頗不以為然。
到了現(xiàn)在這個階段,軟件肯定沒問題,有問題早就測出來了嘛,還能等到現(xiàn)在?肯定是中控鎖的線束出了問題。
當(dāng)我把輕描淡寫的分析說與領(lǐng)導(dǎo)時,領(lǐng)導(dǎo)一改往日的溫和,一下子急了:“人家線束供了這么多年了,按你的說法,有問題早就測出來了,還能等到現(xiàn)在?”
歲月的風(fēng)霜早已經(jīng)消去了我性情中所有的剛硬和火熱,只剩下如水的柔和??吹筋I(lǐng)導(dǎo)急了,我心中起著嘀咕,臉上泛起笑容,小心翼翼地對領(lǐng)導(dǎo)言道:“要不我再看一下代碼,沒準(zhǔn)真是哪里出了岔子呢?”
得到我的表態(tài)后,領(lǐng)導(dǎo)轉(zhuǎn)身踱了開去,一邊嘴里念叨著得跟車廠確認(rèn)一下線束有沒有問題,一邊轉(zhuǎn)過頭來再次叮嚀我一番:抓緊?。】粗I(lǐng)導(dǎo)那殷切的眼神和黑黑的眼圈,我用力地點了點頭,一個猛子扎進(jìn)代碼的汪洋大海,迅速游至中控鎖的休眠和喚醒之地。
代碼的設(shè)計思路總是簡單而且正確的。中控鎖進(jìn)入休眠之前,設(shè)置了兩個喚醒條件:①機械解鎖信號的上升沿中斷喚醒MCU;②定期檢查遙控鑰匙信號的定時器周期喚醒MCU。當(dāng)出現(xiàn)有效的機械鎖信號時,或者檢測到有效的遙控信號時,中控鎖禁止這兩個喚醒條件并返回正常狀態(tài)。
在具體的代碼實現(xiàn)上,把中控鎖的休眠模式處理分成了兩部分:中斷服務(wù)程序和循環(huán)體。中斷由機械鎖解鎖開關(guān)信號觸發(fā),執(zhí)行完ISR后先返回循環(huán)體,再退出休眠模式。循環(huán)體中反復(fù)休眠和臨時喚醒,在臨時喚醒期間通過三級濾波機制檢查是否存在有效的遙控鑰匙信號。存在遙控信號時,這個循環(huán)體層層地通過三級濾波后,退出休眠模式。
邏輯上清晰,代碼也很簡單,我反反復(fù)復(fù)檢查了幾遍,沒看出個子丑演卯來,就準(zhǔn)時準(zhǔn)點地下班回家了。
第二天上班后,我還沒在工位上坐定,還沒來得及平復(fù)一下自己的燥熱勁頭,領(lǐng)導(dǎo)就帶著于我少有的笑意貓過來了。不等他開口,老實巴交的我就主動匯報了檢查代碼后沒有發(fā)現(xiàn)什么問題的情況,no news is good news,但是此時代碼沒有問題就是大問題??!
話音甫落,領(lǐng)導(dǎo)臉上還沒來得及展開的笑容就凝固不動了,他張著口,默默地看著我。
我們倆就這么站著,不說話,就十分地不美好!
城府甚深的領(lǐng)導(dǎo)終于沒有說話,他不動聲色地安排了一位同事搭建了中控鎖的測試系統(tǒng),讓他反復(fù)測試休眠、喚醒的情況。
3
測試的方法很簡單,把電流表串進(jìn)中控鎖的供電線上,看著電流下降到休眠電流范圍后,便操作一下遙控鑰匙,或者給一個機械解鎖信號,看能否執(zhí)行解閉鎖操作。
我在一邊冷眼觀察著測試人員的操作,心情竟然無比地矛盾,既希望他快點測出來不能正常喚醒的故障,又盼著最好測試不出來問題。
大半天下來,中控鎖反反復(fù)復(fù)地正常休眠、被正常喚醒,我的心也反反復(fù)復(fù)地七上八下,百般煎熬。后來,測試人員的電話響了,我默默轉(zhuǎn)身離開,同時發(fā)現(xiàn)自己竟然更加惶恐了!
剛剛坐到電腦前,測試人員就大呼小叫著跑了過來:“寂寞君,問題再現(xiàn)了!”我就像屁股上安了個彈簧一樣,一下子被凳子彈了起來,待我三步并作兩步跨到測試臺前時,領(lǐng)導(dǎo)也已經(jīng)聞聲迅速趕來。
我抓著遙控鑰匙一邊操作,一邊注視著電流表的讀數(shù),電流一直穩(wěn)定在2毫安左右。
問題確認(rèn)了,確實死活也喚不醒了。
我又一屁股栽倒在凳子上,抬起頭來,正趕上領(lǐng)導(dǎo)意味深長的目光。我抿著嘴笑了笑,剛想說點什么,測試臺上測試人員的電話又嗡嗡響了起來。
聽著熟悉的手機鈴聲在耳邊縈繞,看著手機在桌子上震動個不停,我回想起測試人員剛才打電話時的情景,暗鈍的思維再度轉(zhuǎn)動起來:之前只是考慮了機械解鎖單獨觸發(fā)喚醒、遙控鑰匙單獨觸發(fā)喚醒的情況,沒有考慮過遙控信號通過了前兩級濾波而此時機械解鎖信號突然觸發(fā)執(zhí)行了ISR這種罕見情況,莫非...?
測試人員剛才打電話時,由于手機的輻射,RF信號線上出現(xiàn)了若干有效的射頻位,造成RF信號通過了前兩級濾波,此時要在循環(huán)體的第三級濾波程序要等待200ms,判斷是否是遙控按鍵信號。
對于嵌入式系統(tǒng)而言,200ms是一個不容忽視的時間段,倘若測試人員在這期間試圖機械解鎖,觸發(fā)中斷執(zhí)行ISR后會發(fā)生什么呢?
我繼續(xù)扒拉開代碼看進(jìn)去,一絲寒意向我心頭襲來。
原來我在ISR中執(zhí)行了退出休眠模式的函數(shù)-ExitSleepMode(這個函數(shù)里面會禁能喚醒條件),當(dāng)ISR執(zhí)行結(jié)束后回到循環(huán)體中時,它會在200ms超時后進(jìn)入循環(huán)體第三級濾波,當(dāng)然它會發(fā)現(xiàn)不是有效的遙控按鍵信號,于是再度進(jìn)入休眠。
但是這個時候已經(jīng)禁能了喚醒條件,這就意味著它再也喚不醒了呀?。?/p>
4
人的思維真的很奇怪,本來bug明明就在眼前卻視而不見,可是一旦猜到了bug的可能原因,就立馬火眼金睛起來。
為了幫助讀者的理解,筆者給出了下面的偽代碼,相信聰慧的讀者也能看出問題所在。
void interrupt Wake_ISR(void)
{
...
ExitSleepMode();//disable wakeup event
...
}
static void ConfirmRkeWakeUp(void)
{
__delay_ms(200);
if(Rke is valid){
...
ExitSleepMode();
...
}
else{
return to sleep ;
}
}
再讓大家加深一下理解。
假設(shè)機械解鎖信號觸發(fā)ISR時,循環(huán)體正運行到RF信號的第三級濾波程序ConfirmRkeWakeUp之前,MCU會先執(zhí)行ISR,在ISR中禁能喚醒條件,然后返回循環(huán)體中執(zhí)行ConfirmRkeWakeUp。
ConfirmRkeWakeUp函數(shù)延時200ms后,判斷這200ms之間有沒有有效的遙控鑰匙信號,顯然這里是沒有的,于是Return to sleep。
但是,這個時候喚醒條件已經(jīng)被禁能了(在ISR中被禁能了),系統(tǒng)就永遠(yuǎn)不會被喚醒了。
眼尖的讀者可能會納悶,這種故障之前為什么沒有測試出來?
因為按照之前的測試條件,這種情況發(fā)生的幾率非常?。?span style="text-indent: 2em;">循環(huán)體內(nèi)執(zhí)行ConfirmRkeWakeUp函數(shù)之前需要經(jīng)過雙重RF濾波,這要求中控鎖的RF信號線上必須在較短的時間內(nèi)存在較多的符合寬度要求的RF信號位,否則就不會執(zhí)行ConfirmRkeWakeUp這個函數(shù);平時用機械鑰匙解鎖時,雖然ISR中禁能了喚醒條件,但是由于不會執(zhí)行ConfirmRkeWakeUp,ISR回到循環(huán)體之后,系統(tǒng)依然會退出休眠。
平時用遙控鑰匙解鎖時,無論期間有沒有發(fā)生機械解鎖ISR,最終都會執(zhí)行ConfirmRkeWakeUp并退出休眠。
所以,只有無效的遙控按鍵信號觸發(fā)執(zhí)行ConfirmRkeWakeUp函數(shù)時機械解鎖信號同時有效,才會觸發(fā)這種故障。
汽車生產(chǎn)線上為什么測試出來這種故障了呢?
那是因為在生產(chǎn)線上裝配了中控鎖的車太多了,產(chǎn)線后端的中控鎖模塊進(jìn)了休眠之后,用機械鑰匙喚醒時很容易被其它車的遙控鑰匙操作誤觸發(fā),導(dǎo)致循環(huán)體執(zhí)行到ConfirmRkeWakeUp上來。
但是其它車的遙控鑰匙對于本車來說是無效的,所以,根據(jù)上面的原因,故障就出現(xiàn)了。
結(jié)語
定位了故障的原因之后,我不禁又洋洋得意起來,‘這么高級的bug都被我逮出來了!’。好似全然忘記了這個故障就是我自己埋下的坑。
事后,領(lǐng)導(dǎo)讓我寫總結(jié)原因時,我意識到了這種故障背后的深層次原因在于中斷和循環(huán)體的不登對:中斷服務(wù)程序結(jié)束后必然回到主循環(huán)體中被中斷的位置;嵌入式系統(tǒng)中,中斷可能發(fā)生在主循環(huán)體的任何位置;ISR和循環(huán)體程序之間不存在執(zhí)行時間的先后順序關(guān)系,先后次序不同可能導(dǎo)致不同的運行結(jié)果;由于ISR發(fā)生時循環(huán)體可能會執(zhí)行到任何代碼位置上,如果代碼設(shè)計地不嚴(yán)謹(jǐn)就有可能會造成問題!
煮熟的鴨子最終沒有飛走,領(lǐng)導(dǎo)又笑意盈盈地踱了過來,跟我討教故障的原因??粗潜患磳⒌绞值莫劷鸸奈璧赜行┠[脹的胸膛,我一邊默默地念叨著‘狡兔死走狗烹,飛鳥盡良弓藏’,一邊悠悠地說了一句總結(jié)陳詞:中斷和主循環(huán)不登對,系統(tǒng)會一直休眠久睡!
評論