【串口系列】串口接收 STM32 HAL库 HardFault

问题

代码测试时发现设备进入hard_fault,通过call stack查看信息发现最后死在HAL_UART_Transmit函数。

解决

1. 调试发现huart->TxXferCount 被设置成0xFFFE,且运行不断减小,该值的含义为剩余发送字节数,看工程代码实际仅设置为1个字节,所以按照逻辑不应该出现这种情况。根据这个值初步判断应该为在中断中huart->TxXferCount 被设置为0,而返回线程模式后HAL库继续执行之前的操作,将会使huart->TxXferCount--;导致溢出为0xFFFF。接下来排查是哪个中断将这个值清零。

2. 通过仿真发现了调用关系如下

HAL_UART_IRQHandler  --》 HAL_DMA_Abort_IT --》 hdma->XferAbortCallback(hdma)  -》 UART_DMAAbortOnError --》 huart->TxXferCount = 0x00U;  当出错时,该变量会被清零,如果这时刚好在while循环内部,且huart->TxXferCount =1,那么在执行while循环体时huart->TxXferCount已经被置0,那么在退出循环体前进行了减1操作,导致值溢出,发送0xFFFF个字节(64KB),导致内存溢出。

while (huart->TxXferCount > 0U)

{

    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)

    {

        return HAL_TIMEOUT;

    }

    if (pdata8bits == NULL)

    {

        huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);

        pdata16bits++;

    }

    else

    {

        huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);

        pdata8bits++;

    }

    huart->TxXferCount--;

}

3. 新问题引入:为什么会出现HAL_DMA_Abort_IT, 当接收数据出现错误时,如果USART_CR3_DMAR使能,会失能DMAR接收,并清除uart->TxXferCount,导致出现第1步的原因。

static void UART_DMAAbortOnError(DMA_HandleTypeDef *hdma)

{

  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

  huart->RxXferCount = 0x00U;

  huart->TxXferCount = 0x00U;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)

  /*Call registered error callback*/

  huart->ErrorCallback(huart);

#else

  /*Call legacy weak error callback*/

  HAL_UART_ErrorCallback(huart);

#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

}

4. 为什么会出现数据错误呢?查看SR寄存器错误为FE(帧错误),实际串口不断在进入中断,而设备是未进行任何数据输入的,这里接收到的数据肯定是杂乱无序,不按照uart协议通信的,导致stm32检测到帧错误的问题。

排查:开发板串口接收引脚悬空,配置成上拉模式后正常。为保险起见,修改HAL库函数

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

{

  uint8_t  *pdata8bits;
  uint16_t *pdata16bits;
  uint32_t tickstart = 0U;

  /* 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);
    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Init tickstart for timeout managment */

    tickstart = HAL_GetTick();
    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    /* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */

    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {
      pdata8bits  = NULL;
      pdata16bits = (uint16_t *) pData;
    }

    else
    {
      pdata8bits  = pData;
      pdata16bits = NULL;
    }

    /* Process Unlocked */ 
    __HAL_UNLOCK(huart);
    while (huart->TxXferCount > 0U)
    {
      if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
      {
        return HAL_TIMEOUT;
      }

      if (pdata8bits == NULL)
      {
        huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);
        pdata16bits++;
      }
      else
      {
        huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);
        pdata8bits++;
      }

      if(huart->TxXferCount > 0)
      {
          huart->TxXferCount--;
      }

    }

    

    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

    /* At end of Tx process, restore huart->gState to Ready */
    huart->gState = HAL_UART_STATE_READY;
    return HAL_OK;

  }

  else
  {
    return HAL_BUSY;
  }

}

修改的地方如下,这样这个值就不会出现溢出变成65535,导致发送缓存区地址溢出:

      if(huart->TxXferCount > 0)
      {
          huart->TxXferCount--;
      }

版权声明:本文为CSDN博主「Hi,Mr.Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34672688/article/details/117478889

分享不易,点个赞再走吧☺☺☺


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hi,Mr.Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值