HAL库 串口收发函数解析

一、UART_Receive_IT

对于CubeMX生成的代码,USART1_IRQHandler(void)函数为了提高中断效率采用了回调机制。(业务代码可以等中断关闭了再去处理,这样中断处理不会占用太多时间影响程序的执行效率)

HAL库将函数都已封装完整,回调函数完好地提供一个API接口,供用户使用

USART1_IRQHandler(void)函数中只调用了HAL_UART_IRQHandler(&huart1)(可以在STM32f1xx_it.c中找到),参数为uart1的句柄huart1(本质就是个结构体指针),可以通过huart1访问到uart1的各种寄存器和数据类型。

static HAL_StatusTypeDe(UART_HandleTypeDef *huart)
{
  uint16_t *tmp; //定义了一个指针tmp 指向一个地址(由于还没有初始化还不知道指向哪个地址)地址里面装着16位的整型数据

  /* Check that a Rx process is ongoing */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
    if (huart->Init.WordLength == UART_WORDLENGTH_9B)//判断CR寄存器的第12位W是否为1 ,为1则代表设置为一个起始位, 9个数据位, n个停止位
    {
      tmp = (uint16_t *) huart->pRxBuffPtr; //将pRxBuffPtr强制转换为一个指向缓冲区A首地址的指针,这个缓冲区A里放的内容为16位整型,把这个首地址赋值给tmp
      if (huart->Init.Parity == UART_PARITY_NONE)
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);将数据寄存器DR的值赋给temp指向地址里
        huart->pRxBuffPtr += 2U;//指向下两个地址 因为pRxBuffPtr指针指向的地址一个里面只能装8bit,这里有9bit所以需要两个地址位置
      }
      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);//将数据寄存器DR的值载入到huart的缓冲区指针所指向的位置
      }//*(huart->pRxBuffPtr)++ =
//huart->pRxBuffPtr指向的地址里的内容被附上DR里的东西  
//++在后 优先级和等号比起来更低 所以DR的内容先赋值给*huart->pRxBuffPtr,再让huart->pRxBuffPtr这个地址加1
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }

    if (--huart->RxXferCount == 0U)  
