1. 程式人生 > >STM32串列埠中斷卡死主迴圈

STM32串列埠中斷卡死主迴圈

問題分析如下:
1、程式USART2不停接收並處理串列埠資料,波特率115200;
2、主迴圈卡死;
3、USART1中斷及TIM2中斷響應函式執行正常;(USART1及TIM2中斷優先順序均比USART2高)
4、出現現象後,拔掉USART2的接收資料線,現象不能回覆正常;
5、出現現象後,拔掉後再插入USART2的接收資料線,現象不能回覆正常;
6、並未出現HardFault現象;

基於以上4點,可能原因如下:
1、USART2接收中斷標誌沒有清除;
2、堆疊資料溢位,導致程式異常;
3、USART2中斷重入導致異常;
4、USART2中斷函式被異常響應;
5、USART2中斷ERR;
對於以上可能原因一一分析:
1、中斷接收標誌清楚問題:
(1)USART2接收中斷響應函式如下:
[cpp] view plain copy
void USART2_Istr(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
USART_ClearFlag(USART2, USART_FLAG_RXNE);
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
Data = USART_ReceiveData(USART2);
//Process Data
}
}

(2)出現現象後,通過Usart1中斷獲取到如下資訊:
a. USART_GetITStatus(USART2, USART_IT_RXNE) == RESET
b. USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET
c. 執行USART_ClearFlag(USART2, USART_FLAG_RXNE)及 USART_ClearITPendingBit(USART2, USART_IT_RXNE)後無法恢復正常;
結論:與USART2 RXNE中斷標誌無關。

2、堆疊資料溢位,導致程式異常;
(1)使用2倍棧空間,問題存在,概率不會降低;
(2)使用0.5倍棧空間,問題存在,概率不會提高;
(3)使用0.25倍棧空間,程式執行進入HardFault;
結論:與堆疊無關。

3、USART2中斷重入導致異常;
(1)使用標誌法,確認出現問題時,中斷響應函式沒有重入;
結論:中斷響應函式沒有重入。

4、USART2中斷函式被異常響應;
(1)USART2中斷函式可以被正常呼叫,只是不停進入中斷響應函式,卡死主迴圈;
(2)檢查程式Map,沒發現與中斷響應函式地址相同的函式;
(3)檢查中斷向量表,沒發現異常;
結論:中斷函式沒有被異常呼叫;

5、USART2中斷ERR;
(1)關閉USART2中斷,主迴圈恢復正常;
(2)啟動USART2中斷,主迴圈卡死;
(3)獲取到DR=0x0000;
(4)USART_GetITStatus取到:RXNE=0,PE=0,TXE=0,TC=0,IDLE=0,LBD=0,CTS=0,ERR=0,ORE=0,NE=0,FE=0;
(5)通過USART_ClearITPendingBit清除CTS,LBD,TXE,TC,RXNE,IDLE,ORE,NE,FE,PE均無法恢復正常;
(6)通過USART_GetFlagStatus:
a.第一次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=1,ORE=1,NE=0,FE=0,PE=0
b.第二次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=0,ORE=0,NE=0,FE=0,PE=0
c.第三次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=0,ORE=0,NE=0,FE=0,PE=0
(7)通過USART_ClearFlag清除CTS,LBD,TXE,TC,RXNE,IDLE,ORE,NE,FE,PE均無法恢復正常;
分析:
(1)為什麼通過USART_GetITStatus獲取了所有中斷標誌,均為RESET(TC、TXE中斷沒開),還會進中斷?
(2)為什麼通過USART_ClearITPendingBit清除了所有中斷標誌,還會進入中斷?
(3)為什麼關閉USART2中斷後再次啟動它還會進入卡死狀態?
(4)為什麼通過USART_GetFlagStatus第一次和第二次讀的不一樣?而且USART_ClearFlag清掉所有Flag,也沒法恢復正常?

帶著以上幾個疑問,查看了參考手冊,才恍然大悟!如下:
(1)開啟RXNEIE,預設會同時開啟RXNE和ORE中斷。
這裡寫圖片描述
(2)必須第一時間清零RXNE,如沒及時清零,下一幀資料過來時就會產生Overrun error!
這裡寫圖片描述
(3)錯誤就是ORE導致的
出現錯誤時,讀了RXNE=0,出錯應該是上圖打勾的情況,如下

(4)如文件說明,要清除ORE中斷需要按順序讀取USART_SR和USART_DR暫存器!
那就是說USART_ClearFlag清掉所有Flag後,還必須讀一遍USART_DR暫存器!
經過測試出現問題後依次讀讀取USART_SR和USART_DR,程式回覆正常!

(5)那還有一個問題,為什麼USART_GetITStatus讀不到ORE中斷標誌?
讀USART_GetITStatus函式就知道了,只有CR3的EIE置1且SR的ORE置1,讀出來USART_GetITStatus(USART2, USART_IT_ORE) 才是 SET。
見CR3的EIE位說明。
這裡寫圖片描述

解決辦法,出現通過接收時,通過USART_GetFlagStatus讀取ORE,若不為RESET,則讀取DR資料丟棄。
修改如下:
[cpp] view plain copy
void USART2_NewIstr(void)
{
if (USART_GetFlagStatus(USART2, USART_FLAG_PE) != RESET)
{
USART_ReceiveData(USART2);
USART_ClearFlag(USART2, USART_FLAG_PE);
}

if (USART_GetFlagStatus(USART2, USART_FLAG_ORE) != RESET)
{
USART_ReceiveData(USART2);
USART_ClearFlag(USART2, USART_FLAG_ORE);
}

if (USART_GetFlagStatus(USART2, USART_FLAG_FE) != RESET)  

{
USART_ReceiveData(USART2);
USART_ClearFlag(USART2, USART_FLAG_FE);
}

if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  
{     
    USART_ClearFlag(USART2, USART_FLAG_RXNE);  
    USART_ClearITPendingBit(USART2, USART_IT_RXNE);  
    Data = USART_ReceiveData(USART2);  
}  

}

總結:
1、看文件!看文件!還是看文件!(重要的事情要說3遍)
2、庫函式用的時候,也要注意其實現,稍有不慎就可能用錯。
3、注意USART_GetFlagStatus與USART_GetITStatus的區別,還有中斷響應機制。
4、任意時候都要考慮出錯處理。