STM32 F103串口同時收發出現死鎖問題解決辦法

一直使用F4系列,沒有出現此類現象,也可能出現了沒有發現。最近在做和研華工控機通訊時出現串口接收問題。總結如下:

1. 使用DMA+空閒中斷未出現串口同時收發死鎖現象,但是由於研華某款工控機在與板子通信時,出現丟包現象

 

  • 工控機接收到板子發送數據正常,無丟包
  • 工控機發送數據到板子,發送正常,接收總是顯示校驗不通過,不通過時只收到部分包。
  • 板子與電腦通過串口工具相互發送數據,板子無丟包現象,串口工具無丟包現象。
  • 工控機與電腦通過串口工具相互發送數據,工控機無丟包現象,串口工具無丟包現象。

由於此項目是接手前人同事的,他之前用的是標準庫,而且標準庫官方已經不再維護,我一直用的是HAL庫。就在想難道是庫的bug嗎,但是和電腦通信很正常,放在設備上就會異常,然後是各種操作,依然無用,來來回回搞了半個月因爲這個通信問題。最後通過板子和工控機之間增加DB9轉usb的線,發現可以正常通信,本想就這樣結束這個問題。因爲這樣已經排除了軟件原因,只能是硬件行爲影響到軟件的收發。

最終幾天過後,另外的同事找到研華工控機技術,查閱工控機的串口芯片手冊,發現了重大問題,是因爲該芯片緩存的FIFO大小是16,而我們出現問題的幀長度是40,硬件將其分3次發送(16+16+8),但是板子的空閒中斷又認爲是3次空閒中斷,所以包不完整導致校驗不通過。但是項目已經臨近,沒有再做修改,只計劃在新一版上修改。

2. 

這裏又到了新的一版,由於上面的串口緩存FIFO問題,這樣我就考慮不能再使用高效方便的DMA+串口中斷方式了。直接使用串口普通中斷進行接收工控機數據。問題又來了,出現現象如下:

  • 板子接收串口工具下發的數據然後再返回該數據,兩者可以正常通信。
  • 板子週期30ms主動發送數據,同時接收數據,串口工具發送一定次數後板子不在收到數據,且次數不定。

這下GG,嘗試了多種方式依然還是無法解決此類現象(換中斷與發送方式,重新制作新的測試工程,嘗試了更換更新的庫,參照網上使能串口錯誤回調函數在裏面進行清除各種錯誤標誌等等),只是有長有短,沒辦法根治。發現問題是在

HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1)

出現問題的時候,上面函數一致返回 HAL_BUSY,該函數如下:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pRxBuffPtr = pData;
    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

以爲是 if (huart->RxState == HAL_UART_STATE_READY)此語句判斷不過導致,在串口接收完成回調函數裏增加強制改變該串口狀態爲HAL_UART_STATE_READY,依舊無法解決問題。然後看到網上有人在串口錯誤回調函數裏進行清除錯誤操作,照例使用,該函數改爲

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart); 
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_ErrorCallback could be implemented in the user file
   */ 
		uint32_t isrflags   = READ_REG(huart->Instance->SR);//手冊上有講,清錯誤都要先讀SR
	if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
	{
			READ_REG(huart->Instance->DR);//PE清標誌,第二步讀DR
			__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);//清標誌
	}
	if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
	{
			READ_REG(huart->Instance->DR);//FE清標誌,第二步讀DR
			__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);
	}
	
	if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
	{
			READ_REG(huart->Instance->DR);//NE清標誌,第二步讀DR
			__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);
	}        
	
	if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
	{
			READ_REG(huart->Instance->CR1);//ORE清標誌,第二步讀CR
			__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
	} 
	if (HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE)
    __HAL_UART_FLUSH_DRREGISTER(huart);	
	if(huart->ErrorCode&HAL_UART_ERROR_ORE)
	{
		__HAL_UART_CLEAR_OREFLAG(huart);
	}
	if(__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE) != RESET)
	{
		__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE); //清除溢出中斷
		HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1);
	}

	if(__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE) != RESET)
	{
		__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_RXNE);
	//rebuf[num++] = USART_ReceiveData(USART2); 讀取串口數據
	}

}

看到這麼多錯誤該清除的都清除了,心想應該沒問題了,但是燒寫後問題依舊存在。

最後的最後,有幸看到資料,解決此類問題。

main函數主要部分如下:

/* USER CODE BEGIN PV */
extern uint32_t cnt;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  sprintf((char*)sendbuff,(const char*)"recv data cnt %d\r\n",cnt);
	  
	  HAL_UART_Transmit(&huart2,sendbuff,strlen((char*)sendbuff),100);
	  HAL_Delay(20);
  }
  /* USER CODE END 3 */

主要功能就是初始化串口和時鐘,然後再while循環裏打印串口中斷進入的次數。

在usart.c文件中,最重要的兩部,

在usart.h文件中

原因分析:問題出現在串口接收中斷函數中,鎖住了串口,但是後面那句已經解鎖了啊,但是爲什麼還是鎖住了呢

按照上面進行解鎖操作後,沒有出現互鎖現象了。

參考博客:

https://blog.csdn.net/mickey35/article/details/74255041

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章