江协科技STM32学习- P28 USART串口数据包

       🚀write in front🚀  
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​ 

💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚 

🚀Projeet source code🚀   

💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan) - Gitee.com

引用:

STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili

Keil5 MDK版 下载与安装教程(STM32单片机编程软件)_mdk528-CSDN博客

STM32之Keil5 MDK的安装与下载_keil5下载程序到单片机stm32-CSDN博客

0. 江协科技/江科大-STM32入门教程-各章节详细笔记-查阅传送门-STM32标准库开发_江协科技stm32笔记-CSDN博客

【STM32】江科大STM32学习笔记汇总(已完结)_stm32江科大笔记-CSDN博客

江科大STM32学习笔记(上)_stm32博客-CSDN博客

STM32学习笔记一(基于标准库学习)_电平输出推免-CSDN博客

STM32 MCU学习资源-CSDN博客

stm32学习笔记-作者: Vera工程师养成记

stem32江科大自学笔记-CSDN博客

术语:

英文缩写描述
GPIO:General Purpose Input Onuput通用输入输出
AFIO:Alternate Function Input Output复用输入输出
AO:Analog Output模拟输出
DO:Digital Output数字输出
内部时钟源 CK_INT:Clock Internal内部时钟源
外部时钟源 ETR:External Trigger 时钟源 External 触发
外部时钟源 ETR:External Trigger mode 1外部时钟源 External 触发 时钟模式1
外部时钟源 ETR:External Trigger mode 2外部时钟源 External 触发 时钟模式2
外部时钟源 ITRx:Internal Trigger inputs外部时钟源,ITRx (Internal trigger inputs)内部触发输入
外部时钟源 TIx:exTernal Input pin 外部时钟源 TIx (external input pin)外部输入引脚
CCR:Capture/Comapre Register捕获/比较寄存器
OC:Output Compare输出比较
IC:Input Capture输入捕获
TI1FP1:TI1 Filter Polarity 1Extern Input 1 Filter Polarity 1,外部输入1滤波极性1
TI1FP2:TI1 Filter Polarity 2Extern Input 1 Filter Polarity 2,外部输入1滤波极性2
DMA:Direct Memory Access直接存储器存取

正文:

0. 概述

从 2024/06/12 定下计划开始学习下江协科技STM32课程,接下来将会按照哔站上江协科技STM32的教学视频来学习入门STM32 开发,本文是视频教程 P2 STM32简介一讲的笔记。

本节学习一下串口数据包收发的思路和流程

接下来就来学习一下如何去规定一个合理的数据包格式,以及如何收发数据包。

数据包格式一般有两种:一种是Hex数据包,一种是文本数据包

先看一下Hex数据包格式。

1.🚢Hex数据包

首先数据包的作用是把一个个单独的数据给打包起来,方便我们进行多字节的数据通信。

我们之前学习了串口的代码,发送一个字节,接收一个字节都没问题。但在实际应用中,我们可能需要把多个字节打包为一个整体进行发送。比如说我们有个陀螺仪传感器,需要用串口发送数据STM32。陀螺仪的数据,比如x轴一个字节,y轴一个字节,z轴一个字节,总共三个数据需要连续不断的发送,当你像这样xyzxyzxyz连续发送的时候,就会出现一个问题,就是接收方不知道这数据哪个对应x,哪个对应y,哪个对应z,因为接收方可能会从任意位置开始接收,所以会出现数据错位的现象。

这时候我们就需要研究一种方式,把这个数据进行分割,把xyzxyzxyzxyz这一批数据分割开,分成xyz,分成一个个数据包。这样再接收的时候就知道了数据包的第一个数据就是x,第二个是y,第三个是z。这就是数据包的任务,就是把属于同一批的数据进行打包和分割,方便接收方进行识别。

有关分割打包的方法可以是自己发挥想象力来设计,只要逻辑行得通就行。比如可以设计在这个xyzxyzxyzxyz数据流中,数据包的第一个数据,也就是x的数据包,它的最高位置1,其余数据包最高位都置0。这样当接收到数据之后,判断一下最高位,如果是1,就是x数据,然后紧跟着的两个数据就分别是y和z,这就是一种可行的分割方法。

这种方法就是把每个数据的最高位当做标志位来进行分割的。实际也有应用的例子,比如UTF8的编码方法和这就是类似的。

但是本节我们主要讲的数据包分割方法并不是在数据的高位添加标志位这种方式。因为这种方式破坏了原有数据使用起来比较复杂。

我们串口数据包通常使用的是额外添加包头包尾这种方式。比如这里就列举了两种数据包格式

