在一項目中,使用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接收中斷響應函數如下:
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數據丟棄。
修改如下:
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、任意時候都要考慮出錯處理。
原文:https://blog.csdn.net/origin333/article/details/49992383