STM32外设学习-串口数据包笔记-(程序)

上次我们完成了数据包的学习,这次我们来进行程序编写。

一.硬件接线图

和前两次差不多,就多了一个按键触发。

收发文本的话就是加入了一个LED。长脚接正,短脚接PA1。

二.程序编写

1.发送HEX数据包

先复制我们的串口收发的代码,然后进行修改。

在上一个的.c文件钟定义两个数组,用来存放数据。这四个数据只存放用来发送和接受的载荷数据,包头,包尾就不存了。

之后的RxFlag留着,收到一个数据包,我们就置RxFlag。

删除这个函数。

然后中断的删掉。

然后写一个SendPacket函数,我们想要的效果是调用一次这个函数TXPacket数组的四个数据就会自动加上包头包尾发送出去。

调用发送字节函数,把字节发送走

调用发送数组函数,把数组发送走。

把包尾发送走

完整的函数。

声明函数。

声明数组为外部可调用。

来到主函数,先进行发射缓冲区数组填充。

调用这个函数。

这样这个函数就可以把数组读取出数组的内容,加上包头包尾,一起发送出去了。

然后编译下载,这样子就可以看到串口的数据了。

2.接受HEX数据包

上面接受缓冲区的数据包和标志位已经定义好了。

然后再接受函数里,我们就需要用状态机来执行和接受逻辑了。接受数据包,把数据存在RxPacket数组里面,这是我们的任务。

根据这里的状态转移图,首先要定一个标志当前状态的变量S,我们可以在中断里面定义一个静态变量。

这个静态变量类似于全局变量,函数进入只会初始话一次0,在函数退出后数据仍然有效,改变了这个变量的生命周期。与全局变量不同的是,静态变量只能在本函数内使用,那么我就用这个RxState当作状态变量S。根据图片,三个S状态分别为:0,1,2。

这样子我们需要根据RxState不同我们要进入不同的函数。这里不要去掉else为了避免我们的两个if同时起到作用。

获取一下RXData,读取寄存器里面的值

然后先写我们读取包头的函数。如果收到了包头,那么让状态变为1,可以进行下一步了。如果不等于,就不转运状态了。为什么不读寄存器第一个数值?因为寄存器一次只能存储一个字节数据。

在定义一个静态变量,指示接收到哪一个数据了

这里需要依次接受四个数组,存在RxPack数组里,所以需要一个变量用来记录接受几个数据,这里数组的第pRxPacket数是RxData,就是我们寄存器的值。这就是每进入一次接收状态,数据就转运一次缓存数组,同时存位置++。

如果这个数据大于四个,就代表我们接受完了这四个数据,我们就进行到下一个状态。

同时在状态1里面进行清除一下接收计数,为下次接收做准备。

然后是等待包尾。是包尾的话就回到第一个状态。然后标志位代表接收到一组完整的数据了,置一个标志位。

接收缓存数组,也放在这里声明一下。

判断标志位是否等于1 收到了数据包就在屏幕上显示一下。,然后显示一下,各个接收缓存数组的值。编译下载。

然后我们在发送区进行数据的发送。

然后就可以看到我们发送成功了。

如果我们发送的是和包头包尾重复的数据。

也干扰不到我们,因为我们程序在接收数据的时候,并不会判断包头包尾,这就是我们发送和接收数据的程序。

但是这个程序有一个隐藏的小问题就是这个数组,是一个同时被写入和同时读取的数组,在中断函数里我们会依次写入他,在主函数里我们又会依次读取他,这回造成数据包之间可能会混合在一起,比如读出的过程太慢了,前面两个数据刚读出来,等了一会儿才继续往后读取,那么这时后i俺的数据就有可能刷新为,下一个数据包数据,也就是你读出的数据可能属于上一个数据包,另一部分属于下一个数据包。

解决方法就是在接收部分加入判断,让每个数据包读取完成后,在进行下一个数据包,像HEX数据包多数是传输传感器的每个独立数据,他们相邻数据包的数据具有连续性,混合的话也没关系。

