STM32收发HEX数据包

        在实际应用中,STM32的串口通信都是以数据包格式进行收发,这个数据包一般都包含包头和包尾,表示一个数据包。源代码在文末给出

数据包格式:

固定长度,含包头包尾

可变包长,含包头包尾

问题1:当数据包传输时,里面有数据与包头包尾重复怎么办?

1:设置限幅,包头包尾设置为数据包无法超过的16进制数

2:如果无法避免重复,那么就采用固定长度,含包头包尾的包格式

3:增加包头包尾的数据,增强包头包尾的唯一性,例如可以设置两位包头:0xFF与0xFD,判断包头时,需要同时判断第二位是否为0xFD,包尾同理。

问题2:包头包尾是否可以去掉一个?

        是可以的,包头包尾并不是全都需要,例如当采用固定包长进行数据包收发时候,这时就可以只有一个包头,当程序检测到包头后开始接收数据,收够4个字节后,置标志位,一个数据包接收完成。但是这种方法存在很大的弊端,例如当设置包头为0xFF的时候,这时传输4个0xFF的时,程序就可能分不清哪个是包头。

问题3:如何发送字节流:

        在STM32中,数据包都是以一个字节一个字节进行发送的,当需要发送16位,或32位的数据时,如何发送?例如float,double,甚至是结构体。STM32支持这种发送方式,这种16位或32位的数据类型内部都是由多个字节组成的,发送时只需要使用uint8_t指针指向它,把它当成一个字节数组发送就可以发送整个数据。

总结:

        若数据包载荷不会和包头包尾重复,那么就可以选择可变包长,相反就选择固定包长。可变包长的灵活性很强,可以选择任意的数据包长度,发送16位或32位的变量时候使用uint8_t指针指向它,就可以发送整个数据。

HEX数据包与文本数据包:

        HEX数据包发送的就是16进制数据,而文本数据包是将数字译码后的数据,通常以换行作为包尾,文本载荷数据是一个字符串。

        HEX数据包传输最直接,解析数据非常简单,比较适合模块发送原始数据,例如传感器等,缺点是载荷容易和包头包尾重复,文本数据包数据适合输入指令进行人机交互,例如蓝牙模块的AT指令,缺点是解析效率低,例如发送数字100,文本数据就需要占用3个字节,而HEX只需要1个字节空间。 根据实际场景选择数据。

数据包发送:

HEX数据包:

        定义字符数组进行发送

文本数据包发送:

        定义字符串进行发送

数据包接收:

        每收到一个字节,程序都会进入一次中断,因此每拿到一个字节的数据都是独立的过程,对数据包来说,主要有包头,载荷,包尾这三种数据,那么需要设计一种能记住不同状态的程序和机制,在不同状态执行不同的操作,同时还要进行状态的合理转移,这种程序设计就叫做状态机(State Machine)

HEX数据包接收:

如下图状态转移图:

        首先设置状态变量s = 0;表示等待包头,当接收到数据进入中断时,中断程序进行判断状态接收到的数据是否为包头,是的话将s置1,表示接收数据,这样在下一次中断时就会进行数据接收,并且还要在程序中设置一个数组,当接收到载荷的长度后,表示数据接收完成,这时将s置2,表示等待包尾,如果没有问题,那么下一次收到的数据就是包尾数据,这时将s置0,这样就完成了一次接收数据的循环。

        如果数据和包头重复,那么就说明数据判断错误,那么接收完成后包尾的位置就可能不是FE,这时就可以进入重复等待包尾的状态,直到接收到真正的包尾,这样能预防数据和包头重复的错误。

        真正设计串口通信时,尽量避免包头与数据重复。

文本数据包接收:

如下图状态转移图:   

        与HEX不同的是,在s = 1的时候,每次接收数据都要判断是否为\r如果是,则不接受数据并进入下一个状态等待包尾\n,这里有两个包尾,如果只有一个包尾为\r的话那么当接收到\r时可以直接回到s=0了。

 电路连接:

        连接显示屏与触摸模块,显示屏的SCL在B10,SDA在B11,触摸模块的输出引脚在A1(触摸时输出高电平)。

编写代码:

Serial.c

#include "stm32f10x.h"                  // Device header
#include "DELAY.h"
#include "OLED.h"
#include "Serial.h"
uint8_t RxData;
uint8_t KeyNum;int main() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//下拉输入IPD,上拉输入IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//这一行可以不写,在输入模式下这一行不起作用GPIO_Init(GPIOA,  &GPIO_InitStructure);OLED_Init();Serial_Init();OLED_ShowString(1, 1, "TxData:");OLED_ShowString(3, 1, "RxData:");Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;while(1){if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1){//按键按下,这一位为1while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1);//如果一直不松手那么程序就卡在这里KeyNum = 1;//返回按键的值}if(KeyNum == 1) {Serial_TxPacket[0] ++;Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;Serial_SendPacket();OLED_ShowHexNum(2,1, Serial_TxPacket[0], 2);//RxPacket数组是同时读写的数组,OLED_ShowHexNum(2,4, Serial_TxPacket[1], 2);OLED_ShowHexNum(2,7, Serial_TxPacket[2], 2);OLED_ShowHexNum(2,10, Serial_TxPacket[3], 2);KeyNum = 0;}if(Serial_GetRxFlag() == 1) {OLED_ShowHexNum(4,1, Serial_RxPacket[0], 2);//RxPacket数组是同时读写的数组,//当读取速度慢的时候,后面的数据可能会刷新为下一个数据包的数据,可能会造成数据冲突OLED_ShowHexNum(4,4, Serial_RxPacket[1], 2);//若在这里(读取的过程中)进入中断,那么数组的数据就可能被覆盖OLED_ShowHexNum(4,7, Serial_RxPacket[2], 2);OLED_ShowHexNum(4,10, Serial_RxPacket[3], 2);}}
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
extern uint8_t Serial_TxPacket[];//外部可调用,如果模块里有数组需要外部调用,一般使用Get,Set函数进行封装,使用指针传递
extern uint8_t Serial_RxPacket[];void Serial_Init();
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char* format,...);
uint8_t Serial_GetRxFlag();
void Serial_SendPacket();#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "DELAY.h"
#include "OLED.h"
#include "Serial.h"
uint8_t RxData;
uint8_t KeyNum;int main() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//下拉输入IPD,上拉输入IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//这一行可以不写,在输入模式下这一行不起作用GPIO_Init(GPIOA,  &GPIO_InitStructure);OLED_Init();Serial_Init();OLED_ShowString(1, 1, "TxData:");OLED_ShowString(3, 1, "RxData:");Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;while(1){if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1){//按键按下,这一位为1while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1);//如果一直不松手那么程序就卡在这里KeyNum = 1;//返回按键的值}if(KeyNum == 1) {Serial_TxPacket[0] ++;Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;Serial_SendPacket();OLED_ShowHexNum(2,1, Serial_TxPacket[0], 2);//RxPacket数组是同时读写的数组,OLED_ShowHexNum(2,4, Serial_TxPacket[1], 2);OLED_ShowHexNum(2,7, Serial_TxPacket[2], 2);OLED_ShowHexNum(2,10, Serial_TxPacket[3], 2);KeyNum = 0;}if(Serial_GetRxFlag() == 1) {OLED_ShowHexNum(4,1, Serial_RxPacket[0], 2);//RxPacket数组是同时读写的数组,//当读取速度慢的时候,后面的数据可能会刷新为下一个数据包的数据,可能会造成数据冲突OLED_ShowHexNum(4,4, Serial_RxPacket[1], 2);//若在这里(读取的过程中)进入中断,那么数组的数据就可能被覆盖OLED_ShowHexNum(4,7, Serial_RxPacket[2], 2);OLED_ShowHexNum(4,10, Serial_RxPacket[3], 2);}}
}

程序现象:

        触摸开关触摸一次,发送一个数据包,在串口助手中,发送区域填写FF 12 34 56 78 FE发送数据,STM32接收到数据。

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

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

相关文章

递推入门||数字三角形

题目&#xff1a; 如果只能往左下或右下走&#xff0c;找到下面三角形的最大值 代码&#xff1a; #include <bits/stdc.h> using namespace std; int a[105][105];int main() {int n,i,j;cin>>n;for(i1;i<n;i){for(j1;j<i;j){cin>>a[i][j];}}for(in-1…