第一种是固定包长,含包头包尾

第二种是可变包长,含包头包尾

也就是每个数据包的长度可以是不一样的。前面是包头,后面是包尾。

数据包格式可以是用户根据需求自己规定的,也可以是你买个模块别的开发者规定的。

我们这里规定是,比如固定包长,一批数据规定有四个字节,在这四个字节之前加个包头,比如定义0xFF为包头。在四个字节之后加一个包尾,比如定义0xFE为包尾。当接收到0xFF之后就知道一个数据包来了,接着再接收到的四个字节就当做数据包的第一,二、三、四个数据存在一个数组里,最后跟一个包尾。当收到0xFE之后就可以置一个标志位,告诉程序收到了一个数据包。然后新的数据包过来再重复之前的过程。

这样就可以在一个连续不断的数据流中分割出我们想要的数据包来。这就是通过添加包头包尾,实现数据分割打包的思路。

接着我们来研究几个问题。

收发过程中的问题

🐟️🐟️第一个问题就是包头包

这里定义FF为包头FE为包尾,那如果传输的数据本身就是FF和FE怎么办?

这个问题确实存在,如果数据和包头包尾重复可能会引起误判。对应这个问题,我们有如下几种解决方法:

尾和数据载荷重复的问题

第一种方法限制载荷数据的范围

如果可以的话,我们可以在发送的时候,对数据进行限幅,比如xyz三个数据变化范围都可以是0~100,我们可以在载荷中只发送0~100的数据,这样就不会和包头报尾重复了。

第二种方法如果无法避免载荷数据和包头包尾重复就尽量使用固定长度的数据包

这样由于载荷数据是固定的,只要我们通过包头包尾对齐了数据,我们就可以严格知道哪个数据应该是包头包尾,哪个数据应该是载荷数据。在接收载荷数据的时候,我们并不会判断它是否是包头包尾。而在接收包头包尾的时候,我们会判断它是不是确实是包头包尾,用于数据对齐。这样在经过几个数据包的对齐之后,剩下的数据包应该就不会出现问题了。

第三种方法就是增加包头包尾的数量并且让它尽量呈现出载荷数据出现不了的状态

比如我们使用FF、FE作为包头,FD、FC作为包尾,这样也可以避免再和数据和包头包尾重复的情况发生。

🐟️🐟️第二个问题是这个包头包尾并不是全部都需要的

比如我们可以只要一个包头,把包尾删掉。这样数据包的格式就是一个包头FF加四个数据,这样也是可以的。当检测到FF开始接收,收够四个字节后置标志位,一个数据包接收完成,这样也可以。不过这样的话,载荷和包头重复的问题会更严重一些。

比如最严重的情况下,载荷全是FF,包头也是FF,那肯定不知道哪个是包头了。而加上FE作为包尾,无论数据怎么变化,都是可以分辨出包头包尾的。

🐟️🐟️第三个问题就是固定包长和可变包长的选择问题

对应hex数据包来说,如果载荷会出现和包头包尾重复的情况,就最好选择固定包长,这样可以避免接收错误。如果你又会重复又选择可变包长,数据很容易就乱套了。

如果载荷不会和包头包尾重复,可以选择可变包长数据长度,像这样四位、三位、一位、十位、来回任一变,肯定都没问题。因为包头包尾是唯一的,只要出现包头就开始数据包,只要出现包尾就结束数据包,这样就非常灵活。

这就是固定包长和可变包长选择的问题。

🐟️🐟️第四个问题就是各种数据转换为字节流的问题

这里数据包都是一个字节一个字节组成的,如果想发送十六位的整形数据,三十二位的整形数据,float double,甚至是结构体,其实都没问题。因为它们内部其实都是由一个字节一个字节组成的,只需要用一个uint8_t的指针指向它,把它们当做一个字节数组发送就行了。

接下来看一下文本数据包。
 

2.🚢文本数据包

文本数据包和Hex数据包就分别对应了文本模式和Hex这两种模式。在Hex数据包里面,数据都是以原始的字节数据本身呈现的。而在文本数据包里面,每个字节就经过了一层编码和译码,最终表现出来的就是文本格式。但实际上每个文本字符背后,其实都还是一个字节的hex数据。

同样文本数据包也可以有两种模式:

第一种是固定包长,含包头包尾

第二种是可变包长,含包头包尾

由于数据译码成了字符形式,这就会存在大量的字符可以作为包头包尾,可以有效避免载荷和包头包尾重复的问题。比如我这里规定的就是以@这个字符作为包头,以’\r’’\n’,也就是换行这两个字符作为包尾。

