stm32-HAL使用usart傳送中斷判斷髮送標誌庫問題

良知猶存發表於2021-10-10

前言:

stm32是嵌入式MCU開發中最多應用的晶片,很早之前我們開發ST芯一般都是標準庫開發,標準庫簡潔好讀,現在要配合CubeMX生成程式碼,所以官方主推HAL庫和LL庫,但是HAL程式碼冗雜很繞,因為出來也不久,有些程式碼使用之後不是那麼好用。

這次我就來分享兩個實際使用過程中遇到的兩個問題,一個是使用uart的傳送中斷進行資料傳送產生的陣列訪問越界的問題。一個是stop模式下,dma相關的外設休眠喚醒需要注意重新初始化。

這篇是uart使用的介紹:

作者:良知猶存

轉載授權以及圍觀:歡迎關注微信公眾號:羽林君

或者新增作者個人微信:become_me


情節介紹:

串列埠是我們經常是用的一個外設,一般我們為了傳送速度變快,會使用DMA或者中斷髮送接收。而CubeMX配置下,HAL呼叫了自己的一套函式 HAL_UART_IRQHandler 層層呼叫。

在官方提供的 stm32f4xx_hal_uart.c 檔案中你可以看到如下函式:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)

{

.....

  /* UART in mode Transmitter ------------------------------------------------*/
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }

......

}

其中在UART_Transmit_IT 函式中 有一段 函式為

if (--huart->TxXferCount == 0U)
{
    /* Disable the UART Transmit Complete Interrupt */
    __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);

    /* Enable the UART Transmit Complete Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TC);
}

這裡會把傳送的 TxXferCount 的計數值自減,並判斷是否為零。正常工作都沒有問題,可是我們的裝置實際使用過程中,上層的部分斷電之後,會給底層通訊串列埠帶了一箇中斷,這個時候繼續減下去就會出現出現一個很大值,這個是因為 TxXferCount 是一個無符號的16位資料。

  __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter */

debug看到如下資料,原因上文提到。就是我遇到我們Linux核心板掉電之後會產生一箇中斷,導致這裡判斷時候自動減一,TxXferCount從 0 變成 -1 因為是無符號資料,所以資料表現為65535。

在全域性搜尋TxXferCount呼叫,我們可以看到TxXferSize 是傳送buf的長度資料

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* 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->pTxBuffPtr = pData;
    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Transmit data register empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

此外我們還會發現一處 huart->TxXferCount 計數 自減 使用。

此處的函式如下, 伴隨著一個很大的 TxXferCount開始自減, pdata16bits開始自加。剛開始越界的時候由於該記憶體被初始化過,所以沒有問題,該迴圈執行一會之後,程式就會進入hardfault

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

    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    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--;
    }
......
}

修改建議:

和硬體溝通過,他們的掉電機制,就是如此無法修改。所以我們進行軟體的一些修改,因為會產生一箇中斷導致計數值自減,所以我們初步確認進行自減處進行限制,先增加一個零值判斷。

huart->TxXferCount == 0U 

又考慮到我們單包資料單次不會超過150byte,所以又加上150位元組的控制。(此處的資料流控制大家可以按照自己實際使用的情況進行酌情使用)

huart->TxXferCount   > 150U

原函式:

static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
......
    if (--huart->TxXferCount == 0U)
    {
      /* Disable the UART Transmit Complete Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
    
      /* Enable the UART Transmit Complete Interrupt */
      __HAL_UART_ENABLE_IT(huart, UART_IT_TC);
    }
 ......
}

改為:

static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
......
    if (huart->TxXferCount == 0U || --huart->TxXferCount == 0U || huart->TxXferCount   > 150U )
    {
      /* Disable the UART Transmit Complete Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
    
      /* Enable the UART Transmit Complete Interrupt */
      __HAL_UART_ENABLE_IT(huart, UART_IT_TC);
    }
 ......
}

最後程式碼可以正常的使用。

結語

這就是我分享的專案中遇到一個st官方庫使用的問題,如果大家有更好的想法和需求,也歡迎大家加我好友交流分享哈。


作者:良知猶存,白天努力工作,晚上原創公號號主。公眾號內容除了技術還有些人生感悟,一個認真輸出內容的職場老司機,也是一個技術之外豐富生活的人,攝影、音樂 and 籃球。關注我,與我一起同行。

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推薦閱讀

【1】C++的智慧指標你瞭解嗎?

【2】嵌入式底層開發的軟體框架簡述

【3】CPU中的程式是怎麼執行起來的 必讀

【4】cartographer環境建立以及建圖測試

【5】設計模式之簡單工廠模式、工廠模式、抽象工廠模式的對比

本公眾號全部原創乾貨已整理成一個目錄,回覆[ 資源 ]即可獲得。

相關文章