STM32CubeMX之串口封裝詳解

概述

​ 上一篇寫了 STM32CubeMX 的串口的使用,而這篇來扒一扒,它是怎麼進行封裝的。其實在標準庫中也類似如下過程。

一.串口實例

​ 我們都知道,其實單片機最後其實都是對 串口相關的寄存器 進行操作,那麼我們想扒一扒它的流程,必然要先知道串口相關的寄存器是哪些,因此查閱 STM32F4xx中文參考手冊 ,我們可以在 第711頁 找到以下相關寄存器:

  • 狀態寄存器

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QkmCCDd8-1593537240013)(/image/串口狀態寄存器.png)]

  • 數據寄存器

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oWj7qy0U-1593537240016)(/image/串口數據寄存器.png)]

  • 波特率寄存器

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-dZGEjeOD-1593537240018)(/image/波特率寄存器.png)]

  • 控制寄存器1

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-dgaEzY5R-1593537240020)(/image/串口控制寄存器1.png)]

  • 控制寄存器2

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yjCi97QR-1593537240022)(/image/串口寄存器2.png)]

  • 控制寄存器3

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QUPmKDRx-1593537240026)(/image/串口控制寄存器3.png)]

  • 保護時間和預分頻器寄存器

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-m90u99HG-1593537240027)(/image/串口時間和分頻寄存器.png)]

​ 這些就是操作串口所需要的寄存器,那麼我們應該怎麼和 HAL庫 的東西對應起來,去HAL庫中找找,我們就會在 stm32f407xx.h 的頭文件中找到以下結構體

/** 
  * @brief Universal Synchronous Asynchronous Receiver Transmitter
  */
 
typedef struct
{
  __IO uint32_t SR;        /*!< USART Status register,                   Address offset: 0x00 */
  __IO uint32_t DR;        /*!< USART Data register,                     Address offset: 0x04 */
  __IO uint32_t BRR;       /*!< USART Baud rate register,                Address offset: 0x08 */
  __IO uint32_t CR1;       /*!< USART Control register 1,                Address offset: 0x0C */
  __IO uint32_t CR2;       /*!< USART Control register 2,                Address offset: 0x10 */
  __IO uint32_t CR3;       /*!< USART Control register 3,                Address offset: 0x14 */
  __IO uint32_t GTPR;      /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;

你會發現,寄存器和結構體中的變量名一一對應,那就是它了,我們再深入研究以下,爲什麼每個聲明都是用uint32_t 的類型?我們還可以在 STM32F4xx中文參考手冊 找到以下這張圖,更直觀的解釋原因

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vzyrBIB4-1593537240029)(/image/串口偏移.png)]

​ 從上圖可以看出,每個寄存器都是之間的地址偏移都是 4個字節,也就是 32 bit ,所以是用 uint32_t ,使每個寄存器佔用 4 個字節,符合芯片文檔。

​ 可是不知道你發現了沒有,這邊都沒有說串口在內存的實際地址,有些小夥伴可能也不知道,就算有地址要怎麼操作,這裏我們稍微複習一下C 語言中的 常量指針 的知識,下面給出一小段代碼

*(uint32_t *)0x40011000 = 0xFF;

​ 這段代碼的含義,就是將 0x40011000 這個值轉爲指向一個32 bit 的內存空間的指針,* 取地址符號就是使得0xFF存入該 32 bit的空間內。

​ 因此我們應該要找到我們所使用的 串口1在內存的地址 ,說了這麼多遍內存,我們就來看看 STM32 的內存到底長什麼樣子,我們可以在 對應的芯片手冊上找到其內存映射圖,當前芯片是 STM32F407,因此我們在芯片手冊中會看到

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fFjksyGO-1593537240031)(/image/內存映射圖.png)]

​ 從上圖可以看到 STM32F407 的內存映射圖,內存被分成了很多塊,而串口是屬於外設,因此現在只要關心從下往上數第三塊內存塊就行了,但是目前我們也只能從中看到有 AHB1,AHB2,APB1,APB2,用標準庫的小夥伴,應該對這些詞很熟悉了,就是經常要對其進行時鐘初始化的外設總線。