在载荷数据中间,可以出现除了包头包尾的任意字符,这很容易做到。所以文本数据包基本不用担心载荷和包头包尾重复的问题使用非常灵活。可变包长、各种字母、符号、数字都可以随意使用。

当我们接收到载荷数据之后,得到的就是一个字符串,在软件中再对字符串进行操作和判断。就可以实现各种指令控制的功能了,而且字符串数据包表达的意义很明显,可以把字符串数据包直接打印到串口助手上,什么指令、什么数据一眼就能看明白。所以这个文本数据包通常会以换行作为包尾。这样在打印的时候就可以一行一行的显示了非常方便。

3.🚢Hex数据包和文本数据包的优缺点

Hex数据包和文本数据包这两种对比下来其实也各有优缺点。

hex数据包

优点是传输最直接解析数据非常简单,比较适合一些模块发送原始的数据。比如一些使用串口通信的陀螺仪,温湿度传感器,

缺点就是灵活性不足,载荷容易和包头包尾重复。

文本数据包

优点是数据直观易理解,非常灵活,比较适合一些输入指令,进行人机交互的场合。比如蓝牙模块常用的AT指令,CNC和3D打印机常用的G代码都是文本数据包的格式。

缺点就是解析效率低,比如发送一个数100,hex数据包,就是一个字节100完事儿,文本数据包就得是三个字节的字符,’1’’0’’0’,收到之后,还要把字符转换成数据才能得到一百。

所以说我们需要根据实际场景来选择和设计数据包格式

接下来我们就来学一下数据包的收发流程。

4.🚢数据包的收发流程

首先是数据包的发送,这个比较简单。

发送数据包

如果想发送一个数据包,就定义一个数组填充数据,然后用上节我们写过的SendArray函数一发就完事了,文本数据包这里也很简单,写一个字符串,然后调用SendString一发送也完事了。

所以说发送这个数据包是很简单的,因为发送过程是完全自主可控的,想发啥就发啥,我们写代码的时候也能感受到串口发送比接收简单多了。

接下来接收一个数据包就比较复杂了。

接收数据包

这里演示了固定包长hex数据包的接收方法可变包长文本数据包的接收方法,其他的数据包也都可以套用这个形式。下节写程序就会根据这里面的流程来。

HEX数据包接收

我们先看一下如何来接收这个固定包长的hex数据包。

首先根据之前的代码,我们知道每收到一个字节程序都会进一步中断。在中断函数里,我们可以拿到这一个字节,但拿到之后我们就得退出中断了。所以每拿到一个数据都是一个独立的过程。而对于数据包来说很明显,它具有前后关联性。包头之后是数据,数据之后是包尾。对于包头、数据和包尾这三种状态,我们都需要有不同的处理逻辑。所以在程序中,我们需要设计一个能记住不同状态的机制

在不同状态执行不同的操作同时还要进行状态的合理转移。这种程序设计思维就叫做状态机在这里我们就使用状态机的方法来接收一个数据包,要想设计一个好的状态机程序画一个这样的状态,转移图是必要的。

对于上面这样一个固定包长hex数据包来说,我们可以定义三个状态,第一个状态是等待包头、第二个状态是接收数据、第三个状态是等待包尾,每个状态需要用一个变量来标志一下。比如这里用变量s来标志。三个状态依次为s等于0,s等于1,s等于2。这一点类似于置标志位,只不过标志位只有零和一,而状态机是多标志位状态的一种方式。

然后执行流程是最开始s等于0收到一个数据进中断,根据s等于0进入第一个状态的程序,判断数据是不是包头FF,如果是FF则代表收到包头,之后置s等于1,退出中断结束。这样下次再进中断,根据s等于1就可以进行接收数据的程序了。

在第一个状态,如果收到的不是FF就证明数据包没有对齐,我们应该等待数据包包头的出现。这时状态就仍然是0,下次进中断,就还是判断包头的逻辑,直到出现FF才能转到下一个状态。

之后出现了FF,我们就可以转移到接收数据的状态了。这时再收到数据,我们就直接把它存在数组中。另外再用一个变量记录收纳多少个数据,如果没收够四个数据,就一直是接收状态。如果收够了,就置s等于2,下次进入中断时就可以进入下一个状态了。

最后一个状态就是等待包尾了。判断数据是不是FE,正常情况应该是FE,这样就可以置s等于0,回到最初的状态,开始下一个轮回。当然也有可能这个数据不是FE,比如数据和包头重复,导致包头位置判断错了,这个包尾位置就有可能不是FE,这时就可以进入重复等待包尾的状态,直到接收到真正的包尾。这样加入包尾的判断,更能预防因数据和包头重复造成的错误。这就是使用状态机接收数据包的思路。

