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)里面的中断接收函数(作者自己生成的函数代码,中间有关闭接收中断,但是原子教程中没有关闭中断的语句注意区别) 的相关文章

  • HAL_DMA_IRQHandler()代码详细分析

    这里使用ADC1联动DMA1 xff0c 开启半传输中断 传输中断 debug时 xff0c dma也一直在工作 xff0c 所以半传输中断 传输中断会同时生效 1 adc1使用了DMA1 Stream0 xff0c Instance表示如
  • STM32 硬件UART接收超时检测设置

    STM32 硬件UART接收超时检测设置 本文作者 智御电子 xff0c 期待与电子爱好者交流学习 应用场景 在uart应用中有时候需要进行双工通信 xff0c 主机需要对从机的数据进行接收超时检测 xff0c 例如modbus协议 xff
  • UART串口通信

    目录 一 通信特点二 通信应用三 接线示意图三 UART通信协议四 STM32F4 串口使用1 资源分布2 特性3 UART框图4 使用方法5 相关库函数6 函数实例 五 实战 上位机控制开发板小灯 一 通信特点 异步 串行 全双工 一般描
  • Ubuntu 20.04下配置 HAL 汇编开发环境教程

    目录 安装解压配置环境变量测试 案例 安装 环境 xff1a Ubuntu 20 04 安装包 xff1a linux64 hla tar gz 这里安装的是64位的 xff0c 具体与linux hla tar gz有什么不同 xff0c
  • 一文理解UART通信

    还记得当年的打印机 xff0c 鼠标和调制解调器吗 他们都有巨大笨重的连接器和粗电缆 xff0c 并且必须拧到你的电脑上 这些设备正是使用UART协议与计算机进行通信 虽然USB几乎完全取代了旧的电缆和连接器 xff0c 但UART绝对没有
  • C51_day5:串口通信UART

    3 1 串口基本认知 串行接口简称串口 xff0c 也称串行通信接口或串行通讯接口 xff08 通常指COM接口 xff09 xff0c 是采用串行通信方式的扩展接口 串行接口 xff08 Serial Interface xff09 是指
  • HAL_UART_IRQHandler(UART_HandleTypeDef *huart)里面的中断接收函数

    目录 前言1 UART Receive IT2 HAL UART Receive3 HAL UART Receive IT 前言 看了很长时间串口中断的HAL库 xff0c 最容易混淆的就是函数的名称 xff0c 主要集中在UART Rec
  • Node.js笔记:SerialPort(串口)模块使用(基于9.x.x)

    文章目录 目的 模块安装 基础使用 扫描端口 打开端口 发送数据 接收数据 错误处理 数据解析器 SerialPort类 构造方法 属性 事件 方法 命令行工具 总结 目的 上位机与各种电路模块间常常采用串口进行通讯 Node js中可以使
  • 51单片机串口通信(UART)

    项目描述 1 串口工作方式为1 8位UART 波特率可变 无校验位 2 通信数据格式为 1位起始位 8位数据位 1位停止位 3 上电后MCU给上位机发送问候语 之后等待上位机的信息 MCU和上位机的通信为收发交替进行 MCU接收到的信息会实
  • MicroPython串口数据转换技巧

    技巧一 还原从串口接收的HEX字符串 首先上官方文档 确认接收数据类型为bytes 串口读来的bytes类型HEX数据转换成HEX类型字符串转换函数 ByteToHex的转换 返回数据16进制字符串 def ByteToHex bins r
  • UART串口协议

    通用异步收发传输器 Universal Asynchronous Receiver Transmitter 通常称作UATR 是一种异步收发传输器 将数据由串行通信与并行通信间做传输转换 作为并行输入称为串行输出的芯片 UART是一种通用串
  • CUBEMX+HAL库实现STM32串口(不定长度)收发

    CUBEMX HAL库实现STM32串口接收 不定长度 并发送 首先新建一个CUBEMX工程 选择你自己开发板的芯片型号 设置时钟和串口 设置时钟为72MHZ 设置串口中断 点击小齿轮生成KEIL5的工程 设置工程名称及存放位置 点击Ope
  • UART ISR Tx Rx 架构

    我让事情变得复杂了吗 我正在构建我的代码 以便通过 UART 从 8051 micro 与外围设备进行通信 外设响应主机的命令 一次只能响应一个命令 这是一个简单的发送和接收协议 tx1 rx1 tx2 rx2 tx3 rx3 每个 TX
  • 以字符串形式接收数字(uart)

    我正在尝试通过 uart 接收一个包装为字符串的数字 我发送数字 1000 所以我得到 4 个字节 空字符 但是 当我使用 atoi 将数组转换为数字并将整数与 1000 进行比较时 我并不总是得到正确的数字 这是我用于接收号码的中断处理函
  • IOError:[Errno 2]没有这样的文件或目录(当它确实存在时)Python [重复]

    这个问题在这里已经有答案了 我正在通过 python 中的 uart 传输文件文件夹 下面您可以看到简单的功能 但有一个问题 因为我收到如标题所示的错误 IOError Errno 2 No such file or directory 1
  • C、硬件抽象层中“extern”类型的变量

    我正在研究硬件抽象层 该 HAL 的目的是在 Linux 驱动程序和 MCU 驱动程序之间轻松切换 我正在研究SPI接口 下面是 打开 SPI接口的HAL函数的签名 哈尔 spi h spi handle t spi open spi po
  • C语言UART通信(十六进制)

    我想向写入函数发送一个十六进制值 例如 0 90 这是因为需要通信的设备接收到的是十六进制数的命令 未使用的变量在测试时出现 并注释为丢失十六进制值 稍后将被删除 如何编写具有字符串以外的十六进制值的写入函数 对于初学者 请告诉我们如何通过
  • 如何将 microbit 与 BLE 连接并监听按钮按下事件?

    2021 年 11 月 28 日编辑 如果您需要使用蓝牙低功耗将 microbit 连接到计算机 并在单击按钮时执行操作 直接跳并跟随 ukBaz https stackoverflow com users 7721752 ukbaz的回答
  • 通过 USB 模拟 UART

    有谁知道是否可以通过 USB 模拟 UART 简单串行发送和接收 这将如何实现 我在 Microchip 网站上找到了这个链接 但不是很容易找到 http www microchip com forums m522571 print asp
  • 使用 /dev/tty* 进行 9 位 uart 仿真

    我有一个不常见的协议 它需要 9600 波特率 9 位和 1 个停止位 我找不到任何可以实现此发送 接收的驱动程序 我可以寄东西到 dev tty 用于模拟这些查询 我应该发送什么 如何模拟 9600 波特率 您可以使用粘性奇偶校验 也称为

随机推荐