​ 但是我們還是沒有看到我們想要的 地址,因此我們還是要藉助一張時鐘樹的圖,來判斷串口1 在哪一條總線之下。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SqOEg8Nn-1593537240032)(/image/時鐘樹1.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-t7WwVzvD-1593537240032)(/image/時鐘樹3.png)]

​ 結合時鐘樹和內存映射圖,我們可以看到,串口1的地址在0x400100000x4001 4BFF ,但是還不是真正我們想要的

​ 最終,你會在 STM32F4xx中文參考手冊 中發現一張存儲的映射表,串口1的地址範圍就寫在那裏

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Eyi5Scx3-1593537240034)(/image/存儲器映射圖1.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-bPKmmnsr-1593537240034)(/image/存儲器映射圖2.png)]

​ 這個地址範圍就是我們要對其進行串口操作的實際地址,但是我們的操作實際就用到了7個寄存器

​ 爲了更形象的說明,我們用以下代碼,將前面提到的寄存器的實際的地址打印出來:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DdUj5A8V-1593537240035)(/image/打印寄存器地址.png)]

打印結果如下:

huart1.Instance->SR 40011000
huart1.Instance->DR  40011004
huart1.Instance->BRR 40011008
huart1.Instance->CR1 4001100c
huart1.Instance->CR2 40011010
huart1.Instance->CR3 40011014
huart1.Instance->CTPR 40011018

現在我們就很明白了,串口1的基地址 (40011000)+ 上面所說的地址偏移量 = 每個寄存器的實際地址,

​ 接下來我們再看看,這個基地址是怎麼在 HAL 庫中用上的。我們從上面的分析中知道,串口1其實是在外設總線上的,並且是 APB2 外設總線上,所以還是在stm32f407.h 的頭文件中找,我們可以找到以下宏定義:

  • 外設基地址

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tR8X0czO-1593537240037)(/image/外設基地址.png)]

  • APB2基地址

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qQ4m8eXh-1593537240038)(/image/APB2基地址.png)]

  • 串口1基地址

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NG9zR2LJ-1593537240040)(/image/串口1基地址.png)]

  • 串口1用 USART_TypeDef 結構體封裝

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-K4bOctfq-1593537240041)(/image/串口寄存器封裝.png)]

    這樣我們就可以算出,串口1的基地址爲:

    USART1_BASE = 0x40000000 + 0x00010000 + 0x1000
    USART1_BASE = 0x40011000   //正如我們上述文檔所看到的一樣
    

    將這個值通過 USART_TypeDef 結構體指針的強制轉換,就可以用結構體的方式來對寄存器進行存取值了

    (昇華一下下,其實數組的首地址也可以用結構體指針進行強制轉換,只要結果符合你的預期就可以了,比如這個技巧可以用在網絡傳輸的時候)

現在來看看 USART1 用在哪裏了,我們可以在 usart.c 找到以下代碼

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Cjuv4Vlk-1593537240043)(/image/USART1使用.png)]

我們可以看到它用在了串口1初始化的函數(MX_USART1_UART_Init )裏面了,也終於看到了我們這個標題要說的實例( huart1 )了

接下來我們看看這個實例的結構體( UART_HandleTypeDef )長什麼樣子

/**
  * @brief  UART handle Structure definition
  */
typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                *Instance;        /*!< UART registers base address        */
  UART_InitTypeDef              Init;             /*!< UART communication parameters      */
  uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */
  uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */
  __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter           */
  uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */
  uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */
  __IO uint16_t                 RxXferCount;      /*!< UART Rx Transfer Counter           */
  DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */
  DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */
  HAL_LockTypeDef               Lock;             /*!< Locking object                     */
  __IO HAL_UART_StateTypeDef    gState;           
  __IO HAL_UART_StateTypeDef    RxState;          
  __IO uint32_t                 ErrorCode;        /*!< UART Error code                    */
    
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Tx Half Complete Callback        */
  void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Tx Complete Callback             */
  void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Rx Half Complete Callback        */
  void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */
  void (* ErrorCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART Error Callback  */
 void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort CompleCallback      */
  void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
  void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART Abort Receive Complete Callback  */
  void (* WakeupCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART Wakeup Callback */
  void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback*/
  void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInitcallback*/
#endif  /* USE_HAL_UART_REGISTER_CALLBACKS */

} UART_HandleTypeDef;

​ 我們可以看到在這個結構體裏看到,它需要串口基地址,串口初始化所需要的值,以及數據接收和發送所需要的指針,和當前狀態標誌位。

二. 串口實例初始化

​ 我們可以在串口初始化的函數(MX_USART1_UART_Init )中,找到我們自己在STM32CubeMX 的圖形界面中爲串口配置的值,如下

  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16; 

​ 這不是真正的初始化(因爲還沒操作到我們之前所說的寄存器),它只是用來記錄需要初始化的值

​ 而真正的初始化這些參數,是在 HAL_UART_Init 中的 UART_SetConfig 函數中,如下代碼所示

/**
  * @brief  Configures the UART peripheral.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
static void UART_SetConfig(UART_HandleTypeDef *huart)
{
  uint32_t tmpreg;
  uint32_t pclk;

  /* Check the parameters */
  assert_param(IS_UART_BAUDRATE(huart->Init.BaudRate));
  assert_param(IS_UART_STOPBITS(huart->Init.StopBits));
  assert_param(IS_UART_PARITY(huart->Init.Parity));
  assert_param(IS_UART_MODE(huart->Init.Mode));

  /*-------------------------- USART CR2 Configuration -----------------------*/
  /* Configure the UART Stop Bits: Set STOP[13:12] bits
     according to huart->Init.StopBits value */
  MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits);

  /*-------------------------- USART CR1 Configuration -----------------------*/
  /* Configure the UART Word Length, Parity and mode:
     Set the M bits according to huart->Init.WordLength value
     Set PCE and PS bits according to huart->Init.Parity value
     Set TE and RE bits according to huart->Init.Mode value
     Set OVER8 bit according to huart->Init.OverSampling value */

  tmpreg = (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode | huart->Init.OverSampling;
  MODIFY_REG(huart->Instance->CR1,
             (uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8),
             tmpreg);

  /*-------------------------- USART CR3 Configuration -----------------------*/
  /* Configure the UART HFC: Set CTSE and RTSE bits according to huart->Init.HwFlowCtl value */
  MODIFY_REG(huart->Instance->CR3, (USART_CR3_RTSE | USART_CR3_CTSE), huart->Init.HwFlowCtl);

  /* Check the Over Sampling */
  if (huart->Init.OverSampling == UART_OVERSAMPLING_8)
  {
    /*-------------------------- USART BRR Configuration ---------------------*/
#if defined(USART6) && defined(UART9) && defined(UART10)
    if ((huart->Instance == USART1) || (huart->Instance == USART6) || (huart->Instance == UART9) || (huart->Instance == UART10))
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate);
    }
#elif defined(USART6)
    if ((huart->Instance == USART1) || (huart->Instance == USART6))
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate);
    }
#else
    if (huart->Instance == USART1)
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate);
    }
#endif /* USART6 */
    else
    {
      pclk = HAL_RCC_GetPCLK1Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate);
    }
  }
  else
  {
    /*-------------------------- USART BRR Configuration ---------------------*/
#if defined(USART6) && defined(UART9) && defined(UART10)
    if ((huart->Instance == USART1) || (huart->Instance == USART6) || (huart->Instance == UART9) || (huart->Instance == UART10))
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
    }
#elif defined(USART6)
    if ((huart->Instance == USART1) || (huart->Instance == USART6))
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
    }
#else
    if (huart->Instance == USART1)
    {
      pclk = HAL_RCC_GetPCLK2Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
    }
#endif /* USART6 */
    else
    {
      pclk = HAL_RCC_GetPCLK1Freq();
      huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
    }
  }
}

/**
  * @}
  */

它真正操作到我們所說那些串口相關的寄存器(CR1, CR2, CR3, BRR)的配置,這裏沒看到 (SR,DR),因爲它在數據的發送和接收時用到了。

三. 串口數據的發送(阻塞模式)

接下來就是對數據發送進行說明,先放出函數

