当前位置: 首页 > news >正文

单片机(STM32)与上位机传输浮点数

目录

  • 单片机(STM32)与上位机传输数据的方法
    • 1. 传输整形数据
    • 2. 传输浮点数据
    • 3. 如何打包与解包

单片机(STM32)与上位机传输数据的方法

在进行单片机程序的开发时,常常需要与其他设备进行通信。一种情况是与其他电路板通信,比如STM32主机与STM32从机通信,STM32从机与树莓派主机通信。一种情况是与上位机通信,上位机软件进行人机交互。这个时候需要进行数据传输,传输数据有两种方式,传输整形数据与直接传输浮点数据。

1. 传输整形数据

一种方法是传输整形数据,工业中常用的Modbus就是这种方式。这里以传输16位整形数据为例,一个数据就占用两个字节,可以是正数和负数。

测试代码:

int main()
{uint16_t me, you;uint8_t data[100];me = 120;data[0] = me >> 8;data[1] = me;you = (uint16_t)data[0] << 8 | (uint16_t)data[1];printf("you = %d", you);return 0;
}

出来的结果是一样的120

那么负数怎么办呢?其实是一样的。不管是uint16_t还是int16_t,在内存中的存储都是一样的,区别不在于写,而在于怎么读

int main()
{uint16_t me, you;uint8_t data[100];int16_t para;me = -120;data[0] = me >> 8;data[1] = me;you = ((uint16_t)data[0] << 8 | (uint16_t)data[1]);para = (int16_t)((uint16_t)data[0] << 8 | (uint16_t)data[1]);printf("me = %d\n", me);printf("me = %d\n", (int16_t)me);printf("you = %d\n", (int16_t)you);printf("para = %d", para);return 0;
}

结果:
在这里插入图片描述

上面me是一个uint16_t类型,怎么能直接让它等于-120呢?当然是可以的,只不过调用me的时候,是按照uint16_t类型读取的,结果就是65416,如果按照int16_t类型读取,结果就是-120。

同理,you也是一个uint16_t类型,you = ((uint16_t)data[0] << 8 | (uint16_t)data[1])是按照移位拷贝的方式将me的值赋给了you,只要按照int16_t类型读取出来,结果就是正确的负数。

理解了这种思想,在进行单片机与其他设备通信的时候,就可以定义一个数组,uint16_t register1[1000],数组的索引就是数据地址,一个萝卜一个坑。第二个设备(其他单片机或电脑)同样定义一个数组,uint16_t registe2[1000],按照上面的方法一个数据一个数据传输就行了。

再次注意:直接定义无符号数组即可,传输负数时直接赋值,只要另一端收到数据后按照int16_t类型读取,结果就是正确的负数。

2. 传输浮点数据

传输整形方法的缺点是:(1)不能直接传输浮点数,传输浮点数时需要进行倍数处理。例如0.12,将其乘100变成整形的12,上位机收到后除100变成浮点型的0.12。这种方法较麻烦,哪些地址的数据需要进行倍数,需要下位机和上位机同时定义清楚。(2)有符号和无符号类型数据区分。uint16类型数据较简单,直接传输,直接解析,没问题。int16上位机解析时,就需要进行类型转换了。哪些地址的数据要进行(int16_t)类型转换,也要定义清楚。(3)表示的数据范围有限,16位整形无符号数只能到65535,有符号数除2减半。如果是浮点数,乘掉了倍数,表示范围直接缩水。如果是翻100倍,只能表示到655。

所以,最方便的就是直接传输浮点数,省去很多麻烦。当然浮点数的缺点就是,一个数据要占用4个字节。因此效率是传输整形数据的一半。

传输浮点数,需要定义一个联合体:

union float_data
{float f_data;uint8_t byte[4];
};

f_databyte[4]共用4个字节的内存单元,成员f_data是实际使用的数据,成员byte[4]是通信时用的数据,各司其职。

使用方法:

#include <stdio.h>
#include <stdint.h>union float_data
{float f_data;uint8_t byte[4];
};int main()
{union float_data me, you;me.f_data = 0.12;you.byte[3] = me.byte[3];you.byte[2] = me.byte[2];you.byte[1] = me.byte[1];you.byte[0] = me.byte[0];printf("you = %f", you.f_data);return 0;
}

