STM32CubeMX与HAL库开发教程八(串口应用/轮询/中断/DMA/不定长数据收发)

目录

前言

初识串口-轮询模式

串口中断模式收发

串口DMA模式

蓝牙模块与数据包解析


前言

前面我们简单介绍过串口的原理和初步的使用方式,例如怎么配置和简单的收发,同时我们对串口有了一个初步的了解,这里我们来深入的来使用一下串口

初识串口-轮询模式

这里我们不介绍如何使用STM32CubeMX配置串口,我们默认使用USART1同时波特率为115200,首先让我们来使用串口发送一些数据

这里我们定义了一个数组,数组的内容为Hello Wrold,在主循环中我们调用了一个函数

HAL_UART_Transmit(huart,pData,Size,Timeout)

huart      :第一个参数为要操作串口的指针,这里要填写 &huart1 如果设置2那就换成 &huart2

pData     :第二个参数填的是需要发送信息的指针  (uint8_t *)message

Size        :第三个参数填的是数据字符串的长度 我们可以使用关键字 strlen(message) 识别长度

Timeout :第四个为发送数据的时间,超过这个时间就停止发送  单位 毫秒

完整的代码例子 HAL_UART_Transmit(&huart1 , (uint8_t *)message , strlen(message) , 50 )

 发送的解决了之后,我们来使用轮询模式接收一些数据

让我们讲发送的代码注释掉,同时再定义一个接收用的数组,这里我们直接使用uint8_t来定义,他和char是相同的作用,串口接收的参收与串口发送的参数一模一样

第一个参数也使用哪个串口来接收数据,也是需要填地址,第二个参数是用来接收数据的指针,也就是receiveData的首地址,第三个还是一样,最后一个参数我们设置为 HAL_MAX_DELAY 为最大时间,就一直等待,没有超时时间,如果使用uint8_t定义数组,第二个参数就不需要强制类型转换了  

HAL_UART_Receive(&huart1 , receiveData , strlen(receiveData) , HAL_MAX_DELAY )

编写一个使用串口控制红绿蓝三个灯的颜色

上电默认电平为高,如果串口第二位接收到0了则将电平拉低,同时串口根据第一位接收的数据来控制红绿蓝灯的亮灭

轮询模式不够灵活,同时会阻塞程序或者程序会阻塞串口收发,而且只能确定串口数据长度,我们接下来会学习中断模式串口收发数据

串口中断模式收发

 中断的发送函数与普通轮询模式的发送函数差别不大只在后面加上了IT,同时没有超时时间

HAL_UART_Transmit_IT(&huart1 , receiveData , strlen(receiveData) )

接收的函数也大致相同,

HAL_UART_Receive_IT(&huart1 , receiveData , 2)

不过需要注意由于不会堵塞数据,也就是还没等到接收到数据,就会接着向下执行下去,这就会造成执行到下次循环时,可能上次的数据还没有接收完,就又开始执行串口中断接收了,所以我们需要放在循环前,只执行一次,注意接收一次之后需要重新开启接收中断

而当我们需要再次接收时,我们可以在串口中断回调函数中再次开启接收中断就可以了

我们将while中的逻辑代码复制粘贴到串口接收中断回调函数中,这样可以帮助节约cpu资源,使用中断非常简单,在cubemx中勾选开启中断,同时在函数后面加上_IT后缀,以及将处理逻辑移入到串口中断函数中即可,我们成功的解决了串口堵塞程序的问题,那如何接收不确定长度的数据呢,能否进一步减少CPU的占用呢

串口DMA模式

虽然中断方式相较于轮询模式已经节约了很多cpu资源,但对于cpu本身来说,却是屡屡被打断,疲于在中断搬运数据与处理正常任务代码间辗转反侧,那我们能否进一步提升效率,给cpu找一个帮助搬运数据的小助手,这时候DMA的用处就显现出来了,DMA全称叫直接内存访问

打开cubemx,找到USART1设置界面,找到DMA Settings,点击下面的Add就可以添加DMA通道

在这里设置的DMA发送通道,DMA通道7为专门为USART2发送设置的,数据搬运方向由内存向外设搬运,也就是从内存变量向发送数据寄存器进行搬运,优先级默认为低,可以自己选择,其他参数CubeMX已经帮我们配置好了,默认即可,之后我们再添加一个USART2_RX的通道就行了

DMA模式的发送函数也就是把中断的_IT切换成了DMA,参数依旧是用哪个串口发送,要发送的变量以及发送的字节数,接收函数也是一样