//--放前面 优先级高 也就是减1后再执行if判断操作
    { //中断已使用 然后让中断失能
      /* Disable the UART Data Register not empty Interrupt */  
      __HAL_UART_DISABLE_IT(huart, UART_IT_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;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
      /*Call registered Rx complete callback*/
      huart->RxCpltCallback(huart);
#else
      /*Call legacy weak Rx complete callback*/
      HAL_UART_RxCpltCallback(huart); //用户接口 用户在这个里面写回调函数的内容
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

      return HAL_OK;
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

为什么要定义tmp?不可以直接把值放在pRxBuffPtr指向的地址里吗?

tmp的存在是必须的,DR必须做一次读操作才能动作,没有一个赋值语句,DR那句就没办法执行,所以必须有一个临时变量,用来操作DR(这句话抄的 不知道什么意思)

这个函数是把所有输入的数据一个一个存放到缓存区中,也就是,一个数据对应一次中断,进入中断处理函数,调用UART_Receive_IT,根据RxXferSize设置你要放多少数据在缓存区,直到确认所有的数据都存放到缓存区中,huart->RxXferCount对应的值也会自减为0,此时会执行3个__HAL_UART_DISABLE_IT函数来关闭中断(我也不知道为什么HAL库要这样设置),之后会进入回调函数,我们只需在回调函数中写入我们的用户代码即可。但是其中一定要包含打开中断的函数,因为__HAL_UART_DISABLE_IT这个函数已经关闭中断。

二、HAL_UART_Receive

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

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

这个函数和上一个相比,多了给RxXferCount等赋值的操作,但是少了回调函数,来一个数据存一个,存在缓存区,直到RxXferCount为0

三 HAL_UART_Receive_IT

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

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

记住!这个函数不是用来接收数据的!这个函数不是用来接收数据的!这个函数不是用来接收数据的!他是用来打开中断,配置串口中断的!不是真正的接收数据的中断函数,很容易把它和其他两个函数混淆。看了上面两个函数的解释,这个函数的内容应该不难理解,这也就是,我们要手动打开串口中断,就要在main函数里面首先写下它,否则无法进入串口中断(亲测如此),其次还要在回调函数里面添加这个函数(因为之前就说过一旦进入回调函数,串口中断就会关闭),为了下一次接收数据考虑,需要这么做

### 回答1: hal_uart_receive_dma是HAL库中的一个函数,用于启动UART接收DMA传输。DMA(Direct Memory Access)是一种直接内存访问技术,可以在不占用CPU时间的情况下完成数据传输。使用DMA传输可以提高数据传输效率和系统性能。 hal_uart_receive_dma函数的参数包括UART句柄、接收缓冲区、接收数据长度和DMA通道。函数的作用是启动DMA传输,将接收到的数据存储到指定的接收缓冲区中。当DMA传输完成后,会触发DMA传输完成中断,可以在中断处理函数中处理接收到的数据。 需要注意的是,在使用DMA传输时,需要先配置UART的DMA接收模式,并启用DMA传输。同时,需要配置DMA通道的传输模式和传输方向,以及DMA传输的数据长度和地址等参数。在使用DMA传输时,还需要注意内存对齐和缓冲区大小等问题,以确保DMA传输的正确性和稳定性。 ### 回答2: hal_uart_receive_dma是STM32 HAL库中UART串口接收DMA模式的函数。它使得UART数据接收可以使用DMA进行数据传输,从而大大提高数据传输的效率。hal_uart_receive_dma函数的详细解释如下: 该函数需要用到以下几个参数:UART_HandleTypeDef结构体指针、指向数据缓冲区的指针和接收数据的长度。 其中UART_HandleTypeDef结构体指针需要先用HAL_UART_Init函数进行初始化,而指向数据缓冲区的指针需要用户提供,接收数据的长度也需要用户提供。 在调用hal_uart_receive_dma函数之前,需要先使能USART的DMA接收模式,这可以通过调用HAL_UART_Receive_DMA函数来实现。 hal_uart_receive_dma函数的具体功能是将接收到的数据存储到缓冲区中,当数据接收完成后,它会调用DMA完成中断处理函数,将接收到的数据长度和缓冲区指针作为参数传递给此中断处理函数。同时,此函数还会返回接收的状态,指示数据接收是否成功。 在应用程序中,可以通过调用hal_uart_receive_dma函数来实现片内或者片间的串口数据的高效接收。同时,由于使用了DMA模式,该函数可以很好地降低CPU的负载,从而提高整个系统的效率。 需要注意的是,在使用hal_uart_receive_dma函数时,确保UART的传输速率和数据缓冲区的容量都要匹配,否则可能会导致数据丢失。在实际应用中,使用这个函数时,应该根据实际情况进行调整和优化。 ### 回答3: hal_uart_receive_dma是HAL库中UART接收DMA模式的函数,下面进行详细解释: 1. 函数定义与参数 函数定义:HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 参数解释: 1) huart: UART句柄。包含UART外设的配置信息,如波特率、数据位、奇偶校验等。 2) pData: 数据缓存区地址。用于存储接收到的UART数据,大小不小于Size。 3) Size: 需要接收的数据长度。该参数为16位无符号整数,范围为1~65535。 2. 函数原理 在HAL库中,UART接收可分为两种模式:轮询模式和DMA模式。轮询模式需要不断地检查数据是否到达,会带来 CPU 资源的浪费和响应延迟。而DMA模式利用DMA控制器,将数据直接传输到内存中,告别了CPU的轮询,可以大大减少功耗和响应延迟,提高性能。 hal_uart_receive_dma函数是基于DMA模式的UART接收函数,其原理如下: 1) 初始化DMA通道 首先,需要初始化与UART对应的DMA通道。DMA通道会将UART的数据传输到指定内存缓存区。 2) 启动UART接收 接下来,调用HAL_UART_Receive_DMA函数启动UART接收。函数会使UART进入接收模式,此时UART会将数据存放在DMA通道向内存传输的缓存区中。 3) 中断和超时处理 在数据传输过程中,若出现错误(DMA传输失败、UART数据校验失败等),则会触发中断,通过HAL_UART_ErrorCallback函数处理错误。同时,也需要根据超时时间检查数据是否到达,若超时则通过HAL_UART_RxCpltCallback函数处理接收完成事件。 4) 停止接收 当需要停止接收时,调用HAL_UART_DMAStop函数停止DMA传输和UART接收。 3. 应用场景 hal_uart_receive_dma函数适用于需要快速、稳定地接收大量UART数据的场景。例如,在工业自动化中,需要使用UART接收传感器发来的原始数据。使用DMA模式可以直接将数据保存到内存中,避免实时性问题。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值