STM32使用HAL库UART接收不定长数据-1

使用STM32的HAL库实现UART串口不定长数据的接收

在这里插入图片描述

使用STM32的UART接收数据的时候,经常会遇到接收长度不固定的数据,比如一帧数据可能是10个字节,也可能是12个字节。这种数据称为不定长数据。

现有的很多通信协议是不定长的,比如modbus-rtu,其不同指令的长度是不同的,还比如与ESP8266通信使用的AT指令,其数据长度也不是固定的。

对于上面所说的不定长数据,可以使用状态机的方式判断一帧数据的结束,即在UART接收中断中按照通信协议的格式进行判断,确定哪个字节为帧的结尾。但是这种方式编程比较复杂。

有一个更简单的方法,就是判断一帧数据的时间间隔。默认两帧数据之间是有一定时间间隔的,比如10ms,那么当收到一个字节的数据后,5ms内没有收到新的字节,就认为一帧数据的结束。这种方法能够大大简化UART接收中断的处理函数。只需要在主流程里判断这一帧数据是否合法即可,能够很好的减低编程难度,也提高了MCU的处理效率。

那么如何实现这种编程方式呢?这里介绍一种通过HAL库实现的方式。

不定长数据接收方式

在STM32中最通用的方式就是借助UART的IDLE中断来实现,即结合UART的接收DMA和IDLE中断来实现。这种方式适用于STM32的所有芯片。

UART的IDLE中断是:当一个空闲帧被检测到时产生的中断。空闲帧如下图所示,为持续一个字节数据的高电平,就是空闲帧。也就是持续1个字节总线上没有数据就是一个空闲帧。
空闲帧

通过设置UART使用DMA进行接收,使得UART能够自动接收数据,并放入缓冲区中。当STM32检测到IDLE中断后,通过回调函数对接收到的数据进行处理。这样不用每接收到一个字节就处理,提高了MCU的运行效率。

HAL库函数

在STM32的HAL库中提供了现成的函数
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

这个函数使用DMA接收数据,当接收到Size个数据或者遇到IDLE中断时完成接收,然后调用下面的回调函数。

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

库函数使用注意实现

因此对一帧数据的处理函数要放到 HAL_UARTEx_RxEventCallback() 函数中。注意这个函数是在中断程序中,因此其处理函数要尽量的短。而且其参数Size 在DMA为循环模式时,指的是在接收缓冲区中下一个字节的写入索引位置,这个要注意。

在使用HAL_UARTEx_RxEventCallback() 回调函数的时候也要注意,这个函数在DMA的“半传输”中断和“传输完成”中断的时候也会进入此回调函数。比如说使用HAL_UARTEx_ReceiveToIdle_DMA() 函数接收20个字节,那么在接收到10个字节的时候会触发DMA“半传输中断”,接收到20个字节的时候会触发DMA的“传输完成”中断。DMA的“传输完成”中断不会产生影响,因为此时接收也已经完成了。但是“半传输”中断会影响处理,比如接收到不定长数据为12个字节,那么在收到10个字节的时候会进入HAL_UARTEx_RxEventCallback() 回调函数,在接受到12个字节的时候还会进入HAL_UARTEx_RxEventCallback() 回调函数。

防止DMA中断影响的方法

最简单的处理方法是关闭DMA的这2个中断,可以在调用HAL_UARTEx_ReceiveToIdle_DMA() 函数后,关闭这2个中断,参考如下代码:

  HAL_UARTEx_ReceiveToIdle_DMA(&huart1,recvbuf,20);__HAL_DMA_DISABLE_IT(huart1.hdmarx,DMA_IT_HT); //关闭半传输中断__HAL_DMA_DISABLE_IT(huart1.hdmarx,DMA_IT_TC); //关闭传输完成中断

这种方法既适用于将UART的DMA接收设置为循环接收,也适用于将DMA设置为单次接收模式。

如果不想关闭中断,而且DMA接收模式时单次模式,那么也可以在回调函数中进行判断,判断方法如下:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if(huart->RxState == HAL_UART_STATE_READY){// 接收完成}else{// 半传输中断}
}