服务运营 | 印第安纳大学翟成成:改变生活的水井选址

编者按&#xff1a; 作者于2023年4月在“Production and Operations Management”上发表的“Improving drinking water access and equity in rural Sub-Saharan Africa”探讨了欠发达地区水资源供应中的可达性和公平性问题。作者于2020年1月去往非洲埃塞俄比亚提格雷地区进行…

上位机图像处理和嵌入式模块部署(qmacvisual图像拼接)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 qmacvisual本身提供了图像拼接的功能。功能本身比较有意思的。大家如果拍过毕业照&#xff0c;特别是那种几百人、上千人的合照&#xff0c;应该就…

Maven的pom.xml中resources标签的用法

spring-boot-starter-parent-2.4.1.pom文件中resources标签内容如下&#xff1a; <build><resources><resource><directory>${basedir}/src/main/resources</directory><filtering>true</filtering><includes><include>…

数字化运维:引领IT运维管理的新潮流

目录 前言 数字化运维与传统运维 数字化运维是什么 数字化运维的关键技术 书本推荐 主要内容 读者对象 总结 前言 数字化转型已经成为大势所趋&#xff0c;各行各业正朝着数字化方向转型&#xff0c;利用数字化转型方法论和前沿科学技术实现降本、提质、增效&#xf…

【前端学习——js篇】6.事件模型

具体见&#xff1a;https://github.com/febobo/web-interview 6.事件模型 ①事件与事件流 事件(Events) 事件是指页面中发生的交互行为&#xff0c;比如用户点击按钮、键盘输入、鼠标移动等。在js中&#xff0c;可以通过事件来触发相应的操作&#xff0c;例如执行函数、改变…

37、Linux中Xsync数据同步备份工具

37、Linux中Xsync数据同步备份工具 一、介绍二、配置集群hostname三、修改xsync文件四、赋权五、安装Rsync六、验证一七、配置免密登录1、生成rsa密钥2、copy机器自身公钥到目标机器3、.ssh/文件目录赋权 八、验证二 ⚠️ 注&#xff1a;本文全程在普通用户下操作&#xff0c;…

Null-text Inversion for Editing Real Images using Guided Diffusion Models

Null-text Inversion for Editing Real Images using Guided Diffusion Models (Paper reading) Ron Mokady, Google Research, CVPR23, Paper, Code 1. 前言 在本文中&#xff0c;我们介绍了一种精确的反转技术&#xff0c;从而有助于直观地对图像进行基于文本的修改。我们提…

鸿蒙开发之ArkUI组件常用组件文本输入

TextInput、TextArea是输入框组件&#xff0c;通常用于响应用户的输入操作&#xff0c;比如评论区的输入、聊天框的输入、表格的输入等&#xff0c;也可以结合其它组件构建功能页面&#xff0c;例如登录注册页面。 TextInput为单行输入框、TextArea为多行输入框 TextArea 多行…

Oracle数据库管理:从基础到高级应用【文末送书-45】

文章目录 入门篇&#xff1a;初识Oracle进阶篇&#xff1a;深入学习Oracle精通篇&#xff1a;掌握Oracle高级技术Oracle从入门到精通&#xff08;第5版&#xff09;&#xff08;软件开发视频大讲堂&#xff09;【文末送书-45】 在当今数字化时代&#xff0c;数据是企业成功的关…

django orm DateTimeField 6位小数精度问题

from django.db.backends.mysql.base import DatabaseWrapperDatabaseWrapper.data_types[DateTimeField] "datetime"意思就是重写源码里面的DateTimeField字段

xxl-job 适配人大金仓数据库 V8R6

前言 由于一些众所周知的原因&#xff0c;项目需要需要进行改造使其适配人大金仓的数据库。 xxl-job适配人大金仓 特此说明&#xff1a; 当前修改的xxl-job版本 为 2.4.1-SNAPSHOT mysql上的xxl-job库 迁移到 人大金仓数据库上pom中新增依赖 kingbase8 驱动 注意版本<!-…