HAL库的串口基础学习(包含串口接收不定长数据的实现)

2023-05-16

HAL库的串口基础学习(1)

HAL库有一个特点就是对于许多外设的初始化以及功能操作,都提供有一个weak版本的函数,这是充分的展现出库名字的含义(Hardware Abstraction Layer)硬件抽象层。
例如串口的HAL_UART_MspInit()函数和HAL_UART_MspDeInit()函数等,这些都可以供用户在需要时在stm32f1xx_hal_msp.c中进行重写实现功能。

用串口初始化来举例子,用Cube配置UART1使能并生成代码后可以看到有三个关键函数:

1、void MX_USART1_UART_Init(void)

这个函数是Cube配置完成后自动帮我们生成的,存在于用户文件usart.c中(如果Cube中有配置选项生成用户.c文件,否则在main函数中)。里面主要是将USART的结构体初始化成USART1以及我们设置好的参数),并且在最后有一个语句:if (HAL_UART_Init(&huart1) != HAL_OK)通过这个来调用HAL_UART_Init函数。

2、HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)

这个函数在HAL库文件stm32f1xx_hal_uart.c中,在上一个函数 MX_USART1_UART_Init(void)中有被调用,这个函数将初始化UART1,使能UART1,而重点是里面有调用一个函数HAL_UART_MspInit(huart)

3、__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)

这个函数也在HAL库文件stm32f1xx_hal_uart.c中,它是weak修饰的,也就是可以重写。我们发现在Cube生成的用户文件usart.c文件中有对这个函数进行重写,主要实现对UART1的TX、RX引脚的IO口配置,以及开启串口中断等设置。

由此,HAL库的串口外设初始化操作就清晰了,也就是说如果我们使用Cube生成代码,要完成对串口的初始化,就要自行对串口的结构体参数进行配置,并对HAL_UART_MspInit函数进行重写。到这里,也能看出HAL库的优越性,用户只需在msp.c中对要实现的函数进行重写,调用相关函数既可实现。

串口初始化完成后,下一步就是实现串口的收发数据,轮询发送很简单,这里主要研究下串口的中断接收,和中断接收有关的函数有下面几个:

1、HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

函数有是三个参数,分别是串口结构体,要传输的数据指针以及待传输数据的大小,这其实是对中断接收的一个配置函数,指定当接收到Size大小的字节后就产生一次中断进入中断处理。
对比以前的库函数的串口接收协议,可以将这里的Size设置为1,也就是每次接收一个字节就产生一次中断,并将数据存储到pData指向的地址中。这就类似于库函数中的每次中断将数据赋值给临时变量res。需要注意的是,这个函数调用一次只适用于一次中断,如果要连续接收数据的话应该在重写函数中调用它。
如果数据定长接收的话也可以在设置中断时直接使用设置长度,如接收定长的size为8就可以设置HAL_UART_Receive_IT(&huart, &DataBuff, 8)设置后串口接收回调函数就在接收到8字节后进入。
如果串口接收的数据为不定长数据可以通过三种方法来实现,定时器和接收中断配合、DMA和串口中断配合、空闲中断实现。稍后会线介绍下空闲中断的实现方法。

2、USART1_IRQHandler(void)    和    HAL_UART_IRQHandler(&huart1)

第一个是在it.c中的通用的硬件中断入口函数,其中在里面调用了HAL_UART_IRQHandler(&huart1),也就进入了UART1的中断处理,HAL_UART_IRQHandler函数在stm32f0xx_hal_uart.c文件中,主要进行中断标志的判定,当判定接收到数据时,调用UART_Receive_IT(huart)函数。

3、HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)

这个函数也在stm32f0xx_hal_uart.c中,这才是真正的处理数据的函数,读取接收寄存器,将数据赋值给结构体huart的pRxBuffer并清除中断标志,当接收数据完成并把串口的接收标志位huart->RxState = HAL_UART_STATE_READY;这个状态很重要,这个状态会直接影响下次中断的使能,因为在HAL_UART_Receive_IT函数中会对此状态进行判断成立时才会对中断进行使能。随后调用了一个函数HAL_UART_RxCpltCallback(huart);

4、__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