出来的结果是一样的,0.12。聪明的读者可以发现,meyou对应两个设备。只要按照这种方式进行传输,就可以传输浮点数。传输多个浮点数,meyou就可以定义为一个数组,例如me[100], you[100]

3. 如何打包与解包

知道了数据如何传输,第二步就是思考如何进行数据打包和解包了,因为一个数据帧当然是要传输多个数据的。需要两个设备定义好通信协议,才能正确的解析数据。

数据打包也有两种方式,一种按照功能字,一种按照地址——数据对。

(1)按照功能字

这种方法用一个数据位表示功能字,对方设备收到这一帧数据,根据这个功能字就能判断你这一帧数据是什么,然后进行解析。例如一款陀螺仪的数据上传协议为:

在这里插入图片描述
在这里插入图片描述
它用第一个字节表示帧头,0x55,第二个字节表示功能字,0x52是角速度输出,0x53是角度输出,单片机读陀螺仪的数据时,按照它给定的这个协议,依次把数据读出来就可以了。

如果是自己定义通信协议,也可以模仿,这种方式每一帧数据都要进行定义,优点是物理意义明确,缺点是一旦确定了,如果想要修改,两端的设备要同时修改。

(2)按照地址数据对

这种方法模拟计算计的存储方式,为每一个数据安排一个地址,请注意这个地址并需要是真正的内存地址,它的核心是“索引”。例如一个数据就可以实现这种功能。uint16_t data[100],数组的索引就是地址。例如我用data[0]表示姓名,data[1]表示年龄。那么姓名的地址就是0,年龄的地址就是1。

这种方法的优点和缺点与第一种方法相反,物理意义不明确,但移植性强、维护性好。

下面是我自创的一种通信协议,传输浮点数。前两个字节为帧头,不同帧头分别代表从机主动上传、主机下发修改数据、主机下发查询数据。(这种通信协议为一对一,不支持总线通信)

(1)单片机主动上传数据:

在这里插入图片描述
发送N个数据(32 bits)一共4N+6个帧字节。

(2)上位机下发更改数据:

在这里插入图片描述

发送N个数据(32bits)也是一共2N+6个帧字节。

(3)上位机下发查询数据:

在这里插入图片描述

查询从起始地址开始的N个数据,查询帧是6个字节。下位机收到数据按照上传数据格式上传。

http://www.lryc.cn/news/374929.html

相关文章:

  • 50etf期权交易规则杠杆怎么计算?
  • 鸿蒙: 基础认证
  • 2024年最佳插电式混合动力电动汽车
  • 上海交通大学、中科大 开源镜像站停止 Docker Hub 仓库镜像支持后的可用替代源
  • 【Linux】shell——条件判断test,各种运算符,expr
  • 中介子方程二十二
  • 你还不会选ProfiNET和EtherCAT网线?
  • 醉美酒话:承载着深厚文化底蕴的敬酒词
  • vue3-sfc-loader动态加载一个异步vue组件生成cesium画面
  • flink学习-状态管理
  • OpenCV图像算术位运算
  • 【调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新】
  • ssm宠物网站系统-计算机毕业设计源码07183
  • 想上币的项目方怎么去选择交易所
  • mysql如何创建并执行事件?
  • k8s环境里查看containerd创建的容器对应的netns
  • 学习笔记——网络管理与运维——SNMP(基本配置)
  • CMake从安装到精通
  • 【C++】认识STL
  • 力扣 50.pow(x,n)
  • R可视化:微生物相对丰度或富集热图可视化
  • Unity Maximum Allowed Timestep的说明
  • 长短期记忆神经网络(LSTM)的回归预测(免费完整源代码)【MATLAB】
  • 关于 python request 的 response 返回 b‘\xa3\xff\xff\x11E .....‘ 类型的数据的解决方案
  • 后端高频面试题分享-用Java判断一个列表是否是另一个列表的顺序子集
  • 【数据初步变现】论自助BI在数字化转型中如何赋能业务
  • Python 学习 第二册 第14章 网络编程
  • 微信 小程序应用,页面,组件的生命周期
  • 代码随想录算法训练营Day41|背包问题、分割等和子集
  • oracle SCHEDULER