可以把这个缓冲区数组在接受完成之后赋给另一个数组,然后显示新的数组数据。

3.完善程序现象

引用按键的头文件。

接收返回的值。

初始化按键。

显示字符串。

复制一个用于显示接收。

先写按一下按键发送一个初始值到串口上。

加入按键判断,按键按下,就执行发送,并且刷新一下值方便我们观察。

然后我们调用OLED显示一下。发送的数据在第二行显示

接受的数据放在第四行显示。

编译下载。

按一下按键,发送一次,数据变化一次。

然后我们发送指定模式数据包,OLED接收数据包

三.串口收发文本数据包

1.程序测试

复制一下上一个工程。

删除多余程序。

我们数据包的定义是可变包长韩包头包尾的。以@符号为包头,\r\n为包尾。

来到.c文件把发送的删掉,并且更改为char型,方便我们接收,同时数量给到100,防止溢出。

状态机部分参考一下。

因为我们用@当包头,所以我们需要修改一下。

因为我们是不固定的长度,所以每次都要判断是不是为包尾

如果不是包尾才需要接收数据

之后再状态2,我们继续判断第二个包尾,如果不等于\n,我们就一直等待包尾。同时再接收到之后,还需要对字符数据加一个字符串结束标志位\0方便我们后续对字符串进行处理。

修改一下头文件,删除发送的缓存数组,然后将接受的数组改为char。

标志位为1代表接收到了数据

有一个错误

把这个函数删除。

编译下载。

串口助手这里,发送和接收都选择文本模式。

按照规定格式发送数据包,换行一定要打出来。

2.完善最终的程序

加入led文件。

初始化led。

调用C预言的官方字符库。

判断字符串是否相等,第一个是要判断的字符串,第二个是需要和谁判断,如果相等的话函数返回0。

所以我们加入if进行判断。同时向串口助手回传一个字符串表示led打开

关闭LED的指令我们也完成。

如果上面指令都不匹配就不执行操作

最终的程序。编译下载

先发送一个led_on。

LED亮起来,并且屏幕显示

然后传回LED点亮。

关闭led

led熄灭

回传的是LED关闭。

其他的指令

LED无操作

传回错误的指令

为了避免我们的数据包被覆盖,我们可以等待上一个程序处理完成之后在进行我们的下一组数据接收,因为我们是不固定的文本,错误的话问题比较大。

所以再判断包头这里,如果发送完了标志位为0才执行接收,否则就是发送的太快我还没处理完上一个数据。

读取标志位立刻清零的我们就删掉。

然后获取标志位的函数,我们也外部声明一下。

然后代表我们接收到了数据

标志位清0再程序结束之后。再中断哪里

只有满足同时的两个条件,才能进行下一步。还可以再定义一个指令缓冲区,然后就可以提高发送效率。

编译下载

发现可以实行。

发送关闭关灯。数据包发送慢的时候没有问题,如果太快就需要定义一个指令缓冲区,然后就可以提高发送效率,再缓冲区进行排队处理。

相关推荐
----云烟----2 小时前
MCU单片机驱动WS2812
单片机·嵌入式硬件
ACP广源盛139246256733 小时前
GSV6127E#ACP#Type-C/DisplayPort 1.4/HDMI 2.0 到 MIPI CSI-2 混合转换器(带嵌入式 MCU)
单片机·嵌入式硬件·音视频
弘毅 失败的 mian3 小时前
编译和链接
c语言·经验分享·笔记·编程入门
zhoutanooi3 小时前
安卓bp文件编译学习
android·学习
百***46454 小时前
SocketTool、串口调试助手、MQTT中间件基础
单片机·嵌入式硬件·中间件
蓝桉~MLGT4 小时前
Python学习历程——Python面向对象编程详解
开发语言·python·学习
aramae4 小时前
MySQL数据库入门指南
android·数据库·经验分享·笔记·mysql
chenzhou__4 小时前
LinuxC语言文件i/o笔记(第十七天)
linux·c语言·笔记·学习
chenzhou__4 小时前
LinuxC语言文件i/o笔记(第十八天)
linux·c语言·笔记·学习