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循环里打印串口中断进入的次数。

STM32 F103串口同时收发出现死锁问题解决办法_第1张图片

STM32 F103串口同时收发出现死锁问题解决办法_第2张图片

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

STM32 F103串口同时收发出现死锁问题解决办法_第3张图片

在usart.h文件中

原因分析:问题出现在串口接收中断函数中,锁住了串口,但是后面那句已经解锁了啊,但是为什么还是锁住了呢

STM32 F103串口同时收发出现死锁问题解决办法_第4张图片

按照上面进行解锁操作后,没有出现互锁现象了。

STM32 F103串口同时收发出现死锁问题解决办法_第5张图片

参考博客:

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

你可能感兴趣的:(嵌入式)