HAL库串口函数解析

串口句柄 UART_HandleTypeDef

UART_HandleTypeDef UART1_Handler;

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

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

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)和控制寄存器(cr1itscr3its)的状态来判断中断原因并进行相应的处理,如调用接收函数、传输函数或结束传输函数,并设置错误标志位(huart->ErrorCode)以便后续处理。

具体而言,

当没有发生错误时,==如果接收缓冲区非空且接收中断使能,则调用接收函数UART_Receive_IT==;如果处于发送模式,调用发送函数

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

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

函数具体解析:

/**
  * @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

该函数负责开启中断

/**
  * @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回调函数,去实现当数据接收完需要实现的业务。

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

串口发送函数

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,则返回忙碌状态
  }
}

Last updated