文章目錄
- STM32F4 HAL庫串口死鎖問題調試記錄
- 調試方法
- 結果
- 分析
- 解決
- 方法一:
- 方法二:
STM32F4 HAL庫串口死鎖問題調試記錄
使用方法:通過串口DMA固定周期向外發送數據,同時開啟串口DMA接收用于接收其它板卡發來的數據。
問題:在程序運行一段時間后會出現程序不再接收數據的情況,但向外發送數據正常。
分析:一開始認為是觸發了串口ORE錯誤導致的這個問題呢,但奇怪的是并沒有觸發串口錯誤中斷的回調函數,通過進一步分析排查發現是由__HAL_LOCK()
引起的,而串口ORE錯誤是在觸發這個問題之后出現的。
調試方法
- 通過如下函數開啟串口DMA接收
HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);
- 通過如下函數以10ms周期定時向外發送數據
HAL_UART_Transmit_DMA(&JOYSTICK_HUART,(uint8_t*)&joystick_display_frame,sizeof(JoystickDisplayFrame_t));
- 實現串口DMA+IDLE中斷接收回調函數
uint32_t huart2_err_cnt = 0;
uint32_t aaa[10] = {0};
HAL_StatusTypeDef huart2_status1;
HAL_StatusTypeDef huart2_status2;uint32_t tx_lock_cnt = 0;
uint32_t tx_unlock_cnt = 0;
uint8_t rx_irq_flag = 0;void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {uart_rx_event_cnt++;huart2_status1 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);if(huart2_status1 != HAL_OK) {huart2_err_cnt++;rx_irq_flag = 1;/* 根據相應的錯誤類型清除相應的錯誤標志 */if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_ORE) != RESET) {__HAL_UART_CLEAR_OREFLAG(&JOYSTICK_HUART); //清除ORE溢出錯誤標志,(讀SR然后讀DR,這里用于清除串口錯誤標志)aaa[0]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_FE) != RESET) {__HAL_UART_CLEAR_FEFLAG(&JOYSTICK_HUART); //清除FE幀錯誤標志aaa[1]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_PE) != RESET) {__HAL_UART_CLEAR_PEFLAG(&JOYSTICK_HUART); //清除PE奇偶校驗錯誤標志aaa[2]++;} else if(__HAL_UART_GET_FLAG(&JOYSTICK_HUART, UART_FLAG_NE) != RESET) {__HAL_UART_CLEAR_NEFLAG(&JOYSTICK_HUART); //清除NE噪聲錯誤標志aaa[3]++;}huart2_status2 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);}
}
- 實現串口錯誤中斷回調函數
uint32_t uart_error_cnt = 0; //記錄是否進入串口錯誤中斷
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {uart_error_cnt++;// 其它處理
}
- 改造HAL庫串口DMA發送函數來驗證問題
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{uint32_t *tmp;/* Check that a Tx process is not already ongoing */if (huart->gState == HAL_UART_STATE_READY){if ((pData == NULL) || (Size == 0U)){return HAL_ERROR;}/* Process Locked */__HAL_LOCK(huart);if(rx_irq_flag == 0) {tx_lock_cnt++;}// 省略中間的處理/* Process Unlocked */__HAL_UNLOCK(huart);if(rx_irq_flag == 0) {tx_unlock_cnt++;}/* Enable the DMA transfer for transmit request by setting the DMAT bitin the UART CR3 register */ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);return HAL_OK;}else{return HAL_BUSY;}
}
結果
下圖為出錯時對應的變量值
第一次測試結果:
第二次測試結果:
分析
- 根據上面的結果可以看出:程序在執行發送函數中的
__HAL_LOCK(huart)
串口加鎖函數后,__HAL_UNLOCK(huart)
串口解鎖函數前觸發了串口IDLE中斷,此時由于串口處于鎖定狀態,執行HAL_UARTEx_ReceiveToIdle_DMA()
函數會直接返回HAL_BUSY(即2)的狀態,導致出現了錯誤。 - 根據
aaa[0]
的值可以判定此時尚未出現串口ORE的報錯,而usart2_sr
串口狀態寄存器的值顯示當前出現了串口ORE錯誤,這個錯誤是由于后續又繼續收到數據導致的。 - 根據
usart2_cr
串口控制寄存器1的值可以看出IDLEIE中斷使能標志位現在為0,因而不會繼續觸發串口IDLE接收中斷了,自然就不會繼續執行回調函數的內容了
解決
STM32串口是支持全雙工工作的,按理說收發可以做到完全互不干擾,但是串口鎖定處理的這段時間確實會影響全雙工的性能。程序同時高頻接收和高頻發送時可能會有比較高的概率出現該問題。
方法一:
屏蔽掉HAL庫中關于串口鎖的函數(不推薦)
方法二:
觸發這種情況時添加串口解鎖處理
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {uart_rx_event_cnt++;huart2_status1 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);if(huart2_status1 != HAL_OK) {huart2_err_cnt++;JOYSTICK_HUART.RxState = HAL_UART_STATE_READY;JOYSTICK_HUART.Lock = HAL_UNLOCKED; //或者調用 __HAL_UNLOCK(&JOYSTICK_HUART);huart2_status2 = HAL_UARTEx_ReceiveToIdle_DMA(&JOYSTICK_HUART, joystick_uart_buffer, JOYSTICK_UART_BUFFER_SIZE);}
}