这个状态机其实是一种很广泛的编程思路,在很多地方都可以用到。使用的基本步骤是先根据项目要求定义状态画几个圈,然后考虑好各个状态,在什么情况下会进行转移,如何转移,画好线和转移条件,最后根据这个图来进行编程,这样思维就会非常清晰了。比如你要做个菜单,就可以用到状态机的思维,按什么键切换,什么菜单,执行什么样的程序。还有一些芯片内部逻辑也会用到状态机,比如芯片什么情况下进入待机状态,什么情况下进入工作状态,这也是状态机的应用。希望大家可以研究一下,对你的编程肯定会有帮助。

接下来继续我们来看一下这个可变包长文本数据包的接收流程。

文本数据包接收

同样也是利用状态机定义三个状态。第一个状态,等待包头,判断收到的是不是我们规定的@符号,如果是就进入接收状态,在这个状态下依次接收数据。同时,这个状态还应该要兼具等待包尾的功能,因为这是可变包长,我们接收数据的时候,也要时刻监视,是不是收到包尾了,一旦收到包尾了就结束。这个状态的逻辑就应该是收到一个数据判断是不是’\r’,如果不是则正常接收,如果是则不接受,同时跳到下一个状态,等待包尾’\n’,因为这里数据包有两个包尾’\r’’\n’,所以需要第三个状态。如果只有一个包尾,在出现包尾之后,就可以直接回到初始状态了,只需要两个状态就行。因为接收数据和等待包尾需要在一个状态里同时进行。

由于串口的包头包尾不会出现在数据中,所以基本不会出现数据错位的现象。这就是使用状态机接收文本数据包的方法。

下节我们就写程序验证一下以上所学的内容。

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

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

相关文章

信刻国产化智能光盘柜管理系统

在数字化时代,数据的长期安全储存和管理成为各行业档案部门的重要任务,随着光盘存储技术的不断发展和应用领域的日益广泛,如何高效、安全地管理海量光盘数据成为了一个亟待解决的问题。信刻智能光盘管理柜应运而生,以其创新的设计…

uniapp position: fixed 兼容性不显示问题

position: fixed; bottom: 0;以上运行到微信小程序时正常,但是h5会出现不显示的问题。 解决方法 修改为: position: fixed; bottom: var(--window-bottom, 0);

【python】OpenCV—Tracking(10.3)—GOTURN

文章目录 1、功能描述2、模型介绍3、代码实现4、完整代码5、结果展示6、优缺点分析7、参考 1、功能描述 基于 Generic Object Tracking using Regression Networks 方法,实现单目标跟踪 2、模型介绍 (1)发表来自 Held D, Thrun S, Savarese…

如何将本地项目上传至Gitee仓库(详细教程)

前提条件 1、本地电脑安装Git客户端 2、本地已有项目 3、Gitee注册好了账户 如果没有安装Gitee 可以区菜鸟查看一下安装教程 Git教程https://www.runoob.com/git/git-tutorial.html 操作示例 前提条件已经准备好的情况下登录gitee 码云 https://gitee.com 点解右侧加号 新…

二叉树中的深搜 算法专题