需要注意的是,即使使用了DMA,其实还是有中断参与其中的,例如中断接收回调函数RxCpltCallback还是由中断触发,当然这次就不是串口的中断了,而是DMA传输中断,说完了DMA的用法,我们来看看如何接收不定长数据

其实接收不定长数据非常简单,主要靠的是串口空闲中断,此中断的触发条件与接收的字节数无关,只有当RX引脚上无后续数据进入,也就是串口接受从忙率转变为空闲时才会触发,这里我们就可以认为,当串口空闲中断时,一帧数据包就发送完成了

这里我们需要将串口接收函数替换为HAL封装的一个拓展函数

HAL_UARTEx_ReceiveToIdle_DMA(&huart2 , receiveData , 50)

我们可以注意,这个函数也有阻塞DMA和中断三个版本,我们这里选择DMA模式,由于是接收不定长数据,这里我们可以将receiveData这个数组改大一点,改成50,而这函数的最后一个参数,是填写一次能接收的最大数据长度,一般就是填写接收数组的长度,来避免接收数据太长而导致数组越界

此时我们要用的中断函数也变了

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart , uint16_t size)

这个回调函数和中断回调函数的区别就是他的参数中多了一个 size 长度,因为之前中断回调函数是接收定长数据,我们已知了数据长度,我们需要size入参来得知本次到底接收了几字节的数据

我们在下面这个回调函数中先判断一下是不是USART2接收了,我们将接收到的函数重新发送回串口助手中,要注意这里发送函数的第三个参数要改成回调函数参数中的size,来发送与接收相同的字节数,最后别忘记再次开启接收中断

可以发现,我们再次发送R1,发现数据返回来了,但是灯没有点亮,也就能证明使用ReceiveToldle函数后,不再调用RxCpltCallback回调函数,而是使用了RxEventCallback回调函数,我们再尝试将数据增长,发现数据正常返回,这就说明我们完美的实现了不定长数据接收

我们需要注意在这个函数中需要重新开启接收中断,但是我们使用普通模式或者中断模式的Toldle函数也是可以的,唯独DMA模式有个恼人之处,DMA的传输过半中断,也会触发这个回调函数,就是说如果接收的数据量达到我们设置的最大值的一半的时候,也会触发一次回调函数,使用一个很大的接收数组虽然能解决问题,但也是治标不治本,此方案的正确解决办法则是关闭DMA传输过半中断

__HAL_DMA_DISABLE_IT(&hdma_usart2_rx , DMA_IT_HT)

第一个参数我们填写DMA通道的指针地址,第二个参数我们填写需要关闭的中断

注意别忘记在初始化中加上这一句,否则第一次接收依旧会出现问题

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于高德地图JS API实现Vue地图选点组件

基于高德地图JS API2.0实现一个搜索选择地点后返回给父组件位置信息的功能,同时可以进行回显 目录 1 创建key和秘钥1.1 登录高德地图开放平台1.2 创建应用1.3 绑定服务创建秘钥 2 使用组件前准备2.1 导入loader2.2 在对应的组件设置秘钥2.3 引入css样式 3 功能实现…

记录dockers中Ubuntu安装python3.11

参考: docker-ubuntu 安装python3.8,pip3_dockerfile ubuntu22 python3.8-CSDN博客

揭示数据在内存中存储的秘密!

** ** 悟已往之不谏,知来者犹可追 ** ** 创作不易,宝子们!如果这篇文章对你们有帮助的话,别忘了给个免费的赞哟~ 整数在内存中的存储 整数的表达方式有三种:原码、反码、补码。 三种表示方法均有符号位和数值位两部分…

Oracle数据库:使用 bash脚本 + 定时任务 自动备份数据

Oracle数据库:使用 bash脚本 定时任务 自动备份数据 1、前言2、为什么需要自动化备份?3、编写备份脚本4、备份脚本授权5、添加定时任务6、重启 crond / 检查 crond 服务状态7、备份文件检查 💖The Begin💖点点关注,收…

matlab 平面桁架有限元受力分析计算

1、内容简介 略 62-可以交流、咨询、答疑 平面桁架有限元受力分析计算 2、内容说明 略 3、仿真分析 略 4、参考论文 略

宿主环境

1. 什么是宿主环境 宿主环境( host environment )指的是程序运行所必须的依赖环境。例如: Android 系统和 iOS 系统是两个不同的宿主环境。安卓版的微信 App 是不能在 iOS 环境下运行的,所以, Android 是安卓软件的宿…

“import ... =“ 只能在 TypeScript 文件中使用

