STM32
直播中

刘英

7年用户 1054经验值
私信 关注
[问答]

关于HAL库代码stm32f1xx_hal_uart.c代码分析,不看肯定后悔

STM32串口通信原理是什么?
关于HAL库代码stm32f1xx_hal_uart.c代码分析,不看肯定后悔

回帖(1)

刘桂珍

2021-12-9 10:04:56
在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器,另一个是程序看不到的移位寄存器,对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空(单字节),另一个是TC=发送结束(多字节)。
  当USART_DR中的1字节数据传送到移位寄存器后,TXE被置位,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的1字节数据(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位(共8位)发送结束,所有多个字节均发送结束时(最后1字节中送出停止位后)硬件会将TC标志置位。
    另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的(故通常程序串口初始化时不打开此中断,否则频繁进入TXE中断)。而发送完成TCIE和IDLEIE则必须等发送1次数据后,才会有此中断产生,故一开始打开TCIE和IDLEIE中断没有关系。当然一开始必须要打开RXNEIE中断(接收数据),TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
    至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。
  TXE--写寄存器DR清零
  RXNE--读寄存器DR清零,也可软件手动清零
  TC--  读/写寄存器DR清零,也可软件手动清零
  IDLE--需要软件清除,__HAL_UART_CLEAR_IDLEFLAG[HAL]     
  串口收发数据都是以1个字节为单位,如果是阻塞轮询模式,是while多少字节数,如果是中断,则每收发1个字节,则进入1次中断
  HAL库代码分析:

  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;//还剩余的要发送的数据量
    while(huart->TxXferCount > 0U)//阻塞模式,while循环
    {
      huart->TxXferCount--;//每发完1个字节,剩余要发送数据量减1
      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)
        {//上面函数作用是保证发送寄存器里的数据为空(UART_FLAG_TXE置位)
          return HAL_TIMEOUT;
        }
        huart->Instance->DR = (*pData++ & (uint8_t)0xFF);//发送寄存器写入数据,UART_FLAG_TXE复位【待数据全部移到发送移位寄存器时,UART_FLAG_TXE置位,故前面超时等待】
      }
    }
      if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {//上面函数作用是保证全部传输完成,UART_FLAG_TC置1,否则也认为传输失败,双重保证!
      return HAL_TIMEOUT;
    }
      /* At end of Tx process, restore huart->gState to Ready */
    huart->gState = HAL_UART_STATE_READY;
      /* Process Unlocked */
    __HAL_UNLOCK(huart);
      return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

  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);//此处仅仅打开TXE中断,【此时还未开始发送数据,发送缓冲区TDR为空,将进入到串口中断函数-stm32f1xx_it.c的void USARTx_IRQHandler(void) x=1,2,3...】
      return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
   
  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;
    /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if(errorflags == RESET)
  {//无任何串口错误时
    /* UART in mode Receiver -------------------------------------------------*/
    if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {//接收中断标志置位且中断使能
      UART_Receive_IT(huart);//串口中断接收处理函数
      return;
    }
  }
    /* If some errors occur */
  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
    /* UART parity error interrupt occurred ----------------------------------*/
    if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
    {//校验出错且校验中断使能
      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }
      /* UART noise error interrupt occurred -----------------------------------*/
    if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }
      /* UART frame error interrupt occurred -----------------------------------*/
    if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }
      /* UART Over-Run interrupt occurred --------------------------------------*/
    if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }
      /* Call UART Error Call back function if need be --------------------------*/
    if(huart->ErrorCode != HAL_UART_ERROR_NONE)
    {
      /* UART in mode Receiver -----------------------------------------------*/
      if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
      {
        UART_Receive_IT(huart);
      }
        /* If Overrun error occurs, or if any error occurs in DMA mode reception,
         consider error as blocking */
      dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
      if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
      {
        /* Blocking error : transfer is aborted
           Set the UART state ready to be able to start again the process,
           Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
        UART_EndRxTransfer(huart);
          /* Disable the UART DMA Rx request if enabled */
        if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
        {
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
            /* Abort the UART DMA Rx channel */
          if(huart->hdmarx != NULL)
          {
            /* Set the UART DMA Abort callback :
               will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
            huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
            if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
            {
              /* Call Directly XferAbortCallback function in case of error */
              huart->hdmarx->XferAbortCallback(huart->hdmarx);
            }
          }
          else
          {
            /* Call user error callback */
            HAL_UART_ErrorCallback(huart);//异常回调函数可增加提示
          }
        }
        else
        {
          /* Call user error callback */
          HAL_UART_ErrorCallback(huart);
        }
      }
      else
      {
        /* Non Blocking error : transfer could go on.
           Error is notified to user through user error callback */
        HAL_UART_ErrorCallback(huart);
        huart->ErrorCode = HAL_UART_ERROR_NONE;
      }
    }
    return;
  } /* End if some error occurs */
    /* UART in mode Transmitter ------------------------------------------------*/
  if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {//接回上面提到的TXEIE中断被触发,接着执行到此处【每发1个字节执行到此处】,下面有UART_Transmit_IT详细分析
    UART_Transmit_IT(huart);
    return;
  }
  
  /* UART in mode Transmitter end --------------------------------------------*/
  if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {//如果上面的多个字节都发送完毕,则此刻TC标志被置位,如果中断使能,将进入到下面函数执行
    UART_EndTransmit_IT(huart);//__HAL_UART_DISABLE_IT(huart, UART_IT_TC);关闭TC中断,还有个回调空白函数-发送完成函数HAL_UART_TxCpltCallback供用户增加想要的功能
    return;
  }
}
   
  static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
  uint16_t* tmp;
  
  /* Check that a Tx process is ongoing */
  if(huart->gState == HAL_UART_STATE_BUSY_TX)
  {
    if(huart->Init.WordLength == UART_WORDLENGTH_9B)
    {//带有校验处理
      tmp = (uint16_t*) huart->pTxBuffPtr;
      huart->Instance->DR = (uint16_t)(*tmp & (uint16_t)0x01FF);
      if(huart->Init.Parity == UART_PARITY_NONE)
      {
        huart->pTxBuffPtr += 2U;
      }
      else
      {
        huart->pTxBuffPtr += 1U;
      }
    }
    else
    {//不带校验发送
      huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);//TDR发送寄存器写入数据,TXE标志被清零,待发送寄存器中的1字节数据被全部转移到发送移位寄存器中,此刻发送寄存器又为空,TXE中断再次被触发,由再次进入到串口中断函数,进入到此处执行,故而实现数据连续不间断发送【pTxBuffPtr++指向待发送下1字节数据】
    }
      if(--huart->TxXferCount == 0U)
    {//待发送的字节数为0时,表示数据发送完毕
      /* Disable the UART Transmit Complete Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);//必须关闭TXE中断,否则会一直进入,毕竟没数据发送时,TDR一直为空
        /* Enable the UART Transmit Complete Interrupt */   
      __HAL_UART_ENABLE_IT(huart, UART_IT_TC);//打开发送完成中断,因为数据发送完毕,TC标志将置位,将再次进入到串口中断函数里执行,此时执行到前面的UART_EndTransmit_IT函数
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
   
  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;//还剩余的要接收的数据量字节数
      /* Check the remain data to be received */
    while(huart->RxXferCount > 0U)
    {
      huart->RxXferCount--;//每接收完1字节,则减1
      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)
        {//等待RXNE标志置位,等待时间Timeout【特别说明,如果传递的Size大于实际收到的数据,则肯定会超时】,如果被置位,表示接收寄存器RDR收到数据
          return HAL_TIMEOUT;//如果未被置位,则超时退出
        }
        if(huart->Init.Parity == UART_PARITY_NONE)
        {
          *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//从DR读取数据,该操作会将RXNE标志复位,前面置位,故而形成循环,保证数据正确接收
        }
        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;
   
    /* Process Unlocked */
    __HAL_UNLOCK(huart);
      return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
   
  HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{//串口中断接收函数
  /* 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->pRxBuffPtr = pData;//接收的数据缓存指针
    huart->RxXferSize = Size;//接收的最大数据量字节数
    huart->RxXferCount = Size;//还剩余的要接收的数据量字节数
      huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;
   
    /* Process Unlocked */
    __HAL_UNLOCK(huart);
      /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);//校验错误中断使能
      /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);//错误中断使能-帧错误,噪错误,满溢错误
      /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);//接收中断使能【如果串口收到数据,则进入到串口处理函数HAL_UART_IRQHandler】
      return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
   
  如果串口收到数据,则进入到串口处理函数HAL_UART_IRQHandler的下面部分执行
      if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {//打开串口中断,且有数据收到-RXNE被置位
      UART_Receive_IT(huart);
      return;
    }
  static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t* tmp;
  
  /* Check that a Rx process is ongoing */
  if(huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
    if(huart->Init.WordLength == UART_WORDLENGTH_9B)
    {//带有校验
      tmp = (uint16_t*) huart->pRxBuffPtr;
      if(huart->Init.Parity == UART_PARITY_NONE)
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
        huart->pRxBuffPtr += 2U;
      }
      else
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
        huart->pRxBuffPtr += 1U;
      }
    }
    else
    {//不带校验
      if(huart->Init.Parity == UART_PARITY_NONE)
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//接收数据寄存器RDR被读取【保存数据到接收buf,并指向下一个】,RXNE标志复位。【当下1字节数据8位全部都从接收移位寄存器转移到接收数据寄存器RDR后,RXNE再次被置位,将再次触发RXNE中断,再次到此处执行,如此循环直到收完全部数据】
      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }
      if(--huart->RxXferCount == 0U)
    {//收完全部数据后
      /* Disable the IRDA Data Register not empty Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);//关闭RXNE中断
        /* Disable the UART Parity Error Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);//关闭校验错误中断
        /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
        __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);//关闭错误中断
        /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;
        HAL_UART_RxCpltCallback(huart);//接收完成中断回调函数,用户可以再这里做操作
        return HAL_OK;
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分