关于CubeMX的串口全双工接收发送锁死的问题

一,现象描述

在使用STM32 CubeMX生成的工程中用到了串口2,发送开启了DMA模式,接收使用中断,每次接收一个字节,在回调函数中调用HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1)接收开启函数。刚开始工作还算正常,但运行一段时间后(时间长度随机),调试窗口上串口数据不再更新,使用示波器检查引脚仍有数据接收到,STM32整体运行正常。初步判断,串口2发生异常错误。

二,问题分析

1,初始化配置检查

这里写图片描述
这里写图片描述
这里写图片描述
好像没有什么问题

2,代码检查

MX自动生成的初始化代码就不贴了。


main函数中开启接收:

void main(void)
{
    ...
    if(HAL_UART_Receive_IT(&huart2,(u8 *)&i,1) != HAL_OK)   Error_Handler();
    ...
}

串口接收回调函数:

if(huart->Instance == USART2)
{
    ...
    HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1);
    ...
}

DMA发送函数:

void dma_send(unsigned char *buffer,unsigned int length)
{
    //等待上一次的数据发送完毕
    while(HAL_DMA_GetState(&hdma_usart2_tx) == HAL_DMA_STATE_BUSY);

    /* 关闭DMA */
    __HAL_DMA_DISABLE(&hdma_usart2_tx);

    //开始发送数据
    HAL_UART_Transmit_DMA(&huart2,buffer,length);
}

因为串口发送和接收是分离的,感觉代码没什么问题,实践证明之前确实可以运行,但串口死机的情况仍会出现。

3,硬件仿真检查

先是根据外部状态观察,每次死机都紧接着发送之后出现。
上网查找对应问题,发现确实有类似串口发送接收互锁的问题,貌似可以在重新初始化串口解决。
根据论坛的说法,修改回调函数为以下代码:

if(huart->Instance == USART2)
{
    u16 i = 0;
    ...
    while(HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1) != HAL_OK )   
    {
        i++;
        if( i > 10000 )
        {
            huart2.RxState = HAL_UART_STATE_READY;
            MX_USART2_UART_Init(&huart2);
            i = 0;
        }
    }
    ...
}

但是问题仍然存在,并且变成整体运行卡死,初步判断卡死在该循环内
使用ST-LINK进行硬件调试,保持debug状态全速运行,等到串口死机后,暂停运行查看。
首先确认 HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,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 == 0)) 
    {
      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 Error Interrupt: (Frame error, noise error, overrun error) */
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

    /* Enable the UART Parity Error and Data Register not empty Interrupts */
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY; 
  }
}

一开始我以为返回 HAL_BUSY 是因为 hart->RxState != HAL_UART_STATE_READY,但多次观察后发现在__HAL_LOCK(huart);这个宏定义之后直接函数就返回了。
__HAL_LOCK(huart)的源码如下:

  #define __HAL_LOCK(__HANDLE__)                                           \
                                do{                                        \
                                    if((__HANDLE__)->Lock == HAL_LOCKED)   \
                                    {                                      \
                                       return HAL_BUSY;                    \
                                    }                                      \
                                    else                                   \
                                    {                                      \
                                       (__HANDLE__)->Lock = HAL_LOCKED;    \
                                    }                                      \
                                  }while (0U)

原来是因为串口一直加锁失败导致串口死机。

三,解决办法

根据这个原因再次修改回调函数:

if(huart->Instance == USART2)
{
    u16 i = 0;
    ...
    while(HAL_UART_Receive_IT(&huart2,(u8 *)&rx_data_2,1) != HAL_OK )   
    {
        i++;
        if( i > 10000 )
        {
            huart2.RxState = HAL_UART_STATE_READY;
            __HAL_UNLOCK(&huart2);
            i = 0;
        }
    }
    ...
}

重新进行测试,到目前为止还没有观察到死机的现象发生。


小结:这种方法虽然可以暂时解决这个锁死的问题,但假设ST官方库文件没有问题,那么可能导致收发数据丢包。但按道理既然是全双工串口,就不应该存在发送和接收互锁的情况,况且大多数情况下,发送和接收总有一个是几乎实时进行的,怎么可能不冲突呢?是否还有更好的解决办法,还望诸位大神和ST工程师能在此指导,谢谢!

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值