[首先簡單研究一下什么時候會出現(xiàn)overrun的問題,配置正常的HAL串口中斷接收如下 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* judge interrupt source */
if(huart ->Instance == USART2)
{
//send data to huart1 from huart2
HAL_UART_Transmit(&huart1, (uint8_t*)recv_buf, 3,0xFFFF);
//enable RX IT
HAL_UART_Receive_IT(huart, (uint8_t*)recv_buf, 3);
}
} [
image-20210530135826312.png (10.37 KB, 下載次數(shù): 52)
下載附件
2021-6-1 23:54 上傳
可以看出,分三種情況:發(fā)送數(shù)據(jù)=接收緩存,正常 發(fā)送數(shù)據(jù)<接收緩存,BUF未滿,要等待下一次數(shù)據(jù)到來填滿緩存才會觸發(fā)中斷 發(fā)送數(shù)據(jù)>接收緩存,BUF已滿,多余部分被截斷,和下一次數(shù)據(jù)一起觸發(fā)中斷
[其中,觸發(fā)兩次接收緩存溢出的時候,就會進入Overrun Error。 [比如說BUF長度為3,連續(xù)發(fā)兩次長度為4的數(shù)據(jù),就會進入ORE;蛘哒f第一次發(fā)送長度為2的數(shù)據(jù),然后接著發(fā)送兩次長度為3的數(shù)據(jù),也是會觸發(fā)ORE的,后續(xù)的兩次長度為3的數(shù)據(jù)因為有之前一條長度為2的鋪墊,都是視作BUF溢出的數(shù)據(jù)。但是如果發(fā)送一次長度為4的數(shù)據(jù)之后,后續(xù)發(fā)長度一條長度為2的數(shù)據(jù),就會進入正常狀態(tài),是有彈性的。彈性限度為BUF長度的兩倍,也就是說發(fā)送一次長度為6的數(shù)據(jù),會立即進入ORE。 [ORE狀態(tài)下,串口進入ORE溢出中斷,無法繼續(xù)接收數(shù)據(jù),但是可以發(fā)送數(shù)據(jù),相當(dāng)于接收數(shù)據(jù)不會觸發(fā)中斷了。 [對于ORE溢出常用解決方案有下: [無論哪一種方法都要理解HAL_UART_RxCpltCallback的實現(xiàn)原理: HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中斷處理函數(shù)) UART_Receive_IT(UART_HandleTypeDef *huart) (接收函數(shù)) HAL_UART_RxCpltCallback(huart)(中斷回調(diào)函數(shù))
[從上到下,層層封裝和調(diào)用。 - void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
- {
- uint32_t isrflags = READ_REG(huart->Instance->SR);
- uint32_t cr1its = READ_REG(huart->Instance->CR1);
- uint32_t cr3its = READ_REG(huart->Instance->CR3);
- uint32_t errorflags = 0x00U;
- uint32_t dmarequest = 0x00U;
- /* If no error occurs */
- errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
- //close ORE check
- //errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_NE));
- if (errorflags == RESET)
- {
- /* UART in mode Receiver -------------------------------------------------*/
- if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
- {
- UART_Receive_IT(huart);
- return;
- }
- }
復(fù)制代碼[ 想要關(guān)閉ORE中斷,將errorflags中的USART_SR_ORE 這一位去掉即可,相當(dāng)于對ORE錯誤不予檢查,經(jīng)過實際的測試是可行的,實際的表現(xiàn)就是出現(xiàn)ORE溢出之后,串口只接受固定BUF長度的數(shù)據(jù),對于溢出的數(shù)據(jù)直接丟棄,但是還需要考慮ORE溢出之后,那個時刻中BUF肯定是有數(shù)據(jù),但是由于沒有清除BUF,后續(xù)的數(shù)據(jù)直接進來會導(dǎo)致錯位,因此想要真正實現(xiàn)完美的溢出數(shù)據(jù)丟棄處理且不影響后續(xù)的數(shù)據(jù),是要在程序中加入清除BUF的指令的。但是這些方法都會影響HAL庫的底層代碼,包括去掉USART_SR_ORE 這一位,這也是STM32IDE的一點的不好的地方,底層文件是沒有用戶代碼入口的,修改IOC文件之后,一切都會回到最初的起點。 [ 其次就是雙重緩存,具體的就是將BUF長度設(shè)置為1,這樣子每一次收到一個字節(jié)的話都會調(diào)用回調(diào)函數(shù),為什么長度長度大于1的時候會發(fā)生ORE,而1的時候可以接受很長的數(shù)據(jù)而不ORE呢,我猜是因為1的時候?qū)⒛承┪恢梦涣,間接關(guān)閉了ORE,然后用戶自己定義BUF區(qū),一個一個去讀取寫入,這個方法還可以實現(xiàn)不定長的接收,設(shè)置數(shù)據(jù)結(jié)束幀位就可以了。缺點也很明顯,當(dāng)數(shù)據(jù)很長的時候,中斷要進入很多次,有點占資源了。 [用戶回調(diào)函數(shù)HAL_UART_RxCpltCallback只有接收到設(shè)定的BUF的長度才會調(diào)用一次,而不是有字節(jié)就調(diào)用,實現(xiàn)的方法在UART_Receive_IT函數(shù)中: - if (--huart->RxXferCount == 0U)
- {
- /* Disable the UART Data Register not empty Interrupt */
- __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
-
- /* Disable the UART Parity Error Interrupt */
- __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
-
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
- __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
-
- /* Rx process is completed, restore huart->RxState to Ready */
- huart->RxState = HAL_UART_STATE_READY;
-
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
- /*Call registered Rx complete callback*/
- huart->RxCpltCallback(huart);
- #else
- /*Call legacy weak Rx complete callback*/
- HAL_UART_RxCpltCallback(huart);
- #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
復(fù)制代碼
[if (--huart->RxXferCount == 0U)這一句,每次字節(jié)來都會調(diào)用UART_Receive_IT,然后RxXferCount減1,這個變量就是一開始設(shè)置的BUF接收長度,只有等這個到零了,才會調(diào)用HAL_UART_RxCpltCallback(huart)。 [最終,因為在項目中串口是需要來高頻率接收固定長度的測量數(shù)據(jù)的,但是在之前要用串口發(fā)送上電指令,這個指令會返回一個和測量數(shù)據(jù)長度不同的ASK信號,處理方法就是在上電之后,再開啟串口接收,這樣子就可以設(shè)置BUF長度為固定長度,同時稍微降低測量的頻率,讓串口接收有足夠的時間,避免ORE的產(chǎn)生。
|