天天看點

【序列槽系列】序列槽接收 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

分享不易,點個贊再走吧☺☺☺