当你遇到这个问题很苦恼。 可以按照以下解决办法 使用ctrlshiftP 修改"javascript.validate.enable": false

php中 0 == ‘’(0等于任意字符串) 判断是否成立 返回true

php中不同类型变量之间比较大小 一、背景二、探究0是为什么?三、探究 0all是为什么?四、程序中如何判断0是否等于指定字符串 一、背景 最近在项目实际开发中,我需要判断前端传来的参数值是否等于一个字符串;然后发现当参数值是0时…

MATLAB学习笔记(二)PDE求解偏微分方程组

一、利用PDE求解偏微分方程组 初值为: 针对上述方程组,利用matlab自带工具箱和函数PDE进行求解。 以下是matlab中对工具箱中pdepe函数的解释。 二、matlab编程 在matlab中编程,利用PDE函数求解以上偏微分方程。 clc; clear; syms y global …

【蓝桥杯单片机】十三届省赛“重难点”解析(附源码)

【蓝桥杯单片机】十三届省赛“重难点”解析 一、题目难点解析二、易出错点提示三、完整代码链接 笔记包括:①题目难点解析、②易出错点提示、③完整代码链接 注:本文提供的所有代码都是使用第十四届竞赛包完成 ⭐----------系列文章链接----------⭐ 【蓝…

Java初阶数据结构二叉树实现+练习完整(工程文件后序会进行上传)

i1.二叉树的概念 1.二叉树的定义 (1)二叉树可以是一个节点的有限集合 (2)可以为空 (3)或者是由一个根节点加上两棵分别称为左子树和右子树的二叉树组成的 (4)二叉树的每一个节点…

力扣细节题:字符串中的最大奇数

奇数只要找到第一位是奇数的即可,不是找单个数字 //即从最低位开始,找到第一位为奇数的位 //然后之前的就是需要的数字char * largestOddNumber(char * num){int i strlen(num) - 1;while(i > 0){if((num[i] - 0) % 2 1)break;i--;}//先找到低位开…

电子科技大学链时代工作室招新题C语言部分---题号E

1. 题目 这道题大概的意思是说,一座城市中被埋了许多雷(用一个只含0和1的字符串表示城市,1代表有雷,0代表无雷)。 你作为一个排雷兵,需要花最少的钱引爆所有的雷来使城市中不再有雷(太逆天了&a…

Java宝典-异常

目录 1. 异常的分类1.1 运行时异常1.2 编译时异常 2. 异常的抛出2.1 throw2.2 throws 3. 异常的捕获3.1 try-catch3.2 finally 4. 异常执行的过程5. 自定义异常 在Java中,异常(Exception)是指程序发生不正常的行为,异常其实就是一个一个的类。 1. 异常的…

c++刷题(自用)(问题合集)---(一直会更)

初始化列表易错点 #include <iostream> using namespace std;struct A {A() { std::cout << "A"; } }; struct B {B() { std::cout << "B"; } };class C { public:C() : a(), b(){std::cout << "C";}private:B b;A a; …

【AcWing】蓝桥杯集训每日一题Day5|归并排序|离散化|二分|逆序数对|505.火柴排队(C++)

火柴排队 505. 火柴排队 - AcWing题库难度&#xff1a;中等时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;2058总尝试数&#xff1a;4484来源&#xff1a;NOIP2013提高组算法标签贪心离散化树状数组归并排序 题目内容 涵涵有两盒火柴&#xff0c;每盒装有 n 根火柴…

基于Pnpm + Turborepo + QianKun的微前端+Monorepo实践

基于Pnpm Turborepo QianKun的微前端Monorepo实践 背景 微前端一般都会涉及多个代码库&#xff0c;很多时候要一个一个代码库地去开发维护和运行&#xff0c;很不方便&#xff0c;这种时候引入Monorepo搭配微前端就能很好地解决这种问题&#xff0c;一个代码库就可以完成整…

AI赋能写作:AI大模型高效写作一本通

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

文件包含例子

一、常见的文件包含函数 php中常见的文件包含函数有以下四种&#xff1a; include() require() include_once() require()_once() include与require基本是相同的&#xff0c;除了错误处理方面: include()&#xff0c;只生成警告&#xff08;E_WARNING&#xff09;&#x…

linux之source.list解析

众所周知&#xff0c;linux可以通过apt命令安装软件&#xff0c;那么apt又是从哪里获取软件包呢并安装呢&#xff1f;这里就绕不开一个文件source.list&#xff0c;该文件定义了软件源相关的信息。下面以实际例子&#xff0c;详细的介绍下这个文件。 文件作用 定义软件源的信…