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

2023-05-16

前言
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库串口中断接收哪些路子 第三弹》谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 收藏!了解UART总线工作原理看这一篇就够了!

    原文 xff1a 玩转单片机 2019 08 24 16 50 29 越学到后面 xff0c 基础知识更加不能忘记 xff0c 温故而知新 还记得当年的打印机 xff0c 鼠标和调制解调器吗 xff1f 他们都有巨大笨重的连接器和粗电缆 x
  • UART通信中流控RTS和CTS的理解

    一 流控 xff0c 顾名思义就是流量控制的意思 目的是协调收发双方 xff0c 使数据不会丢失 如果UART只有RX TX两个信号 xff0c 要流控的话只能是软流控 xff1b 如果有RX xff0c TX xff0c CTS xff0
  • arduino学习——UART串口通信

    Serial begin 初始化串口 用作串口的启动 xff0c 常放置在setup xff08 xff09 中 原型 xff1a Serial begin speed Serial begin speed config 参数 xff1a
  • FPGA uart串口收发verilog源码程序,适用于RS232 RS422

    FPGA uart串口收发verilog源码程序 xff0c 适用于RS232 RS422 xff0c 支持修改波特率 xff0c 数据位 xff0c 校验位 ID 3750670799663712
  • STM32—UART中断收发 Day4

    软件 xff1a STM32CubeMX xff0c MDK ARM 硬件 xff1a 蓝桥杯物联网Lora开发板 xff0c 板载芯片STM32L071 一 STM32CubeMX配置 1 先在连接 xff08 Connectivity
  • 【STM32】HAL库-SPI

    3线全双工同步传输 带或不带第三根双向数据线的双线单工同步传输 8或16位传输帧格式选择 主或从操作 支持多主模式 8个主模式波特率预分频系数 最大为fPCLK 2 从模式频率 最大为fPCLK 2 主模式和从模式的快速通信 主模式和从模式
  • 4.RTT-UART-中断接收及轮询发送

    本期博客开始分享RTT的UART xff0c 利用战舰V3的uart2来输入输出一些字符串 UART xff08 Universal Asynchronous Receiver Transmitter xff09 通用异步收发传输器 xff
  • Tm4c123GX(tiva)入门详细教程

    TM4C123GX系列 学了几个月的Tiva 总的来说这个款单片机功能还是比较强大的 下面我将以TM4C123GH6PM为例介绍其基本资源及工程建立 点亮板卡上的LED灯以及对基本的时钟配置 PWM uart Timer等方面来做简要说明
  • 嵌入式Linux应用开发笔记:串口

    文章目录 目的 基础说明 开发准备 设备树 应用程序 应用程序与演示 代码 演示 总结 设备树文件 目的 串口 UART 是嵌入式设备中比较常用的功能 这篇文章将记录下应用程序中串口操作相关内容 这篇文章中内容均在下面的开发板上进行测试 新
  • UART通信原理

    UART 通信格式 串口全称叫做串行接口 通常也叫做 COM 接口 串行接口指的是数据一个一个的顺序传输 通信线路简单 使用两条线即可实现双向通信 一条用于发送 一条用于接收 串口通信距离远 但是速度相对会低 串口是一种很常用的工业接口 I
  • Verilog功能模块——Uart收发

    摘要 本文分享了一种通用的Uart收发模块 可实现Uart协议所支持的任意波特率 任意位宽数据 5 8 任意校验位 无校验 奇校验 偶校验 1校验 0校验 任意停止位 1 1 5 2 的数据传输 此模块需要搭配FIFO使用 以消除发送端和接
  • 使用HAL库开发STM32:GPIO口基础使用与外部中断

    文章目录 目的 GPIO口基础使用 基础说明 初始化设置 输出与控制 读取端口值 GPIO口与外部中断 总结 目的 对于MCU来说GPIO口的使用是最基础的内容 仅使用GPIO口和延时等 就可以完成很多功能了 GPIO口基础使用 基础说明
  • Android架构分析之硬件抽象层(HAL)

    一 分析HAL module 架构 Android硬件抽象层有三个核心数据结构 分别是hw module t hw module methods t hw device t 定义在hardware libhardware include h
  • STM32 HAL库更改中断向量表的偏移地址

    以STM32F767为例 打开system stm32f7xx c文件 定位VECT TAB OFFSET 更改此宏定义的值 即可更改偏移量
  • Android Things:连接到串行调试控制台

    我一直在尝试连接到串行控制台树莓派 3 with 安卓事物 using USB to TTL cable从我的Linux Ubuntu 机器 尽管我按照文档连接了电缆 但执行时我得到的只是minicom命令如下 with 没有机会输入任何字
  • C、硬件抽象层中“extern”类型的变量

    我正在研究硬件抽象层 该 HAL 的目的是在 Linux 驱动程序和 MCU 驱动程序之间轻松切换 我正在研究SPI接口 下面是 打开 SPI接口的HAL函数的签名 哈尔 spi h spi handle t spi open spi po
  • GATT 配置文件和 UART 服务

    我是开发通过蓝牙连接到外围设备的移动应用程序的新手 我搜索到 GATT 是用于蓝牙LE 通信的相关配置文件 但我们的客户建议我们使用 UART 服务 现在我很困惑 1 这两件事是如何关联的 2 我们是否必须选择其中之一 如果是的话 每一个的
  • 通过 USB 模拟 UART

    有谁知道是否可以通过 USB 模拟 UART 简单串行发送和接收 这将如何实现 我在 Microchip 网站上找到了这个链接 但不是很容易找到 http www microchip com forums m522571 print asp
  • 嵌入式Linux中UART上的9位协议

    我正在尝试在嵌入式 Linux 中的 UART 上强制使用 9 位协议 目前我正在 am335x evm 板上对此进行测试 我计划使用坚持平价来做到这一点 理想情况下 我希望不需要实际修改 omap serial c 驱动程序的任何代码 9
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt

随机推荐