HAL_UART_IRQHandler(UART_HandleTypeDef *huart)里面的中断接收函数(作者自己生成的函数代码,中间有关闭接收中断,但是原子教程中没有关闭中断的语句注意区别)

前言
1、UART_Receive_IT
2、HAL_UART_Receive
3、 HAL_UART_Receive_IT
前言
看了很长时间串口中断的HAL库,最容易混淆的就是函数的名称,主要集中在UART_Receive_IT、HAL_UART_Receive、HAL_UART_Receive_IT。有点傻傻分不清楚,接下来分析一下他们各自的含义。

1、UART_Receive_IT
函数代码如下:

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)//判断CR寄存器的第12位W是否为1 ,为1则代表设置为一个起始位, 9个数据位, n个停止位
    {
      tmp = (uint16_t *) huart->pRxBuffPtr;//将pRxBuffPtr这个缓冲区的首地址先转化为16位的整型再赋值给tmp
      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);//将数据寄存器DR的值载入到huart的缓冲区指针所指向的位置
      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }

    if (--huart->RxXferCount == 0U)
    {
      /* 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这个变量存在与否没有什么关键含义,因为if-else的两种情况都是将USART的DR寄存器的值存放到pRxBuffPtr这个缓存区,只不过这个pRxBuffPtr是一个指向缓存区首地址的指针。这个函数是把所有输入的数据一个一个存放到缓存区中,也就是,一个数据对应一次中断,直到确认所有的数据都存放到缓存区中,huart->RxXferCount对应的值也会自减为0,此时会执行3个__HAL_UART_DISABLE_IT函数来关闭中断(我也不知道为什么HAL库要这样设置),之后会进入回调函数,我们只需在回调函数中写入我们的用户代码即可。但是其中一定要包含打开中断的函数,因为__HAL_UART_DISABLE_IT这个函数已经关闭中断。可以仔细品读这个函数,详细的注释我已经写在了代码块里面,有了注释应该就不难理解。

2、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;
  }
}

这个函数和UART_Receive_IT这个函数内容大同小异,只是少了回调函数而已,可以参考上面的函数。

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

PS:很多人一直觉得用户代码可以在中断函数里面写,但是我们一般不写在中断函数中,而是在回调函数里面写。如果写在中断函数中,和标准库没什么两样。而HAL库将函数都已封装完整,回调函数完好地提供一个API接口,供用户使用。回调函数和普通函数还是有一定区别的,读者可以查阅其他资料,在此不再赘述。

想要更加细致地了解这三个函数,推荐一下这篇文章:关于HAL库串口中断接收哪些路子 第二弹
————————————————
版权声明:本文为CSDN博主「津野自渡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45413245/article/details/104585026

关于HAL库串口中断接收 第二

奈何篇幅限制,只传了一小部分上去,今天陆续把接下来的也都发上去,记得第一的结尾说道了串口处理结构体,`UART_HandleTypeDef huart1;`这里暂且不用,往下看,声明三个函数,,有两个带static修饰的要注意,封装好的接口,对外部透明,别想着在main.c外面调用。
现在进去主函数,HAL库初始化略过,时钟初始化略过,引脚初始化略过,都是在MX里配置好的,不管它们,现在到了串口初始化,这个本该也略过的,但是里面涉及了一些参数,后期封装时如果想修改例如波特率之类的,可以借鉴一下,也一笔带过。
在一系列初始化完成后,我们需要自己操作串口了,调这个东东`while(HAL_UART_Receive_IT(&huart1,&RxByte,1)==HAL_OK);`
这个函数的作用为打开串口接收中断,我们之前在MX里设置的global interrupt 是UART的总中断开关,这里单独打开接收中断(本人亲测,不打开是进不去中断IRQ函数滴)
,第一个参数指明UART控制器,第二个参数是一个地址,用来存储接收到的数据,第三个是接收个数,这三参数我一个一个说:
1.先说这里为何要 while();经我了解,HAL_UART_Receive_IT()函数只会返回3种结果:错误,正常,忙碌。体现到代码里,分别是HAL_ERROR、HAL_OK、HAL_BUSY。如果参数错误,直接返回HAL_ERROR如果参数正确,第一次会返回HAL_OK,第二次会返回HAL_BUSY,原因在这里



跟踪这两枚举常量,值不去管他,看它的解释



意思是这样的:HAL_UART_STATE_READY外设已被初始化并且备用,HAL_UART_STATE_BUSY_TX数据发送进程正在进行中,看到这里,应该就差不多明白了while();的意义。在我们打开串口接收中断的初次,系统标志位没有被置位,返回了HAL_OK,当第二次进入的时候,由于外设已被初始化,直接跳出判断语句,返回HAL_BUSY。这样做的好处是:我表达了一个主观意愿,我要打开UART接收中断,你只有打开了或者打开出错了,才能从while()里出来,你要是尝试去打开了,结果又没出错又打不开,你就卡到while()里去吧!这种状况很少见,除非寄存器坏了或初始化错误!理论通了,你也可以表达你的主观意愿,例如:while(HAL_UART_Receive_IT(&huart1,&RxByte,1)==HAL_ERROR);`
例如:
 

do{
    if(huart.State== HAL_UART_STATE_BUSY_TX)
    {
        continue;
    }
}while(HAL_UART_Receive_IT(&huart1,&RxByte,1)!=HAL_BUSY);



打开接收中断后,理论上已经可以收到数据了,用串口助手发送一个字符或数字,可以看到程序进到USART1串口中断处理函数`void USART1_IRQHandler(void)`,类似这样的中断处理函数是有限的,根据芯片资源固定,声明在启动代码文件里它在内部自己调用了`HAL_UART_IRQHandler(&huart1);`MX生成的中断处理函数,看他传参的形式,可以了解到看来串口的中断处理九成九是共用的这个函数,再次给ST的可移植性点个赞!
继续跟进,HAL_UART_IRQHandler内部是一大堆的标志位检验,什么中断奇偶校验错误、中断帧错误巴拉巴拉之类的不去管它,主要看看被控UART到底发生的是神马中断类型和中断源,这时,来波小高潮,如果按照上面所说,串口助手发送的是一个字符,那么恭喜你,你将跳过所有错误,进入`UART_Receive_IT`球洞,并成功的接收到数据而不报错。这个 一个字符,主要由`HAL_UART_Receive_IT`最后一个参数决定。那如果发的不是一个字节呢?呵呵,你的第一个数据会放到指定地址的内存空间,之后程序会报这个错Over-Run,然后清掉你的UART ORE标志位,意味着你再也收不到了。那我只能收一个数据吗?我的回答是:第一弹里说过,我们平时用到的数据位,也就8位,每次发过来的数据,必定是一帧一帧的,所以我们以往的中断接收,都是发来一帧,立刻中断关闭,拿出数据,打开中断,处理数据。你发10个字符,他要关中断10次,开中断10次。而ST新的HAL库,是这么做滴:开中断等数据,数据来了之后,关闭中断,如果数据个数不够我传给它的接收个数,继续接收,什么时候收够了,再打开中断,处理数据这样,接收了10个数据,只中断了一次,避免了频繁产生中断的一系列问题,你问我依据在哪里?这样搞有木有不好的地方?咱第三弹再聊!

发现打字真的好累,当你看到这行字的的时候,找 《关于HAL库串口中断接收哪些路子 第三弹》谢谢!

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值