网站图片是用什么软件做的,wordpress修改备案号,蚌埠市建设工程质监站网站,免费隐私网站推广app目录 单片机(STM32)与上位机传输数据的方法1. 传输整形数据2. 传输浮点数据3. 如何打包与解包 单片机(STM32)与上位机传输数据的方法
在进行单片机程序的开发时#xff0c;常常需要与其他设备进行通信。一种情况是与其他电路板通信#xff0c;比如STM32主机与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_data和byte[4]共用4个字节的内存单元成员f_data是实际使用的数据成员byte[4]是通信时用的数据各司其职。
使用方法
#include stdio.h
#include stdint.hunion 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。聪明的读者可以发现me和you对应两个设备。只要按照这种方式进行传输就可以传输浮点数。传输多个浮点数me和you就可以定义为一个数组例如me[100], you[100]。
3. 如何打包与解包
知道了数据如何传输第二步就是思考如何进行数据打包和解包了因为一个数据帧当然是要传输多个数据的。需要两个设备定义好通信协议才能正确的解析数据。
数据打包也有两种方式一种按照功能字一种按照地址——数据对。
1按照功能字
这种方法用一个数据位表示功能字对方设备收到这一帧数据根据这个功能字就能判断你这一帧数据是什么然后进行解析。例如一款陀螺仪的数据上传协议为 它用第一个字节表示帧头0x55第二个字节表示功能字0x52是角速度输出0x53是角度输出单片机读陀螺仪的数据时按照它给定的这个协议依次把数据读出来就可以了。
如果是自己定义通信协议也可以模仿这种方式每一帧数据都要进行定义优点是物理意义明确缺点是一旦确定了如果想要修改两端的设备要同时修改。
2按照地址数据对
这种方法模拟计算计的存储方式为每一个数据安排一个地址请注意这个地址并需要是真正的内存地址它的核心是“索引”。例如一个数据就可以实现这种功能。uint16_t data[100]数组的索引就是地址。例如我用data[0]表示姓名data[1]表示年龄。那么姓名的地址就是0年龄的地址就是1。
这种方法的优点和缺点与第一种方法相反物理意义不明确但移植性强、维护性好。
下面是我自创的一种通信协议传输浮点数。前两个字节为帧头不同帧头分别代表从机主动上传、主机下发修改数据、主机下发查询数据。这种通信协议为一对一不支持总线通信
1单片机主动上传数据 发送N个数据32 bits一共4N6个帧字节。
2上位机下发更改数据 发送N个数据32bits也是一共2N6个帧字节。
3上位机下发查询数据 查询从起始地址开始的N个数据查询帧是6个字节。下位机收到数据按照上传数据格式上传。