/**
  * @brief  Sends an amount of data in blocking mode.
  * @note   When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
  *         the sent data is handled as a set of u16. In this case, Size must indicate the number
  *         of u16 provided through pData.
  * @param  huart Pointer to a UART_HandleTypeDef structure that contains
  *               the configuration information for the specified UART module.
  * @param  pData Pointer to data buffer (u8 or u16 data elements).
  * @param  Size  Amount of data elements (u8 or u16) to be sent
  * @param  Timeout Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  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;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    while (huart->TxXferCount > 0U)
    {
      huart->TxXferCount--;
      if (huart->Init.WordLength == UART_WORDLENGTH_9B)
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        tmp = (uint16_t *) pData;
        huart->Instance->DR = (*tmp & (uint16_t)0x01FF);
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          pData += 2U;
        }
        else
        {
          pData += 1U;
        }
      }
      else
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
      }
    }

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

  • 進來該函數 檢查當前串口的狀態,若處於準備狀態,將串口狀態設置爲 huart->gState = HAL_UART_STATE_BUSY_TX; 這個其實是爲了對串口做保護,防止同時使用串口,導致數據發送錯誤

  • huart->TxXferCount = Size這變量記錄當前還需要 發送的數據個數,在 while 中不斷進行判斷,如果需要發送數據就通過下面函數查詢 SR 寄存器中的狀態,超過指定時間(Timeout )就是發送失敗了,返回超時。

     if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
     {
              return HAL_TIMEOUT;
     }
    //查詢 UART_FLAG_TXE 標誌位
    
  • 如果沒有超時,就通過下面代碼進行發送,pData 指針 指向我們想要發送的數據的首地址

    huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
    

    總結

    ​ 該函數就是一直在查詢是否是發送狀態,如果可以發送就發送,不能發送,就等到Timeout 結束,返回超時

四. 串口數據接收

接下來就是對數據接收進行說明,再放出函數

/**
  * @brief  Receives an amount of data in blocking mode.
  * @note   When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
  *         the received data is handled as a set of u16. In this case, Size must indicate the number
  *         of u16 available through pData.
  * @param  huart Pointer to a UART_HandleTypeDef structure that contains
  *               the configuration information for the specified UART module.
  * @param  pData Pointer to data buffer (u8 or u16 data elements).
  * @param  Size  Amount of data elements (u8 or u16) to be received.
  * @param  Timeout Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* 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->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Init tickstart for timeout managment */
    tickstart = HAL_GetTick();

    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Check the remain data to be received */
    while (huart->RxXferCount > 0U)
    {
      huart->RxXferCount--;
      if (huart->Init.WordLength == UART_WORDLENGTH_9B)
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        tmp = (uint16_t *) pData;
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
          pData += 2U;
        }
        else
        {
          *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
          pData += 1U;
        }

      }
      else
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
        }
        else
        {
          *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
        }

      }
    }

    /* At end of Rx process, restore huart->RxState to Ready */
    huart->RxState = HAL_UART_STATE_READY;

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
  • 進來該函數 檢查當前串口的狀態,若處於準備狀態,將串口狀態設置爲 huart->gState = HAL_UART_STATE_BUSY_RX; 這也是防止同時使用串口,導致數據接收錯誤

  • huart->TxXferCount = Size這變量記錄當前還需要 接收的數據個數,在 while 中不斷進行判斷,如果需要接收數據就通過下面函數查詢 SR 寄存器中的狀態,超過指定時間(Timeout )就是接收失敗了,返回超時。

     if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
     {
              return HAL_TIMEOUT;
     }
    //查詢 UART_FLAG_RXNE 標誌位
    
  • 如果沒有超時,就通過下面代碼進行數據接收,pData 指針 指向我們想要緩存數據的首地址(一般爲數組首地址),將 DR 中的數據讀取出來。

    *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
    

    總結

    ​ 該函數就是一直在查詢是否是接收狀態,如果可以接收就接收,不能接收,就等到Timeout 結束,返回超時,值得注意的是 這裏如果沒有接收到足夠的數據也會返回超時,比如你要接收10個,如果只接收到了 9個 它也會返回超時,可以通過計算獲取到底接收到幾個。

    當接收超時時,計算接收到的個數 = huart1.RxXferSize - huart1.RxXferCount - 1
    

    全文總結

    • 你會知道如何通過結構體去操作寄存器
    • 你會知道 HAL 庫如何封裝串口
    • 對串口寄存器有整體的瞭解
    • …等等知識

後續還會繼續分享串口的其他基礎知識和使用,感興趣的小夥伴記得關注我!
-----------------------------------------------結束--------------------------------------------------------
文章有價值,請各位看官點個贊,關注我或者點右邊打個賞吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章