0:事由

最近开发,上位机Qt与下位机通讯的时候发现通讯规则有些不一样,这里简单记录一下 。所有代码基于元宝生成,属于伪代码不保证真实可用,啊但是逻辑是这么个逻辑。


1:底层通讯规则

        以STM32向上位机通讯通讯基础为例:

#include "main.h"
#include "usart.h"
#include "gpio.h"void SystemClock_Config(void);int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; // 8个字节数据uint16_t data_length = sizeof(data_to_send); // 数据长度=8while (1){// 发送多个字节HAL_UART_Transmit(&huart1, data_to_send, data_length, HAL_MAX_DELAY);// 延时1秒(可选)HAL_Delay(1000);}
}

还有一种发送的案例如下:

#include "main.h"
#include "usart.h"
#include "gpio.h"void SystemClock_Config(void);int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();uint16_t sensor_data_u16 = 0x1234; // 16位数据uint32_t sensor_data_u32 = 0x12345678; // 32位数据uint8_t u16_data_bytes[2];uint8_t u32_data_bytes[4];// 拆分u16数据(大端模式)u16_data_bytes[0] = (sensor_data_u16 >> 8) & 0xFF; // 高字节u16_data_bytes[1] = sensor_data_u16 & 0xFF;        // 低字节// 拆分u32数据(大端模式)u32_data_bytes[0] = (sensor_data_u32 >> 24) & 0xFF; // 最高字节u32_data_bytes[1] = (sensor_data_u32 >> 16) & 0xFF;u32_data_bytes[2] = (sensor_data_u32 >> 8) & 0xFF;u32_data_bytes[3] = sensor_data_u32 & 0xFF;         // 最低字节while (1){// 发送u16数据(2字节)HAL_UART_Transmit(&huart1, u16_data_bytes, 2, HAL_MAX_DELAY);// 发送u32数据(4字节)HAL_UART_Transmit(&huart1, u32_data_bytes, 4, HAL_MAX_DELAY);// 延时1秒HAL_Delay(1000);}
}

这里简单说一下他们通讯的原理:底层向上位机通讯的是以字节为单位发送的一个字节(8位)可以用两个十六进制(Hex)字符表示。你想发送多少个字节就多少个字节乘以二发送多少个16进制的数。在底层有三个特别经典的数据类型:

uint8_t flag = 0x01;   //两个16进制  8位数据 一个字节
uint16_t sensor_data_u16 = 0x1234; //四个16进制 16位数据  两个字节
uint32_t sensor_data_u32 = 0x12345678; //八个16进制 32位数据    四个字节

然后如果想发送多个字节的话,以下面的代码为例

#include "usart.h"void send_sensor_data() {uint8_t flag = 0x01;               // 1字节uint16_t sensor_data_u16 = 0x1234; // 2字节uint32_t sensor_data_u32 = 0x12345678; // 4字节uint8_t tx_buffer[7]; // 总字节数: 1 + 2 + 4 = 7uint8_t index = 0;// 拼接数据(注意字节序!)tx_buffer[index++] = flag;                          // 直接拷贝1字节// 拆分uint16_t为2字节(假设使用大端序)tx_buffer[index++] = (sensor_data_u16 >> 8) & 0xFF; // 高字节tx_buffer[index++] = sensor_data_u16 & 0xFF;        // 低字节// 拆分uint32_t为4字节(大端序)tx_buffer[index++] = (sensor_data_u32 >> 24) & 0xFF; // 最高字节tx_buffer[index++] = (sensor_data_u32 >> 16) & 0xFF;tx_buffer[index++] = (sensor_data_u32 >> 8) & 0xFF;tx_buffer[index++] = sensor_data_u32 & 0xFF;         // 最低字节// 通过UART发送HAL_UART_Transmit(&huart1, tx_buffer, sizeof(tx_buffer), HAL_MAX_DELAY);
}

2:上位机处理数据

上位机通过串口接收数据的时候可能会有各方面的问题,比如说需要设置串口的波特率、串口、比校验码、当然最主要还是需要借助串口调试助手工具来进行调试。

