本文作者:Miler Shao 某日某工程師跟我交流,他在使用STM32F031的芯片開發(fā)一款電子產(chǎn)品. MCU跟外界有個UART串口通訊,外界經(jīng)常會不定期地傳送一串固定數(shù)量的數(shù)據(jù)包過來。令他郁悶的是,在從外界接受數(shù)據(jù)時偶爾會出現(xiàn)數(shù)據(jù)丟失一個兩個的,尤其波特率高的時候容易發(fā)生。 經(jīng)過跟他深入溝通,了解到STM32F031跟外界有UART數(shù)據(jù)通信并開啟了RXNE接收中斷,還有對外的AD采樣動作,通過定時器定時觸發(fā)AD轉(zhuǎn)換,并開啟了ADC1的轉(zhuǎn)換完成中斷。AD觸發(fā)間隔為2秒。再就是些其它對外的GPIO操作的東西。他陳述當(dāng)波特率低于9600,甚至更低時就很難遇到丟包的現(xiàn)象,只要當(dāng)波特率達(dá)到115200甚至更高時,就比較容易丟包,經(jīng)常丟一個兩個不等,波特率越高越容易丟。 客戶給USART1的時鐘源配置的是48M系統(tǒng)時鐘。按理說,STM32F0芯片的UART的波特率跑個200K是很輕松的事。讓他用示波器在芯片RX腳監(jiān)測外界傳輸過來的信號,當(dāng)外界發(fā)送方的波特率即使在200K左右時波形還是很干凈漂亮,看來不存在信號畸變的問題。 因?yàn)樗劦介_啟了UART RX中斷和ADC的EOC中斷,我懷疑他的中斷優(yōu)先級配置可能有問題。察看其代碼后,發(fā)現(xiàn)關(guān)于UARTTX/RX中斷與ADC的EOC中斷優(yōu)先級一樣的 。 看到這里,基本算是找到原因了,只待進(jìn)一步驗(yàn)證。 外界不定期通過UART發(fā)送數(shù)據(jù)給MCU,當(dāng)它收到一個數(shù)據(jù)本該通過RX中斷請求去讀取數(shù)據(jù)時,如果此刻碰上ADC的EOC中斷服務(wù)程序剛剛開始或正在執(zhí)行途中,由于二者優(yōu)先級一樣,那UART的RX 中斷就得至少等待ADC中斷服務(wù)程序繼續(xù)執(zhí)行到彈棧前的時間。若在這個等待期間內(nèi)UART又收到了第二個數(shù)據(jù)甚至更多,那就會發(fā)生溢出導(dǎo)致數(shù)據(jù)丟失。 那為什么會只是偶爾發(fā)生而且波特率高更容易發(fā)生呢?這也不難理解。 波特率高意味著傳送速度快,相應(yīng)的每個字符的傳送時間就短,即收到一個字符后,下一個字符來得也快。而每次的ADC的中斷程序執(zhí)行時間是相對固定的,最糟糕的情形就是產(chǎn)生UART RX中斷請求時碰到EOC中斷服務(wù)程序剛剛開始,這樣等待時間最長。在UART接收到數(shù)據(jù)等待ADC中斷釋放CPU期間,新的數(shù)據(jù)來得越快,丟數(shù)據(jù)的幾率就越高。當(dāng)然了,不是每次都是碰到那個最糟糕的情形,最好的情形就是碰上ADC中斷服務(wù)程序剛好執(zhí)行完畢。 反過來講,如果UART傳輸波特率比較低,意味著單個字符傳輸時間相對比較長。碰到ADC中斷服務(wù)程序先得到響應(yīng)情況下,或許等人家執(zhí)行完了再來取“待取走”的數(shù)據(jù)還來得及,尤其不在最糟糕的情形下。 當(dāng)該工程師將UART RX中斷優(yōu)先級配置為高于ADC的中斷優(yōu)先級后就再沒那個麻煩了。 順便說說上面那紅色語句“等待ADC中斷繼續(xù)執(zhí)行直到彈棧前”。 這句紅色的話意思是說,在上面情況下,UART中斷請求等待EOC中斷運(yùn)行到執(zhí)行POP之前的時刻就可得到響應(yīng)而去執(zhí)行UART RX中斷服務(wù)程序,并不急著執(zhí)行EOC中斷的POP彈棧動作,隨之的UART中斷服務(wù)程序也無需壓棧操作,UART中斷程序執(zhí)行完畢后再回來做彈棧動作,然后回到主循環(huán)的中斷處接著運(yùn)行。這就是平常所說的咬尾中斷。不難看出,這樣可以大大提升中斷響應(yīng)速度。具體到本案,這個咬尾操作一定程度上減少了丟碼機(jī)會。  既然提到了咬尾中斷,可能很多人聽說過晚到中斷。所謂晚到中斷,簡單點(diǎn)說就是低優(yōu)先級中斷服務(wù)程序正在壓;騽倝簵M戤厱r發(fā)生更高優(yōu)先級的中斷請求,高優(yōu)先級中斷不再做PUSH壓棧操作,等到低優(yōu)先級中斷壓棧完畢即直接運(yùn)行高優(yōu)先級中斷服務(wù)程序,隨后再返回來接著執(zhí)行低優(yōu)先級中斷服務(wù)程序,之后再做POP彈棧操作。 前面提到的STM32F0 的兩個中斷優(yōu)先級相同情況下,都是假定中斷請求在時間上錯開了的情況。如果二者同時到達(dá),那CPU先響應(yīng)哪一個呢?就看二者在中斷矢量表的序號,誰的序號小就先響應(yīng)誰。
另外,玩過CORTEX M3/M4內(nèi)核MCU的人,比方STM32F1,STM32F2,STM32F3,STM32F4等芯片的人可能會發(fā)現(xiàn),CORTEX M0 內(nèi)核的MCU的中斷管理跟其它CORTEX M3/M4內(nèi)核的在中斷優(yōu)先級管理上是有差異的。 M3/M4的MCU在中斷優(yōu)先級做分組管理,分搶占優(yōu)先級和響應(yīng)優(yōu)先級。只有強(qiáng)占優(yōu)先級高的中斷請求才可以打斷低搶占優(yōu)先級的中斷服務(wù)程序;搶占優(yōu)先級相同的情況下,高響應(yīng)優(yōu)先級的中斷請求頂多可以優(yōu)先獲得響應(yīng)權(quán)。而M0內(nèi)核芯片的中斷優(yōu)先級不再做分組管理,誰的優(yōu)先級高就優(yōu)先響應(yīng)并可打斷低優(yōu)先級的中斷服務(wù)程序。 當(dāng)在系統(tǒng)里開啟多個中斷事件時,要合理安排各中斷源的優(yōu)先級,有些時候可能還需精心安排。對于初學(xué)者,因?yàn)橹袛鄡?yōu)先級問題處理不當(dāng)而導(dǎo)致麻煩的情況時有發(fā)生。再就是對于中斷服務(wù)程序,如果不是必需,代碼盡量精簡,不要累贅,能放到中斷外部處理的就盡量放到外部去。 |