这个是用户真正可以进行重写实现自己所需功能的函数,在msp.c中将其重写。前面我们设置了参数使得每接收一个字节就产生中断,于是可以重写这个函数,在里面将每次接收到的数据存放在自己定义的UART1RXBuffer中。另外,上面有提到配置中断参数的函数 HAL_UART_Receive_IT()每次调用仅适用于一次中断,如果要实现连续采用中断接收数据,也要这个函数中调用HAL_UART_Receive_IT()。因为这个回调函数是每次中断发生都会调用,也就相当于每次发生中断处理完数据后又将中断打开,从而实现连续中断接收数据。
下面介绍一下利用空闲中断实现不定长数据的接收。
1、在HAL_UART_MspInit函数中使能中断。HAL_UART_Receive_IT( &huart1, aucUsart1RecBuff, DATA_FIRST_LEN );
DATA_FIRST_LEN 数据长度可以是接收数据缓冲区的大小,不用考虑接收数据的长度,只要保证此数值大于接收数据的可能长度即可。
2、在stm32f1xx_hal_uart.c文件中找到UART_Receive_IT函数(在接收到数据时会在HAL_UART_IRQHandler中调用)添加__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);

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) 
  {
    //Add for kong 20190106
    __HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);
      
    if(huart->Init.WordLength == UART_WORDLENGTH_9B)
    {
      tmp = (uint16_t*) huart->pRxBuffPtr;
      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);
      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }

    if(--huart->RxXferCount == 0U)
    {
      /* Disable the IRDA 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;

      HAL_UART_RxCpltCallback(huart);

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

3、在uart.c文件中添加void HAL_UART_IDLECallback( UART_HandleTypeDef* huart )回调函数

void HAL_UART_IDLECallback( UART_HandleTypeDef* huart )
{
    __HAL_UART_CLEAR_IDLEFLAG(&huart1);                                     //清除空闲中断flag
    __HAL_UART_DISABLE_IT(&huart1,UART_IT_IDLE);
    __HAL_UART_DISABLE_IT(&huart1,UART_IT_RXNE);
    
    huart1.RxState = HAL_UART_STATE_READY;                                  //此状态在整个接收数据缓冲区满的时候会被置位,但是空闲中断响应的时候很可能数据缓冲区还没有被填充满,如果没有此操作会引起下次开中断的失败
    
   /*
  添加用户程序*/
    HAL_UART_Receive_IT( &huart1, aucUsart1RecBuff, DATA_FIRST_LEN );
}

4、修改中断函数,也可以修改HAL_UART_IRQHandler(UART_HandleTypeDef *huart)在函数中添加此判断。

void USART1_IRQHandler(void)
{
    uint32_t isrflags   = READ_REG(huart1.Instance->SR);
    uint32_t cr1its     = READ_REG(huart1.Instance->CR1);
    
    if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))
    {
        HAL_UART_IDLECallback(&huart1);  
    }
    
	HAL_UART_IRQHandler(&huart1);
}

以上就是利用串口空闲中断完成的接收不定长数据的程序。

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