但无论如何只要你能接收到的数据你就会发现你的数据其实是很乱的因为你不知道从哪个地方开始然后就需要下沉向上层传输数据时在头部引入两个没有任何意义的字节,即引入一个头部。根据这个头部来确定发送数据的长度。一般接受的逻辑是这样一个结构

[Header1][Header2][Length][Flag][u16_Data][u32_Data][CRC][Footer]

  • ​Header1/Header2​​: 0xAA 0x55(起始标志)
  • ​Length​​: 数据部分长度(固定为 7 字节,1+2+4
  • ​Flag​​: 用户标志(uint8_t
  • ​u16_Data/u32_Data​​: 传感器数据
  • ​CRC​​: 校验码(简单求和校验)
  • ​Footer​​: 0xBB(结束标志)

当然这个接收也是以字节为单位进行接收的(伪代码)。

    while (1) {// 1. 从串口读取数据到缓存(伪代码,需替换为实际串口读取函数)uint8_t byte;if (serial_read_byte(&byte)) {  // 假设serial_read_byte()从串口读取1字节serial_buf.buffer[serial_buf.index++] = byte;// 2. 检查缓存中是否有完整帧(从头部开始)while (serial_buf.index >= 2) {  // 至少需要Header1/Header2// 查找头部起始位置(简化版:假设头部在缓存开头)if (serial_buf.buffer[0] == HEADER1 && serial_buf.buffer[1] == HEADER2) {// 3. 检查缓存是否足够容纳一帧uint8_t frame_length = 2 + 1 + EXPECTED_LEN + 1 + 1;  // 头+长度+CRC+尾if (serial_buf.index >= frame_length) {// 4. 提取完整帧uint8_t frame[256] = {0};memcpy(frame, serial_buf.buffer, frame_length);// 5. 解析帧if (parse_frame(frame, &sensor_data) == 0) {// 6. 处理有效数据(示例:打印)printf("解析成功: flag=0x%02X, u16=0x%04X, u32=0x%08X\n",sensor_data.flag, sensor_data.u16_data, sensor_data.u32_data);}// 7. 移除已处理的数据(滑动窗口)serial_buf.index -= frame_length;memmove(serial_buf.buffer, &serial_buf.buffer[frame_length], serial_buf.index);} else {break;  // 数据不足,等待更多字节}} else {// 头部不匹配,丢弃第一个字节(避免死循环)memmove(serial_buf.buffer, &serial_buf.buffer[1], --serial_buf.index);}}}

上面的代码其实还是比较难懂的主要就是说干了以下几个事情:

  1. 首先是根据下位机传送出来的数据,并放到一个数组中。这这主要是根据头部码以及长度来确定的
  2. 其次是取出其中的数据具体就是提取从哪一字节到哪一个字节再取出来,然后进行分析解码。
QByteArray pressureData = frame.mid(4, 4);
如果你有一个 frame 的16进制表示(比如 "01 02 03 04 05 06 07 08 09 0a 0b 0c"),那么:
第4个字节(索引4)是 05,
接下来的4个字节是 05 06 07 08。
所以 pressureData 就是 05 06 07 08(16进制)。

        存在一个问题就是这个数据传输到底是第一个字节是高位还是后一个字节是高位的问题,也就是所谓的小端字节跟大端字节的一个问题

QByteArray frame = QByteArray::fromHex("0102030405060708090a0b0c"); // 示例数据
QByteArray pressureData = frame.mid(4, 4); // 提取第4~7字节(索引4开始)​​小端解析​​:0x08 0x07 0x06 0x05 → 0x08070605 → 84281096
​​大端解析​​:0x05 0x06 0x07 0x08 → 0x05060708 → 134810149// 打印提取的16进制数据
qDebug() << "Extracted data (hex):" << pressureData.toHex(); // 输出 "05060708"// 转换为十进制(假设是小端字节序)
quint32 pressureValue = static_cast<quint32>(pressureData[0]) |(static_cast<quint32>(pressureData[1]) << 8) |(static_cast<quint32>(pressureData[2]) << 16) |(static_cast<quint32>(pressureData[3]) << 24);
qDebug() << "Pressure value (decimal, little endian):" << pressureValue; // 输出 84281096// 如果是大端字节序
quint32 pressureValueBigEndian = static_cast<quint32>(pressureData[3]) |(static_cast<quint32>(pressureData[2]) << 8) |(static_cast<quint32>(pressureData[1]) << 16) |(static_cast<quint32>(pressureData[0]) << 24);
qDebug() << "Pressure value (decimal, big endian):" << pressureValueBigEndian; // 输出 134810149

当然一般来说他就是按照大端解析进行的,就是按照正常人的思维从左至右从大到小(当然了是按照正常人的思维进行的考虑到数据加密的话可能会有一点不一样)。 

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

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

相关文章

创建平衡二叉树C++

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 平衡二叉树&#xff1a;每个节点的左右子树高度差不超过1 class Solution { public:TreeNode* dfs(vector<int>& nums, int left, int right){if(l…

海光の初体验

背景 八张K100的风扇已经将近一年没转过了…早在今年4月29日&#xff0c;Qwen3正式发布并全部开源8款「混合推理模型」。作为Qwen系列中的最新一代大型语言模型&#xff0c;Qwen3在推理、指令遵循、工具调用、多语言能力等方面进行了全面增强。海光DCU&#xff08;Deep Comput…

场外交易(OTC)财富管理系统开发及解决方案报告

——跨境金融科技赋能机构客户新增长 一、OTC市场现状与机构业务痛点 1. 政策机遇与市场扩容 “北向互换通”期限延长&#xff1a;2025年7月1日&#xff0c;中国外汇交易中心联合香港交易所将利率互换合约期限延长至30年&#xff0c;首日交易规模达15.3亿元&#xff0c;填补超…

pytorch底层原理学习--JIT与torchscript

文章目录 0 目的1 TorchScript1.1 语言特性的限定性1.2 设计目的&#xff1a;模型表达的专注性 2pytorch JIT&#xff08;Just-in-time compilation)2.1pytorch JIT定义2.1pytorch JIT整个过程&#xff1a;1. 前端转换层&#xff1a;生成静态计算图2. 中间表示层&#xff08;IR…

Ubuntu+Nginx+php+SQLite3+typecho手动搭建个人博客

零.Ubuntu环境 一.安装nginx 使用以下指令进行nginx web服务器安装&#xff1a; apt-get install nginx 如果提示找不到安装包&#xff0c;也可以更新一下系统的apt环境包&#xff1a; sudo apt update 安装完成后&#xff0c;可以使用以下指令查看nginx是否处于激活状态&#…

网络协议概念与应用层

1.概念 1.1 例子 点外卖 上述这个过程,就是自定义协议 自定义协议,具体的方式也是非常灵活的 2.几种开发中更常见的格式 2.1xml 上古时期的组织数据的格式 通过标签来组织数据 xml的优势:让数据的可读性变得更好了 劣势:标签写起来繁琐,传输的时候也占用更多网络带宽 2.2…

pytorch学习—7.处理多维特征的输入

2. 线性模型 3.梯度下降算法 4.反向传播(用pytorch算梯度) 5.用pytorch实现线性回归 6.logistic回归 7.处理多维特征的输入_哔哩哔哩_bilibili 7.1代码复现: import numpy as np import torch import matplotlib.pyplot as plt# 1. 获取数据集 xy_data = np.lo

AI助手“智普清言”《三元》(Python)诗解

文本逻辑解清晰&#xff0c;诗意对应技法轻。 笔记模板由python脚本于2025-07-01 06:54:55创建&#xff0c;本篇笔记适合喜欢python三元语句的coder翻阅。 学习的细节是欢悦的历程 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pytho…

本地RAG实战:用Spring AI+Ollama+DeepSeek+ChromaDB增强文档问答

本文手把手教你在本地部署RAG系统&#xff1a; 用 Spring AI 整合 Ollama&#xff08;运行DeepSeek中文模型&#xff09;ChromaDB 存储本地文档&#xff08;PDF/TXT&#xff09;向量Java程序实现&#xff1a;文档解析 → 语义检索 → 增强生成 最终效果&#xff1a;模型回答更准…

Python 数据分析:DataFrame,生成,用字典创建 DataFrame ,键值对数量不一样怎么办?

目录 1 示例代码2 欢迎纠错3 论文写作/Python 学习智能体------以下关于 Markdown 编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右S…

Java 并发编程的 CAS(Compare and Swap)是什么?

CAS&#xff08;Compare and Swap&#xff0c;比较并交换&#xff09; 并非 Java 语言特有的概念&#xff0c;而是现代计算机硬件提供的一条核心原子指令。在 Java 并发编程中&#xff0c;它扮演着“幕后英雄”的角色&#xff0c;是构建高性能、无锁并发工具&#xff08;如原子…

【UnityAssetBundle】AssetBundle打包

AssetBundle生成AB包资源文件方式&#xff1a; Unity编辑器开发&#xff0c;自定义打包工具&#xff1b;官方提供好的打包工具&#xff0c;Asset Bundle Browser 打包 选择一个资源&#xff0c;new一个压缩包名称或选择一个压缩包名称 点击Window->AssetBundle Browser&…

Hush Puppies大中华区鞋类业务移交品牌方继续经营

据悉&#xff0c;随着百丽集团运营的暇步士&#xff08;Hush Puppies&#xff09;大中华区鞋类授权的到期&#xff0c;暇步士&#xff08;Hush Puppies&#xff09;鞋类业务已开始运营权移交。其中线上渠道授权于2025年6月30日正式到期&#xff0c;线下渠道将于2025年12月31日前…

解释LLM怎么预测下一个词语的

解释LLM怎么预测下一个词语的 通过上文词的向量进行映射 在Transformer架构的大语言模型(如GPT系列、BERT等)中,词语会先被转化为词向量。在预测下一个词时,模型会基于之前所有词的向量表示(并非仅仅上一个词,但上一个词的向量是重要信息来源之一)进行计算。 以GPT-2…

DAY 49 CBAM注意力

目录 DAY 49 CBAM注意力1.通道注意力模块复习2.空间注意力模块3.CBAM的定义作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 DAY 49 CBAM注意力 1.通道注意力模块复习 2.空间注意力模块 3.CBAM的定义 import torch import torch.nn …

【网络】Linux 内核优化实战 - net.ipv4.conf.all.rp_filter

目录 net.ipv4.conf.all.rp_filter 参数详解一、参数基本概念二、参数取值及含义三、反向路径过滤的工作原理四、配置示例与注意事项五、与其他参数的关联六、总结 net.ipv4.conf.all.rp_filter 参数详解 一、参数基本概念 net.ipv4.conf.all.rp_filter 是 Linux 内核中用于控…

ElementUI el-select多选下拉框,回显数据后无法重新选择和修改

问题 ElementUI el-select多选下拉框&#xff0c;回显数据后无法重新选择和修改&#xff0c;点击选择和删除都没有反应&#xff0c;页面也没有报错 方案一 网上搜出来的基本上都是这个解决办法&#xff0c;但是我设置后没有生效&#xff0c;还是无法选择和修改 原因 下拉框数…

计算机视觉的新浪潮:扩散模型(Diffusion Models)技术剖析与应用前景

近年来&#xff0c;扩散模型&#xff08;Diffusion Models, DMs&#xff09;迅速崛起&#xff0c;成为计算机视觉领域最令人瞩目的生成模型之一。从生成高质量图像到风格迁移、图像修复&#xff0c;再到文本驱动图像生成&#xff08;如 DALLE 2、Stable Diffusion、Midjourney&…

「Java流程控制」跳转语句

今天来聊聊Java里的两个重要跳转语句——break和continue。它们就像马路上的交通信号灯,能够控制程序执行的流向。 break和continue break和continue在循环中的作用,特别像快递分拣中心的工作场景: break:就像发现一个破损包裹,直接停止当前分拣流程,把它扔进异常品处理…

R1-Searcher使用强化学习增强语言模型解决问题的搜索能力

R1-Searcher&#xff1a;Incentivizing the Search Capability in LLMs via Reinforcement Learning 2025.3 https://github.com/RUCAIBox/R1-Searcher 针对的问题&#xff1a; 现有大型推理模型在时间敏感或知识密集型问题上通常仅使用模型内部知识&#xff0c;导致回答不准…