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函数里面首先写下它,否则无法进入串口中断(亲测如此),其次还要在回调函数里面添加这个函数(因为之前就说过一旦进入回调函数,串口中断就会关闭),为了下一次接收数据考虑,需要这么做

HAL提供了多种串口接收函,其中包括阻塞式接收函HAL_UART_Receive和中断式接收函HAL_UART_Receive_IT。阻塞式接收函是一个不常用的函数,通常情况下,串口接收会使用中断函数HAL_UART_Receive_IT。中断式接收函的主要流程如下: 1. USART1_IRQHandler:这是一个由硬件调用的函数,不是HAL函数。无论是寄存器编程还是固件编程,都需要调用此函数。 2. HAL_UART_IRQHandler:根据中断类型(发送中断还是接收中断)来判断调用哪个函数。 3. UART_Receive_IT:此函数可以指定,每收到若干个据,调用一次回调函数。每收到一个字节,都会将接收计器减1,如果接收计器为零,则调用串口接收回调函数HAL_UART_RxCpltCallback。 4. HAL_UART_RxCpltCallback:这是一个弱函数,用户可以在此函数中编写业务逻辑。在此函数中,可以清除中断标志,并进行其他操作。 在主函数中,可以实现"串口应声虫"的功能,即收到什么就发送什么。当串口据接收完成时,可以使用HAL_UART_Transmit函数将接收到的据发送出去,并将组、计器和标志恢复到初始状态。 示例代码如下: ```c // 主函数 while(1) { if(UART1_Rx_flg) { HAL_UART_Transmit(&huart1, UART1_Rx_Buf, UART1_Rx_cnt, 0x10); // 发送接收到的据 for(int i = 0; i < UART1_Rx_cnt; i++) { UART1_Rx_Buf\[i\] = 0; } UART1_Rx_cnt = 0; UART1_Rx_flg = 0; } } // 串口接收函,开启串口中断 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); ``` 以上是关于HAL串口接收函的简要介绍和示例代码。 #### 引用[.reference_title] - *1* *2* [HAL教程6:串口据接收](https://blog.youkuaiyun.com/geek_monkey/article/details/89165040)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [(Hal)常用串口函数](https://blog.youkuaiyun.com/lj1986817902/article/details/87633865)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值