单片机接收数据帧帧头帧尾校验数据解析
来源:互联网 发布:饭店点餐软件 编辑:程序博客网 时间:2024/06/05 03:52
转自:http://blog.csdn.net/xr528787067/article/details/52822377
前阵子一朋友使用单片机与某外设进行通信时,外设返回的是一堆格式如下的数据:
- AA AA 04 80 02 00 02 7B AA AA 04 80 02 00 08 75 AA AA 04 80 02 00 9B E2 AA AA 04 80 02 00 F6 87 AA AA 04 80 02 00 EC 91
其中 AA AA 04 80 02 是数据校验头,后面三位是有效数据,问我怎么从外设不断返回的数据中取出有效的数据。
对于这种问题最容易想到的就是使用一个标志位用于标志当前正解析到一帧数据的第几位,然后判断当前接收的数据是否与校验数据一致,如果一致则将标志位加一,否则将标志位置0重新判断,使用这种方法解析数据的代码如下:
- if(flag == 0)
- {
- if(tempData == 0xAA)
- flag++;
- else
- flag = 0;
- }
- else if(flag == 1)
- {
- if(tempData == 0xAA)
- flag++;
- else
- flag = 0;
- }
- else if(flag == 2)
- {
- if(tempData == 0x04)
- flag++;
- else
- flag = 0;
- }
- else if(flag == 3)
- {
- if(tempData == 0x80)
- flag++;
- else
- flag = 0;
- }
- else if(flag == 4)
- {
- if(tempData == 0x02)
- flag++;
- else
- flag = 0;
- }
- else if(flag == 5 || flag == 6 || flag == 7)
- {
- data[flag-5] = tempData;
- flag = (flag == 7) ? 0 : flag+1;
- }
使用上述方法是最容易想到的也是最简单的方法了,百度了一下基本上也都是使用类似的方法进行数据解析,但是使用这种方法有如下几个缺点:
1、 大量使用了判断,容易导致出现逻辑混乱
2、 代码重复率高,抽象程度低。从上述代码可以看到一大堆代码仅仅是判断的数据不同,其他代码都完全一致
3、 代码可复用性差。写好的代码无法用在其他类似的外设上,如果有多个外设就需要编写多份类似的代码
4、 可扩展性低。如果外设还有一个数据校验尾需要校验或者数据校验头发生改变,就需要再次写多个判断重新用于校验,无法在原有的代码上进行扩展
5、 容易出现误判
对此,这里提出了一种新的解决方案,可以通用与所有类似的数据解析,原理如下:
使用一个固定容量的队列用来缓存接收到的数据,队列容量等于一帧数据的大小,每来一个数据就将数据往队列里面加,当完整接收到一帧数据时此时队列中的全部数据也就是一帧完整的数据,因此只需要判断队列是否是数据校验头,队列尾是否是数据校验尾就可以得知当前是否已经接收到了一帧完整的数据,然后在将数据从队列中取出即可。原理图如下:
每来一个数据就往队列里面加:
当接收到一帧完整数据时队列头和数据校验头重合:
此时只需要从队列中取出有效数据即可。如果有数据尾校验,仅仅只需要添加一个校验尾即可,如下图所示:
好,分析结束,开始编码。
首先需要一个队列,为了保证通用性,队列底层使用类似于双向链表的实现(当然也可以使用数组实现),需要封装的结构有队列容量、队列大小、队头节点和队尾节点,需要实现的操作有队列初始化、数据入队、数据出队、清空队列和释放队列,具体代码如下:
-
-
- #ifndef _QUEUE_H_
- #define _QUEUE_H_
-
- #ifndef NULL
- #define NULL ((void *)0)
- #endif
-
- typedef unsigned char uint8;
-
-
- typedef struct Node
- {
- uint8 data;
- struct Node *pre_node;
- struct Node *next_node;
- } Node;
-
-
- typedef struct Queue
- {
- uint8 capacity;
- uint8 size;
- Node *front;
- Node *back;
- } Queue;
-
-
- Queue *init_queue(uint8 _capacity);
-
- uint8 en_queue(Queue *_queue, uint8 _data);
-
- uint8 de_queue(Queue *_queue);
-
- void clear_queue(Queue *_queue);
-
- void release_queue(Queue *_queue);
-
- #endif
其次是解析器,需要封装的结构有解析数据队列、数据校验头、数据校验尾、解析结果以及指向解析结果的指针,需要实现的操作有解析器初始化、添加数据解析、获取解析结果、重置解析器和释放解析器,具体代码如下:
-
-
- #ifndef _PARSER_H_
- #define _PARSER_H_
-
- #include "queue.h"
-
- typedef enum
- {
- RESULT_FALSE,
- RESULT_TRUE
- } ParserResult;
-
-
- typedef struct DataParser
- {
- Queue *parser_queue;
- Node *resule_pointer;
- uint8 *data_header;
- uint8 header_size;
- uint8 *data_footer;
- uint8 footer_size;
- uint8 result_size;
- ParserResult parserResult;
- } DataParser;
-
-
- DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size);
-
- ParserResult parser_put_data(DataParser *_parser, uint8 _data);
-
- int parser_get_data(DataParser *_parser, uint8 _index);
-
- void parser_reset(DataParser *_parser);
-
- void parser_release(DataParser *_parser);
-
- #endif
接下来编写测试代码测试一下:
-
-
- #include <stdio.h>
- #include "parser.h"
-
- int main()
- {
- uint8 i;
-
- uint8 data_header[] = {0xAA, 0xAA, 0x04, 0x80, 0x02};
-
- uint8 data[] = {
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x02, 0x7B, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x00, 0x08, 0x75, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x9B, 0xE2,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xF6, 0x87, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x00, 0xEC, 0x91, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x15, 0x67,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x49, 0x33, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x00, 0xE7, 0x96, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x68, 0x15,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x3C, 0x41, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x00, 0x66, 0x17, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xA5, 0xD8,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x26, 0x56, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x01, 0x73, 0x09, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x64, 0x18,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x8B, 0xF1, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x01, 0xC6, 0xB6, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x7B, 0x01,
- 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xCB, 0xB2, 0xAA, 0xAA, 0x04, 0x80,
- 0x02, 0x00, 0x2C, 0x51, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0xFF, 0xE5, 0x99
- };
-
-
-
-
-
-
-
-
-
- DataParser *data_parser = parser_init(data_header, sizeof(data_header), NULL, 0, 8);
-
-
- for(i = 0; i < sizeof(data); i++)
- {
-
- if(parser_put_data(data_parser, data[i]) == RESULT_TRUE)
- {
- printf("成功解析出一帧数据...\n");
-
-
- printf("第一个数据是:0x%x\n", parser_get_data(data_parser, 0));
- printf("第二个数据是:0x%x\n", parser_get_data(data_parser, 1));
- printf("第三个数据是:0x%x\n\n\n", parser_get_data(data_parser, 2));
- }
- }
-
-
- parser_release(data_parser);
-
- return 0;
- }
测试结果如下:
从上面可以看出,解析的结果与目标一致。
github地址:https://github.com/528787067/DataFrameParser