在“半传输”中断进入时,接收没有完成,因此huart->RxState 的值是HAL_UART_STATE_BUSY_RX ,因此可以通过判断huart->RxState 的值是否等于HAL_UART_STATE_READY ,得知是因哪种事件进入的回调函数。

以上就是STM32使用HAL库实现UART不定长数据接收的一种方法,使用起来很简便。但是使用IDLE中断判断1帧数据结束还有有一定问题的,比如说在终端发送数据的时候,一帧数据中字节与字节之间的时间间隔比1个字节长了,就会造成误判。所以在使用的时候还是要注意区分的。在新出的STM32处理器中,针对这一点提供也新的解决方案,就是包含一个时间间隔可以设置的功能,这个将在下一次进行分享。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/21858.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker大学生看了都会系列(一、Docker介绍)

系列文章目录 第一章 Docker介绍 第二章 2.1 Mac通过Homebrew安装Docker 第二章 2.2 CentOS安装Docker 文章目录 前言Docker容器简介什么是Docker容器为什么要用Docker容器Docker容器的特性Docker容器对比VM(虚拟机)Docker容器三大组成要素镜像容器镜像仓库 Docker容器运行流程…

gradle构建项目简单入门

gradleProject 搭建gradle项目步骤 官网文档地址:https://docs.gradle.org/current/userguide/userguide.html Getting Started 1.Gradle核心内容 1.Gradle介绍 Project:类似模块划分Build Scripts:构建ProjectDependency Management&…

多线程新手村5--线程池

1.1 线程池是什么 线程诞生的意义是因为进程的创建/销毁开销太大,所以使用线程提高代码的执行效率;那如果想要进一步提升执行效率,该怎么办呢?有一个方法是使用线程池。 首先,什么是线程池:池就是池子&am…

Vue——样式绑定的几种方式

文章目录 前言往期回顾绑定对象绑定对象的另一种写法绑定数组数组与对象的嵌套 前言 样式绑定在vue中属于一种很常见的操作。在之前博客中针对样式的绑定操作,介绍了一个指令v-bind。缩写为:xxx。 vue 官网 样式绑定 往期回顾 先简单回顾下最开始绑定标签样式的操…

vue3_组件间通信方式

目录 一、父子通信 1.父传子( defineProps) 2.父传子(useAttrs) 3.子传父(ref,defineExpose ) 4.子传父(defineEmits) 5.子传父(v-model) …

车载测试面试题专题 - 如何测试蓝牙电话功能

现代车载系统中,蓝牙电话功能已经成为了一个必不可少的功能。它不仅提高了驾驶安全性,还提供了极大的便利性。作为车载行业的测试人员,如何全面、有效地测试车载蓝牙电话功能是我们工作的重要部分。因此在车载测试的面试过程中,蓝…

k8s怎么监听自定义资源的变更?(1)

这里我们通过 k8s的 code-generate来生成操作自定义资源的代码来监听变更 第一步下载工具 下载安装 k8s code-generate 查看我们的k8s版本 kubectl get node 输出结果为 可以看到我们的k8s版本为 v1.22.0 所以此时我们要下载与之对应的版本的code-generate git clone htt…

linux配置jupyternotebook

linux配置jupyternotebook 在Linux系统中配置Jupyter Notebook涉及多个步骤,包括安装pip、升级pip、安装Jupyter、修改配置文件以及设置密码等。以下是一个详细的配置过程: 安装pip:Linux系统自带Python,因此只需安装pip即可。可…

Pytorch实用教程:super(MLP,self).__init__()和super().__init__()有什么区别?

在 Python 中,super() 函数用于调用父类(超类)的方法。它的使用在继承关系中非常常见,特别是在初始化继承自父类的属性时。在 PyTorch 中,这通常见于初始化 nn.Module 类的子类。super() 的两种调用方式有微妙的差别,但在 Python 3 中常常用来达到同样的目的。 代码: …

深入解析 Web 开发中的强缓存与协商缓存机制

在 Web 开发中,缓存机制是提高页面加载速度和用户体验的重要技术。缓存分为两种主要类型:强缓存和协商缓存。本文将详细介绍这两种缓存机制的原理、实现方式及其区别,并演示如何在 <meta> 元素中和 Nginx 服务器中进行缓存控制。 强缓存 强缓存(Strong Caching)是指…

