串口是MCU最經(jīng)常使用的外設之一,我遇到過多起串口在使用過程中出現(xiàn)死掉的實際案例。
這種問題在測試階段如果發(fā)現(xiàn)了還好,一旦批量出去在現(xiàn)場發(fā)生就更加麻煩了。今天說的死掉,其實真實現(xiàn)象是MCU一直在不停的進串口中斷,導致其他代碼無法正常執(zhí)行。
遇到該問題的罪魁禍首是串口Overrun,即溢出所致。Overrun其實很好理解,串口在接收數(shù)據(jù)的時候,每一字節(jié)數(shù)據(jù)經(jīng)移位寄存器到數(shù)據(jù)寄存器,所謂溢出就是指,上一個字節(jié)的數(shù)據(jù)在數(shù)據(jù)寄存器還沒有被讀取走,新的1字節(jié)數(shù)據(jù)又已經(jīng)被移入到移位寄存器的現(xiàn)象。以STM32F030為例,其他家的MCU也類似,Overrun錯誤標志位位于USART_ISR寄存器中。
Overrun現(xiàn)象我們可以這么產(chǎn)生:在串口中斷服務函數(shù)剛開始處加一個斷點,debug全速運行,通過串口調(diào)試助手一下發(fā)送2個字節(jié)的數(shù)據(jù),這時就會出現(xiàn)Overrun,原因就是進入到斷點處還沒有去讀數(shù)據(jù),但是新的數(shù)據(jù)又來了。
手冊中描述當此標志位置位時,RDR寄存器中的數(shù)據(jù)沒有丟失,我們可以看到RDR內(nèi)容就是第一個字節(jié)0x31。
默認情況下,Overrun檢測功能時開啟的,USART_CR3寄存器的OVRDIS位可以把該功能關掉。
當把溢出檢測功能給關掉,要注意的是新來的數(shù)據(jù)會覆蓋USART_RDR寄存器中的數(shù)據(jù)。
我們在代碼串口初始化中把溢出功能關閉,同樣做之前的實驗,這時可以看到RDR的值就不是之前的0x31,而是0x32了,ISR里ORE標志位也不會置位了。
出現(xiàn)死掉的問題是因為來了ORE中斷,但是中斷服務函數(shù)沒有去處理所致,所以就會一直進中斷。
其實解決該問題的方法也非常簡單,就是在中斷服務函數(shù)里加入該標志位的清除操作即可?;蛘吒纱嗑桶岩绯龉δ荜P閉不使用也行。
MCU里Overrun這個標志本來目的是為了提醒數(shù)據(jù)傳輸過程中出錯了,接收端需要通知發(fā)送端重傳。但是實際應用中大家一般也不這么用,為了保證串口數(shù)據(jù)通信的正確性,一般都會在應用層上加上數(shù)據(jù)幀的校驗,有了校驗其實用不用溢出功能也無所謂了。
最后強調(diào)一下:如果MCU開啟了Overrun功能,一定要在中斷服務函數(shù)里加入清溢出標志的操作。不加Overrun標志位處理大部分情況下也許不會出問題,但是一旦溢出可就麻煩大了。