> For the complete documentation index, see [llms.txt](https://oceanaparts.gitbook.io/halnote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oceanaparts.gitbook.io/halnote/hal-ku-chuan-kou-han-shu-jie-xi.md).

# HAL库串口函数解析

### 串口句柄 UART\_HandleTypeDef

**UART\_HandleTypeDef UART1\_Handler;**

定义在stm32f1xx\_hal\_uart.h的138行左右，这个结构体，作为对 UART 的抽象来使用的。它记录了 UART 相关参数和状态，并提供回调函数的接口，以便用户可以根据需要执行各种操作。

在整个串口的操作中几乎都要用到。

```c
typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                 *Instance;        /*!< UART 寄存器基地址        */

  UART_InitTypeDef              Init;             /*!< UART 通信参数      */

  uint8_t                       *pTxBuffPtr;      /*!< 指向 UART Tx 传输缓冲区的指针 */

  uint16_t                      TxXferSize;       /*!< UART Tx 传输大小              */

  __IO uint16_t                 TxXferCount;      /*!< UART Tx 传输计数器           */

  uint8_t                       *pRxBuffPtr;      /*!< 指向 UART Rx 传输缓冲区的指针 */

  uint16_t                      RxXferSize;       /*!< UART Rx 传输大小              */

  __IO uint16_t                 RxXferCount;      /*!< UART Rx 传输计数器           */

  DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA 句柄参数      */

  DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA 句柄参数      */

  HAL_LockTypeDef               Lock;             /*!< 锁定对象                     */

  __IO HAL_UART_StateTypeDef    gState;           /*!< 与全局句柄管理和 Tx 操作相关的 UART 状态信息。
                                                       此参数可以是 @ref HAL_UART_StateTypeDef 的值 */

  __IO HAL_UART_StateTypeDef    RxState;          /*!< 与 Rx 操作相关的 UART 状态信息。
                                                       此参数可以是 @ref HAL_UART_StateTypeDef 的值 */

  __IO uint32_t                 ErrorCode;        /*!< UART 错误代码                    */

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Tx 半完成回调        */
  void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Tx 完成回调             */
  void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Rx 半完成回调        */
  void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Rx 完成回调             */
  void (* ErrorCallback)(struct __UART_HandleTypeDef *huart);             /*!< UART 错误回调                   */
  void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART 中止完成回调          */
  void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART 中止传输完成回调 */
  void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART 中止接收完成回调  */
  void (* WakeupCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART 唤醒回调                  */

  void (* MspInitCallback)(struct __UART_HandleTypeDef *huart);           /*!< UART Msp 初始化回调                */
  void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Msp 去初始化回调              */
#endif  /* USE_HAL_UART_REGISTER_CALLBACKS */

} UART_HandleTypeDef;

```

### 串口服务函数 HAL\_UART\_IRQHandler

HAL\_UART\_IRQHandler(UART\_HandleTypeDef \*huart)

**解析：**

当串口接收或发送数据时，会产生相应的中断，该中断处理函数会根据标志位（`isrflags`）和控制寄存器（`cr1its`和`cr3its`）的状态来判断中断原因并进行相应的处理，如调用接收函数、传输函数或结束传输函数，并设置错误标志位（`huart->ErrorCode`）以便后续处理。

具体而言，

当没有发生错误时，==如果接收缓冲区非空且接收中断使能，则调用接收函数UART\_Receive\_IT==；如果处于发送模式，调用发送函数

当发生错误时，根据错误类型设置错误标志位，并根据是否使用DMA模式接收数据以及错误类型采取不同的处理方式，如中止传输、关闭DMA请求等。

在上述过程中，还要考虑如何调用错误处理回调函数（`huart->ErrorCallback`）或者弱错误回调函数（`HAL_UART_ErrorCallback()`）以及是否开启了注册回调函数的功能（`USE_HAL_UART_REGISTER_CALLBACKS`）。

**函数具体解析：**

```c
/**
  * @brief  处理UART中断请求
  * @param  huart  指向包含指定UART模块的配置信息的UART_HandleTypeDef结构体的指针
  * @retval None
  */
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

  /* 如果没有错误发生 */
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if (errorflags == RESET)
  {
    /* UART在接收模式下 -------------------------------------------------*/
    if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }

  /* 如果发生了一些错误 */
  if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
    /* UART奇偶校验错误中断发生 ----------------------------------*/
    if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }

    /* UART噪声错误中断发生 -----------------------------------*/
    if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }

    /* UART帧错误中断发生 -----------------------------------*/
    if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }

    /* UART溢出中断发生 --------------------------------------*/
    if (((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }

    /* 如果需要，调用UART错误回调函数 --------------------------*/
    if (huart->ErrorCode != HAL_UART_ERROR_NONE)
    {
      /* UART在接收模式下 -----------------------------------------------*/
      if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
      {
        UART_Receive_IT(huart);
      }

      /* 如果发生溢出错误，或者在DMA模式接收中发生任何错误，
         将错误视为阻塞 */
      dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
      if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
      {
        /* 阻塞错误：传输被中止
           设置UART状态为就绪，以便能够重新启动该过程，
           禁用Rx中断，并禁用Rx DMA请求（如果正在进行） */
        UART_EndRxTransfer(huart);

        /* 如果启用了UART DMA Rx请求，则禁用它 */
        if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
        {
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

          /* 中止UART DMA Rx通道 */
          if (huart->hdmarx != NULL)
          {
            /* 设置UART DMA中止回调：
               将在DMA中止过程结束时调用HAL_UART_ErrorCallback() */
            huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
            if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
            {
              /* 在出现错误的情况下直接调用XferAbortCallback函数 */
              huart->hdmarx->XferAbortCallback(huart->hdmarx);
            }
          }
          else
          {
            /* 调用用户错误回调函数 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
            /*调用已注册的错误回调函数*/
            huart->ErrorCallback(huart);
#else
            /*调用旧版的弱错误回调函数*/
            HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
          }
        }
        else
        {
          /* 调用用户错误回调函数 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
          /*调用已注册的错误回调函数*/
          huart->ErrorCallback(huart);
#else
          /*调用旧版的弱错误回调函数*/
          HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
        }
      }
      else
      {
        /* 非阻塞错误：传输可以继续进行。
           通过用户错误回调通知用户错误 */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*调用已注册的错误回调函数*/
        huart->ErrorCallback(huart);
#else
        /*调用旧版的弱错误回调函数*/
        HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

        huart->ErrorCode = HAL_UART_ERROR_NONE;
      }
    }
    return;
  } /* 如果发生了一些错误 */

  /* UART在发送模式下 ------------------------------------------------*/
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }

  /* UART在发送模式下结束 --------------------------------------------*/
  if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);
    return;
  }
}

```

### 串口中断使能 HAL\_UART\_Receive\_IT

该函数负责开启中断

```c
/**
  * @brief  以非阻塞模式接收一定量的数据。
  * @note   当UART奇偶校验未启用（PCE = 0）且字长配置为9位（M1-M0 = 01）时，
  *         接收到的数据将被处理为一组u16。在这种情况下，Size必须指示
  *         通过pData可用的u16数量。
  * @param  huart 指向包含指定UART模块的配置信息的UART_HandleTypeDef结构的指针。
  * @param  pData 指向数据缓冲区（u8或u16数据元素）的指针。
  * @param  Size  要接收的数据元素（u8或u16）的数量。
  * @retval HAL状态
  */
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* 检查是否已经有一个Rx进程正在进行 */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    /* 进程锁定 */
    __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;

    /* 进程解锁 */
    __HAL_UNLOCK(huart);

    /* 启用UART奇偶校验错误中断 */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* 启用UART错误中断：（帧错误，噪声错误，溢出错误） */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* 启用UART数据寄存器非空中断 */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

```

### 串口接收函数 UART\_Receive\_IT

UART\_Receive\_IT 是真正在接收数据的函数，==但在最后会关闭中断==

同时该函数会调用HAL\_UART\_RxCpltCallback回调函数，去实现当数据接收完需要实现的业务。

```c
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t *tmp; // 定义一个指向uint16_t类型的指针变量tmp
  /* 检查是否正在进行Rx进程 */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
    if (huart->Init.WordLength == UART_WORDLENGTH_9B) // 如果数据位长度为9位
    {
      tmp = (uint16_t *) huart->pRxBuffPtr; // 将指针变量tmp指向接收缓存区指针所指向的位置
      if (huart->Init.Parity == UART_PARITY_NONE) // 如果无奇偶校验
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF); // 将数据存入tmp指向的位置
        huart->pRxBuffPtr += 2U; // 接收缓存区指针移动2个字节
      }
      else // 否则
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF); // 将数据存入tmp指向的位置
        huart->pRxBuffPtr += 1U; // 接收缓存区指针移动1个字节
      }
    }
    else // 如果数据位长度不为9位
    {
      if (huart->Init.Parity == UART_PARITY_NONE) // 如果无奇偶校验
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF); // 将数据存入接收缓存区指针所指向的位置
      }
      else // 否则
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F); // 将数据存入接收缓存区指针所指向的位置
      }
    }
    if (--huart->RxXferCount == 0U) // 如果接收计数器减为0
    {
      /* 关闭UART数据寄存器非空中断 */
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
      /* 关闭UART奇偶校验错误中断 */
      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
      /* 关闭UART错误中断（帧错误、噪声错误、溢出错误） */
      __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
      /* Rx进程已完成，将huart->RxState恢复为Ready状态 */
      huart->RxState = HAL_UART_STATE_READY;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
      /* 调用已注册的Rx完成回调函数 */
      huart->RxCpltCallback(huart);
#else
      /* 调用旧版的Rx完成回调函数 */
      HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
      return HAL_OK; // 返回状态为OK
    }
    return HAL_OK; // 返回状态为OK
  }
  else // 如果Rx进程未在进行
  {
    return HAL_BUSY; // 返回状态为BUSY
  }
}
```

### 串口发送函数

```c
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint8_t  *pdata8bits;  //定义8位数据指针
  uint16_t *pdata16bits; //定义16位数据指针
  uint32_t tickstart = 0U; //定义计时变量
  /* 检查是否有正在进行的发送过程 */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR; //如果数据为空，返回错误
    }
    /* 进入关键区，锁定中断 */
    __HAL_LOCK(huart);
    huart->ErrorCode = HAL_UART_ERROR_NONE; //将错误码设置为无错
    huart->gState = HAL_UART_STATE_BUSY_TX; //将UART状态设置为忙碌
    /* 初始化计时变量 */
    tickstart = HAL_GetTick();
    huart->TxXferSize = Size; //将发送数据大小赋值给TxXferSize
    huart->TxXferCount = Size; //将发送数据大小赋值给TxXferCount
    /*在进行9位/无奇偶校验传输时，pData需要被处理为uint16_t指针*/
    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {
      pdata8bits  = NULL; //如果是9位/无奇偶校验，将8位数据指针设置为空
      pdata16bits = (uint16_t *) pData; //将pData强制转换为16位数据指针
    }
    else
    {
      pdata8bits  = pData; //如果不是9位/无奇偶校验，将pData强制转换为8位数据指针
      pdata16bits = NULL; //将16位数据指针设置为空
    }
    /* 解锁中断 */
    __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); //将16位数据写入数据寄存器
        pdata16bits++; //指向下一个16位数据
      }
      else
      {
        huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); //将8位数据写入数据寄存器
        pdata8bits++; //指向下一个8位数据
      }
      huart->TxXferCount--; //减少发送数量
    }
    /* 等待发送完成标志位被置位 */
    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT; //如果超时，则返回超时错误
    }
    /* 在发送完成后，将UART状态设置为READY */
    huart->gState = HAL_UART_STATE_READY;
    return HAL_OK; //返回发送完成状态
  }
  else
  {
    return HAL_BUSY; //如果UART状态不是READY，则返回忙碌状态
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://oceanaparts.gitbook.io/halnote/hal-ku-chuan-kou-han-shu-jie-xi.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