二叉树中的深搜 一. 计算布尔二叉树的值 计算布尔二叉树的值 class Solution {public boolean evaluateTree(TreeNode root) {if(root.left null) return root.val 0? false: true;boolean left evaluateTree(root.left);boolean right evaluateTree(root.right);return…

静态水印+动态水印,开启超强PPT版权保护!

在保护 PPT 内容版权时,水印是一种既简单又有效的手段。无论你是为了防止内容被非法复制,还是为了在传播中标明作者身份,水印都能为你的 PPT 提供额外的安全保障。 在传统的 PPT 制作中,最常见的水印添加方法是通过「幻灯片母版」…

std.move 可以重复使用吗?普通变量不行,shared_ptr包装后可以

std.move std::move函数本身可以重复调用,但这取决于对象的状态。在C中,std::move函数用于将一个对象转换为右值引用,从而允许我们从该对象中提取所有权,而不需要创建新的对象。然而,std::move并不会改变对象的状态&am…

关于图像分解的RPCA

将矩阵分解为低秩矩阵和独立同分布的高斯矩阵是PCA 当矩阵 E0 为稀疏的噪声矩阵时,分解为一个低秩矩阵部分 A 和一个稀疏矩阵部分 E 的 矩阵的秩和 ℓ0 范数问题都可以进行凸松弛,矩阵的核范数是矩阵秩的凸包络,(1)变…

XHCI 1.2b 规范摘要(八)

系列文章目录 XHCI 1.2b 规范摘要(一) XHCI 1.2b 规范摘要(二) XHCI 1.2b 规范摘要(三) XHCI 1.2b 规范摘要(四) XHCI 1.2b 规范摘要(五) XHCI 1.2b 规范摘要…

【远程项目管理】Focalboard如何在Windows环境使用Docker快速部署

文章目录 前言1. 使用Docker本地部署Focalboard1.1 在Windows中安装 Docker1.2 使用Docker部署Focalboard 2. 安装Cpolar内网穿透工具3. 实现公网访问Focalboard4. 固定Focalboard公网地址 前言 本篇文章将介绍如何在Windows系统本地快速部署Focalboard项目管理工具&#xff0…

WebStorm EsLint报红色波浪线

如图左侧。 这个错误是由于 ESLint 和 Prettier 的配置不一致导致的。它建议你移除多余的空格。以下是一些解决方法: 安装 Prettier 插件: 确保你在 WebStorm 中安装了 Prettier 插件,并确保它配置正确。 调整 ESLint 配置: 检查…

【Flask】二、Flask 路由机制

目录 什么是路由? Flask中的路由 基本路由 动态路由 路由中的HTTP方法 路由函数返回 在Web开发中,路由是将URL映射到相应的处理函数的过程。Flask是一个轻量级的Web应用框架,提供了简单而强大的路由机制,使得开发者能够轻松…

如何用Python同时抓取多个网页:深入ThreadPoolExecutor

背景介绍 在信息化时代,数据的实时性和获取速度是其核心价值所在。对于体育赛事爱好者、数据分析师和投注行业而言,能否快速、稳定地抓取到实时比赛信息显得尤为重要。特别是在五大足球联赛中,能够在比赛进行时获得比分、控球率等实时数据&a…

深入计算机语言之C++:内存管理

🔑🔑博客主页:阿客不是客 🍓🍓系列专栏:从C语言到C语言的渐深学习 欢迎来到泊舟小课堂 😘博客制作不易欢迎各位👍点赞⭐收藏➕关注 一、 C/C的内存分布 我们先来看一段代码&#xf…

使用Vue.js和Vuex构建可维护的前端应用

使用Vue.js和Vuex构建可维护的前端应用 Vue.js简介 安装Vue.js 使用npm安装 使用CDN引入 创建Vue项目 安装Vuex 初始化Vuex Store 在Vue组件中使用Store Vuex模块化 Vuex命名空间 Vuex插件 Vuex热重载 Vuex持久化状态 Vuex调试工具 Vuex的高级用法 异步Actions 中间件 Vuex的…

云智慧完成华为原生鸿蒙系统的适配, 透视宝 APM 为用户体验保驾护航

2024 年 10 月 22 日,首个国产移动操作系统 —— 华为原生鸿蒙操作系统 HarmonyOS NEXT 正式面世,成为继 iOS 和 Android 后的全球第三大移动操作系统。HarmonyOS NEXT,从系统内核、数据库根基,到编程语言创新、AI(人工…

Metasploit(MSF)使用

目录 Metasploit简要介绍 主要功能 漏洞利用: Payload 生成: 辅助模块: 后渗透模块: 报告生成: 使用教程以及案例 基础命令使用 生成被控端 命令介绍 kali启动主控端 1.启动以及设置载荷等配置 漏洞检测…

Linux 开机自动挂载硬盘

在日常使用 Linux 系统的过程中,我们可能需要挂载一些机械硬盘或者移动硬盘来存储数据。手动挂载虽然简单,但每次重启后都需要重新操作,未免有些繁琐。那么,如何让硬盘在开机时自动挂载呢?本篇博客将详细介绍如何通过配…

GPU 学习笔记三:GPU多机多卡组网和拓扑结构分析(基于数据中心分析)

文章目录 一、概述二、数据中心(DC)2.1 数据中心简介2.2 传统数据中心的网络模型2.3 脊叶网络模型(Spine-Leaf)2.4 Facebook的Fabric网络架构 三、基于数据中心的多机多卡拓扑3.1 Spine-Leaf 架构网络规模测算方法3.2 NVIDIA多机多…

基于 GADF+Swin-CNN-GAM 的高创新扰动信号识别模型!

往期精彩内容: Python-电能质量扰动信号数据介绍与分类-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer的一…