iPhone的5G设置怎么更改吗?设置好这些能够优化电池的使用寿命

随着5G技术的普及&#xff0c;iPhone用户现在可以享受到更快的网络速度&#xff0c;但这同时也带来了一个问题&#xff1a;如何在使用5G和保持电池寿命之间找到平衡&#xff1f;苹果公司通过引入“5G Auto”设置&#xff0c;为用户提供了一个智能的解决方案&#xff0c;但用户也…

【JAVA WEB实用与优化技巧】如何使用本地.bat/.sh脚本快速将服务发布到测试环境?

文章目录 普通方式的springboot 使用docker打包发布【手动构建镜像模式】1. maven 打包可运行jar包2.手动打包镜像3.运行容器 全自动化本地命令发布到远程服务的方式配置ssh信任公钥获取公钥git 获取公钥方式: 桌面右键 -> open git gui here -> help -> show SSH key…

Honor of Kings 2024.06.03 50star (S35) AFK

Honor of Kings 2024.06.03 50star (S35) AFK 来个赛季S35总结吧&#xff0c;这个赛季结束以后&#xff0c;可能要和【魔兽世界】一样AFK了&#xff0c;手游来说肯定没法子和WOW相比&#xff0c;干啥都是有队友才好玩。 我玩的基本都是肉&#xff0c;爆发强的英雄&#xff0c;最…

llama-factory微调大模型

一、背景 微调或者全量调大语言模型&#xff0c;还包括deepseek,想找个快速的微调教程&#xff0c;网上暂时没有 二、原理和步骤 原理&#xff1a;搭建环境太累了&#xff0c;还是docker环境镜像简单快捷 1、先下载模型 如果用本身的会自动从huggingface下载&#xff0c;这…

函数尾调用优化

尾调用 在编码过程中&#xff0c;经常涉及到函数调用函数&#xff0c;例如&#xff1a; function a() {b(); }function b() {}我们每次调用函数&#xff0c;都会在在内存中记录一个“调用帧”又成为“栈帧空间” 在上述例子中&#xff0c;调用a()函数&#xff0c;会在内存中记…

解决JSON.stringify 方法在序列化 BigInt 类型时的错误

今天学nest时&#xff0c;使用apifox发送请求获取数据&#xff0c;结果还一直报错&#xff0c;而且还是我从未见过的 Do not know how to serialize a BigInt at JSON.stringify (<anonymous>) at stringify&#xff0c; 我都是跟着人家敲的&#xff0c;我就纳闷了&…

vector的使用和实现

目录 一、vector的常用接口说明1.vector的介绍2.vector的使用2.1 vector的定义2.2 vector的遍历operator[ ]迭代器范围for 2.3 vector的空间增长问题size和capacityreserveresize 2.4 vector的增删查改push_back和pop_backinserterasefindsort vector的模拟实现1、基本成员变量…

代码随想录算法训练营第四十八天| km57. 爬楼梯、322. 零钱兑换、279.完全平方数

代码随想录算法训练营第四十八天 km57. 爬楼梯 题目链接&#xff1a;km57. 爬楼梯 确定dp数组以及下标的含义&#xff1a;j为背包的最大容量&#xff0c;dp[j]当容量为j有几种组合方式确定递推公式&#xff1a;dp[j]dp[j]dp[j-i]&#xff0c;不放当前数字组成目标值的种类必…

Linux基础操作——文件系统+find+head+tail

Linux基础操作——文件系统findheadtail 本文主要涉及LINUX的一些基础操作&#xff0c;文件系统与find命令head和tail命令 文章目录 Linux基础操作——文件系统findheadtail一、Linux下的文件类型二、ls -l 后各列的解释三、 find 查找四、 head 与 tail 查看文件的头部尾部 一…

Nginx Rewrite

Nginx Rerite概述 Nginx Rerite基本操作 location与rewrite的区别 location 通过前缀或正则匹配用户的URL访问路径做页面跳转、访问控制和代理转发 rewrite 对用户的URL访问路径进行重写&#xff0c;再重定向跳转访问重写的路径 Nginx正则表达式 校验数字的表达式 数字&a…