什么是数据报文
数据报文是网络中交换与传输的数据单元,即站点一次性要发送的数据块。包含了将要发送的完整的数据信息,其长短很不一致,长度不限且可变。(这是官方的说明)。
而在嵌入式中数据报文就是一段用于设备与设备之间进行信息交换的语言。

数据报文的分类
密文。是通过某种算法加密处理过的数据报文。好比两个人的悄悄话,只要你和我知道其中的意思,而别人就算听到了,也不知道其中的意思。一般用于不想给别人知道的数据传输。
明文。就是一段真实的数据报文,很多人都能看明白,知道其意思的数据报文。一般用于不需要保密的数据的传输。
其他。还有一些可能还没学到的数据报文。

数据报文的另一种分类

数值报文。报文内容都是由数值组成。通常是以16进制的形式。
数值报文的报文结构
一段完整的报文包含报头,报尾和数据。报头的作用是判断数据的到来,报尾的作用是判断整段的报文是否已经接收完整,或者把一个校验值放在报尾用于校验接收的报文是否出现错误或者丢失。以下是某传感器使用串口通信时的数值报文:
10e21dfab7a648239b68466ec1646d2d?from=pc.jpg
一些传感器的说明文档都会有说明传感器所使用的报文的说明。因为报文的定义并不是标准的,都是按照一段的规定去自定义。别人想要使用这个传感器就需要清楚上传上来的报文中每一位代表着什么,这样就可以准确的解释出传感器传过来的数值。
那么串口如何准确接收这段报文呢?
传感器的通信方式是串口通信,以下是STM32的串口接收中断函数:
unsigned char CH2Obuf[9] = "";//串口接收缓存
  • Void USART2_IRQHandler()
  • {
  • static float CH2O;
  • u8 res;
  • static char CH2Oflag; //接收标志
  • static int CH2Oi_j = 0; //计数标志,记录接收的数据总共接收了多少位
  • res = USART_ReceiveData(CH2OUSARTx );
  • if(res == 0xFF) //判断是否接收到报头
  • {
  • CH2Oi_j = 0;
  • CH2Oflag = 1;//确定第二位是否接受到
  • }
  • if(CH2Oflag == 1)
  • 复制代码
    这段代码实现的功能是对前面报文的接收,以及解释出所需要的数据。
    CH2Obuf[4]<<8 //这句的意思就是把接收到的数据中的第五位向右移动8位等同于把第5位乘以256的意思。这里为什么是第五位而不是第四位?因为数值的元素是从第0位开始的,而我们数数往往是从1开始的所以CH2Obuf[4]就是数组中第五个元素的意思。
    CH2OCheckSum(CH2Obuf,9);这一句是计算接收的数据的校验值的函数,如果接收到的数据所求出来的校验值跟接收到的校验值相等说明接收到的数据是正确的。以下是传感器说明文档的校验值计算说明和方法:
    5a168859de63486aa1aefd839d4c4f09?from=pc.jpg
    这种校验方法先对来说是比较简单的一种。还有其他一些常用的校验方法有奇偶校验,CRC校验,md5校验和数字签名。这些校验方法都是比较标准的校验方法,但是实现的过程稍微复杂一些。主要的作用还是校验所接收数据的准确性。
    求校验和的程序实现如下:
    unsigned char CH2OCheckSum(unsigned char *i,unsigned char ln)
  • {
  •                         unsigned char j,tempq=0;
  • i+=1;
  • for(j=0;j<(ln-2);j++)
  • {
  • tempq+=*i;
  • i++;
  • }
  • tempq=(~tempq)+1;
  • return(tempq);
  • }
  • 复制代码
    自己如何使用这种报文
    使用这种报文我们可以依葫芦画瓢按照他为的规则来定义报文的结构。步骤如下:
    确定自己传输的数据的长度
    自定义一个报文的头和尾,一般把校验值放到报文的最后一位,也可以不使用校验值,可以直接写一个固定的值在最后一位。下面举一个例子:
    例:有一个单片机系统对两个传感器进行数据读取然后上传到上位机,一个传感器为湿度传感器,一个为PM2.5传感器。湿度的最大值99%RH。PM2.5传感器的最大值为1000ppm。
    在定报文长度的时候需要知道的知识点:
    存储单位:
    1位=0和1
    1个字节=8位,最大值为255
    湿度的最大值为99可以使用1个字节来存,但是PM2.5传感器的最大值为1000,大于256,一个字节存不下那么就需要使用两个字节来存。
    2个字节=16位,最大值为65535
    所以两个传感器的数据占用了3个字节再加上头尾那么最少需要5个字节就可以完成。
    比如现在的湿度数据为90,PM2.5数据为800
    那么如何实现呢?
    程序实现步骤如下:
    先读取传感器的数据保存到一个变量中。
    Unsigned int hum=90;
  • Unsigned int pm25=800;
  • 复制代码
    定义一个数组用于打包成数据报文
    Unsigned char senddata[5];
    复制代码
    把数据存到数组中
    Senddata[0]=0xff;//定义报文的头为ff
  • Senddata[1]=hum; //把湿度存在第二位
  • Senddata[2]=pm25>>8; //PM2.5的数据大于一个字节所以需要把数据分两个字节保存。 (1)
  • Senddata[3]=pm25;//保存的是低八位
  • Senddata[4]=0xfe;//定义一个固定的值为报尾
  • 复制代码
    (1) >>8等同于把PM2.5的数值除以256的意思。为高八位。换成10进制的意思就是128/10=12*10+8,12为高位,8为低位 的意思
    然后通过串口把数据发送出去
    数值报文的应用
    数值报文的解释和打包简单,不需要繁琐的处理过程。所以一般用于内部数据交互,终端和上位机交互,传感器数据传输和各种简单的无线控制等等。

    字符报文
    字符报文是由字符通过一定的规则组成的字符串。常用的有JSON数据报文。Json数据报文如下:
    {"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}
    复制代码
    JSON数据内容是都是以"对"的形式存在,"名称:值"为一对,在整个数据字符中"对"所存在的位置是不固定的。

    Json报文打包
    如果设备只负责把数据发送出去,而不需要解释json数据。这样可以简单的使用sprintf()函数打包。打包成json报文例子如下:
    int main(void)
  • {
  • char sendJSON[256];
  • uart_init(115200); //串口初始化为115200
  • sprintf(sendJSON,"{"name":"%s","age":%d,"address":{"country":"%s","zip-code":"%d"}}","John Doe",18,"china",10000); //把变量和数值打印到字符串
  • printf("%s",sendJSON);
  • }
  • 复制代码
    这种方法不需要使用到第三方的打包和解释的库文件。但是需要自己测试json数据的格式是否正确。方法可以使用json在线解释。
    当然也可以使用第三方标准JSON解释库来进行打包和解释。
    使用第三方库的步骤为:
    下载CJSON库文件。cJSONFiles.zip
    打开压缩包只需把里面的cjson.h和cjson.c文件添加到自己的项目工程中。
    使用相关的函数即可。
    Cjson库文件的使用方法可以baidu一下,有很多使用案例。
    字符报文(json数据)应用
    Json这种数据报文使用的地方还是挺多的,比如网络数据请求,TCP数据传输,大量的数据传输等等,还有微信小程序中也用到了JSON。

    来源:嵌知百汇