HAL库的串口基础学习(包含串口接收不定长数据的实现) 的相关文章

  • 宏定义 宏参数

    带参数的宏定义 xff0c 利用宏参数创建字符串 运算符 看看以下两个宏定义 xff1a define PSQR x printf 34 The square of x is d n 34 x x define PSQR x printf
  • 舵机的PD控制

    PID 舵机以及差速PD调节 span class token keyword struct span span class token class name PID span span class token punctuation sp
  • 位置环与速度环的串级PID

    WHEELTEC的串级pid参考代码 span class token keyword float span Position KP span class token operator 61 span span class token nu
  • 智能车摄像头算法——圆环元素

    入环 1 入环的函数 xff08 1 xff09 搜上下边线 xff08 2 xff09 找凸起的弧 xff08 3 xff09 两点之间补线 xff08 4 xff09 判断上线是否单调 2 找圆环3 补线入环出环 1 入环的函数 xff
  • ROS的代价地图与AMCL定位原理

    地图服务与AMCL定位 costmap xff08 代价地图 xff09 AMCL定位 xff08 自适应蒙特卡罗定位 xff09 costmap xff08 代价地图 xff09 1 地图文件格式 xff1a 除了pgm xff08 便携
  • ROS路径规划算法

    ROS路径规划算法 全局路径规划Dijkstra算法A 算法 局部路径规划DWA算法TEB算法 全局路径规划 提供Dijkstra和A算法 xff0c 默认使用Dijkstra Dijkstra是把从出发点到终点的整个栅格地图上的所有的点
  • STM32常用功能配置

    STM32基本代码 设置外部中断定时器中断定时器产生pwmAD多通道转换DMA 43 AD扫描多通道转换iic协议读取数据SPI协议读取数据 设置外部中断 中断优先级分组 外部中断 AFIO作用 注意 xff1a 1 相同的Pin不能同时触
  • Ogre-渐变背景色(gradient background)的实现

    转载自 xff1a http blog csdn net hefee article details 6287341 背景色在ogre里面是通过ViewPort类中的setBackgroundColour xff08 xff09 这个成员函
  • Qt::WindowFlags

    查了些资料 xff0c 整理了一下 xff0c 以备查询 枚举类型 Qt WindowFlags低位的一个字节用于定义窗口部件的窗口类型 Qt WindowFlags的高位字节定义了窗口提示 xff0c 窗口提示能够进行位或操作 xff0c
  • java学习记录8

    什么是File 文件夹和文件 xff1a 文件夹是用来组织和管理磁盘文件的一种数据结构 文件是在电脑中 xff0c 以实现某种功能或某个软件的部分功能为目的定义的一个单位 xff0c 文件是由文件名和图标组成 xff0c 一种类型的文件具有
  • 保护模式编程之(一)——分段机制与GDT/LDT

    概述 xff1a 若想理解操作系统程序中的启动相关的部分 xff0c 必须要理解保护模式下的编程 xff0c 而分段机制是保护模式编程下的基础 另外 xff0c 由于实模式与保护模式的不同 xff0c 对保护模式下的分段机制更需要注意 同时
  • C++ 网络编程

    socket通信 xff1a socket 创建TCP套接字 bind 将套接字绑定到本地地址端口上 listen 监听端口 connect accept 接受用户请求 xff0c 返回对应此连接的新套接字 read write close
  • ROS学习(2)——rviz与gazebo问题记录

    ROS学习 xff08 2 xff09 rviz与gazebo问题记录 继续按照教程学习 xff0c 踩了很多坑 1 工作环境配置问题 实践6 2 4在rviz中显示模型时 xff0c 运行launch文件出现如下报错 原因 xff1a 出
  • VINS-Mono 代码详细解读——基础储备:在线Cam到IMU的外参标定 InitialEXRotation类

    本讲还是为了estimator类中最主要的函数processImage xff08 xff09 做知识储备 前面两讲知识储备主要讲了IMU预积分相关的integrationBase类以及图像特征点管理器feature manager cpp
  • VINS-Mono 代码详细解读——回环检测与重定位、四自由度位姿图优化

    本文主要介绍VINS的闭环检测重定位与位姿图优化部分 xff0c 作为系列文章的最后一节 回环检测的关键就是如何有效检测出相机曾经经过同一个地方 xff0c 这样可以避免较大的累积误差 xff0c 使得当前帧和之前的某一帧迅速建立约束 xf
  • VS Code创建、调试ROS项目

    前言 xff1a 在vs code下配置ROS项目开发的环境 包括catkin创建编译工作空间 xff0c 创建ROS项目 xff0c 调试ROS节点 一 创建工作空间 首先创建一个cMake工作空间 xff0c 用到了catkin mak
  • 《wiki官网教程》2 编写简单的服务器service和客户端 client(C++)

    服务 xff08 services xff09 是节点之间通讯的另一种方式 服务允许节点发送请求 xff08 request xff09 并获得一个响应 xff08 response xff09 之前讲的是两个节点如果要通信需要经过话题to
  • 进程和线程主要区别与定义

    抽象理解 直接上图 xff0c CPU是工厂 电力资源是cpu 时间片 进程是车间 线程是车间工人 操作系统的资源分配与调度逻辑 以多进程形式 xff0c 允许多个任务同时运行 xff1b 以多线程形式 xff0c 允许单个任务分成不同的部
  • Ogre场景中管道透明之后为黑色的问题

    depth write 设置此渲染通路的深度缓冲写入的状态是打开状态还是关闭状态 格式 depth write lt on off gt 如果深度缓冲写入处于打开状态 xff0c 无论何时一个像素想要写入画面缓冲 xff0c 深度缓冲都会更
  • 移动机器人定位方法概述

    引言 自主移动机器人导航过程需要回答三个问题 xff1a 我在哪里 xff1f 我要去哪儿 xff1f 和 我怎样到达那里 xff1f 定位就是要回答第一个问题 xff0c 确切的 xff0c 移动机器人定位就是确定机器人在其运动环境中的世

随机推荐