打印
[其他ST产品]

STM32串口通信原理及HAL库代码stm32f1xx

[复制链接]
942|38
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一点点0321|  楼主 | 2023-9-20 14:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32串口通信原理及HAL库代码stm32f1xx_hal_uart.c阅读分析
原理性说明:
//参考网址:https://blog.csdn.net/u010561799/article/details/89526266

在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'时产生中断。

使用特权

评论回复
沙发
一点点0321|  楼主 | 2023-9-20 14:52 | 只看该作者
至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

TXE--写寄存器DR清零

RXNE--读寄存器DR清零,也可软件手动清零

TC--  读/写寄存器DR清零,也可软件手动清零

IDLE--需要软件清除,__HAL_UART_CLEAR_IDLEFLAG[HAL]     

串口收发数据都是以1个字节为单位,如果是阻塞轮询模式,是while多少字节数,如果是中断,则每收发1个字节,则进入1次中断;

使用特权

评论回复
板凳
一点点0321|  楼主 | 2023-9-20 14:52 | 只看该作者
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;
        }

使用特权

评论回复
地板
一点点0321|  楼主 | 2023-9-20 14:52 | 只看该作者
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;
  }
}

使用特权

评论回复
5
一点点0321|  楼主 | 2023-9-20 14:53 | 只看该作者
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;
  }
}

使用特权

评论回复
6
一点点0321|  楼主 | 2023-9-20 14:53 | 只看该作者
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;
    }
  }

使用特权

评论回复
7
一点点0321|  楼主 | 2023-9-20 14:53 | 只看该作者
/* 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;
    }

使用特权

评论回复
8
一点点0321|  楼主 | 2023-9-20 14:53 | 只看该作者
/* 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);
      }

使用特权

评论回复
9
一点点0321|  楼主 | 2023-9-20 14:53 | 只看该作者
   /* 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);
            }
          }

使用特权

评论回复
10
一点点0321|  楼主 | 2023-9-20 14:54 | 只看该作者
  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;
      }
    }

使用特权

评论回复
11
一点点0321|  楼主 | 2023-9-20 14:54 | 只看该作者
   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;
  }

使用特权

评论回复
12
一点点0321|  楼主 | 2023-9-20 14:54 | 只看该作者
/* 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;
  }
}

使用特权

评论回复
13
一点点0321|  楼主 | 2023-9-20 14:55 | 只看该作者
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;
      }
    }

使用特权

评论回复
14
一点点0321|  楼主 | 2023-9-20 14:55 | 只看该作者
    else
    {//不带校验发送
      huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);//TDR发送寄存器写入数据,TXE标志被清零,待发送寄存器中的1字节数据被全部转移到发送移位寄存器中,此刻发送寄存器又为空,TXE中断再次被触发,由再次进入到串口中断函数,进入到此处执行,故而实现数据连续不间断发送【pTxBuffPtr++指向待发送下1字节数据】
    }

使用特权

评论回复
15
一点点0321|  楼主 | 2023-9-20 14:55 | 只看该作者
    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;
  }
}

使用特权

评论回复
16
一点点0321|  楼主 | 2023-9-20 14:55 | 只看该作者
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;
    }

使用特权

评论回复
17
一点点0321|  楼主 | 2023-9-20 14:56 | 只看该作者
    /* 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)

使用特权

评论回复
18
一点点0321|  楼主 | 2023-9-20 14:56 | 只看该作者
{
      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;
        }

使用特权

评论回复
19
一点点0321|  楼主 | 2023-9-20 14:56 | 只看该作者
   }
      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标志复位,前面置位,故而形成循环,保证数据正确接收
        }

使用特权

评论回复
20
一点点0321|  楼主 | 2023-9-20 14:56 | 只看该作者
  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;
  }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

41

主题

373

帖子

0

粉丝