一、發現問題
用STMG070的4個串口中兩個串口實時通信時,偶發某個串口通信掛掉,進入不了接收中斷函數,但能進入接收回調函數,另一個串口通信正常,其他程序正常運行?
二、分析定位問題
起初,以為是多串口通信,導致搶占資源,頻繁訪問中斷,導致通信沖突;
于是,在接收回調函數中,各接收中斷函數加入if, else if條件,同一時間只能進入一個串口接收中斷,但還是會偶發串口通信掛掉;
然后,網上查資料,看到HAL庫的接收中斷里面有加鎖、解鎖操作,數據量大會導致串口鎖死,進入串口接收中斷函數,STM32Cube_FW_G0_V1.6.0版本里面沒有加鎖;
后來,看到有說overrun導致過載溢出錯誤,串口接收死掉,仿真檢測當串口通信掛掉時,果然overrun被置位,這里終于定位到了問題。
三、解決問題
overrun標志,就是狀態寄存器ISR寄存器的bit3:ORE,如下圖所示:

提示:可以通過OVRDIS關閉ORE檢測,如下圖所示:

三種解決方案(推薦第3種):
第一種:在串口故障回調函數中檢測ORE標志,如果被置位,則清除,重新打開串口接收中斷,代碼如下:

通過檢測進入串口故障回調函數中,串口ORE置位清除的次數,發現進入幾次之后,后邊就進不來了,不知什么原因?
第二種:STM32CubeMX串口配置中默認overrun默認使能,關閉該使能,但有丟包的風險(該方法未嘗試)。
第三種:定時500ms,檢測幾個串口的ORE是否置位,置位則清除ORE標志,重新打開中斷,這個比較穩定靠譜一些,即使串口接收回調函數異常,也不影響清除ORE標志。代碼如下:
//定時檢測OREvoid PS_Modules_Uart_Clear_ORE(void){static uint16_t su16OreCnt = 0;su16OreCnt++;if(su16OreCnt > 500){su16OreCnt = 0;if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET){__HAL_UART_CLEAR_OREFLAG(&huart1);HAL_UART_Receive_IT(&huart1,(uint8_t*)&rx, sizeof(rx));}if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_ORE) != RESET){__HAL_UART_CLEAR_OREFLAG(&huart2);HAL_UART_Receive_IT(&huart2,(uint8_t*)&rx, sizeof(rx));}if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET){__HAL_UART_CLEAR_OREFLAG(&huart3);HAL_UART_Receive_IT(&huart3,(uint8_t*)&rx, sizeof(rx));}if(__HAL_UART_GET_FLAG(&huart4, UART_FLAG_ORE) != RESET){__HAL_UART_CLEAR_OREFLAG(&huart4);HAL_UART_Receive_IT(&huart4,(uint8_t*)&rx, sizeof(rx));}}}
串口2和串口3通信壓力測試2小時,通信均正常。
通過打印清除次數,發現串口3清除了3次,串口2清除了4次。
四、為什么會出現overrun?
還是要看這個ORE位:

當RXFF=1時,當移位寄存器中正在被接收到的數據轉移到USART_RDR寄存器,該位由硬件設置。
當RXFF=1數據被轉移到USART_RDR寄存器未完成時,又來一個數據,則發生過載溢出,ORE置位。
STM32F1中這樣解釋:

軟件序列先讀SR,再讀CR,可將其清零。
STM32G070也可通過串口中斷標志位清除寄存器,將ORE標志清除,如下圖所示:

以上就是今天的分享,如果有需要查看原圖、代碼的小伙伴,請點擊底部“閱讀原文”進行下載。

END
作者:liao6