前陣子一朋友使用單片機(jī)與某外設(shè)進(jìn)行通信時(shí),外設(shè)返回的是一堆格式如下的數(shù)據(jù):
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 是數(shù)據(jù)校驗(yàn)頭,后面三位是有效數(shù)據(jù),問(wèn)我怎么從外設(shè)不斷返回的數(shù)據(jù)中取出有效的數(shù)據(jù)。
對(duì)于這種問(wèn)題最容易想到的就是使用一個(gè)標(biāo)志位用于標(biāo)志當(dāng)前正解析到一幀數(shù)據(jù)的第幾位,然后判斷當(dāng)前接收的數(shù)據(jù)是否與校驗(yàn)數(shù)據(jù)一致,如果一致則將標(biāo)志位加一,否則將標(biāo)志位置0重新判斷,使用這種方法解析數(shù)據(jù)的代碼如下:
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;
}
使用上述方法是最容易想到的也是最簡(jiǎn)單的方法了,百度了一下基本上也都是使用類似的方法進(jìn)行數(shù)據(jù)解析,但是使用這種方法有如下幾個(gè)缺點(diǎn):
1、 大量使用了判斷,容易導(dǎo)致出現(xiàn)邏輯混亂。
2、 代碼重復(fù)率高,抽象程度低。從上述代碼可以看到一大堆代碼僅僅是判斷的數(shù)據(jù)不同,其他代碼都完全一致。
3、 代碼可復(fù)用性差。寫(xiě)好的代碼無(wú)法用在其他類似的外設(shè)上,如果有多個(gè)外設(shè)就需要編寫(xiě)多份類似的代碼。
4、 可擴(kuò)展性低。如果外設(shè)還有一個(gè)數(shù)據(jù)校驗(yàn)尾需要校驗(yàn)或者數(shù)據(jù)校驗(yàn)頭發(fā)生改變,就需要再次寫(xiě)多個(gè)判斷重新用于校驗(yàn),無(wú)法在原有的代碼上進(jìn)行擴(kuò)展。
5、 容易出現(xiàn)誤判 。
對(duì)此,這里提出了一種新的解決方案,可以通用與所有類似的數(shù)據(jù)解析,原理如下:
使用一個(gè)固定容量的隊(duì)列用來(lái)緩存接收到的數(shù)據(jù),隊(duì)列容量等于一幀數(shù)據(jù)的大小,每來(lái)一個(gè)數(shù)據(jù)就將數(shù)據(jù)往隊(duì)列里面加,當(dāng)完整接收到一幀數(shù)據(jù)時(shí)此時(shí)隊(duì)列中的全部數(shù)據(jù)也就是一幀完整的數(shù)據(jù),因此只需要判斷隊(duì)列是否是數(shù)據(jù)校驗(yàn)頭,隊(duì)列尾是否是數(shù)據(jù)校驗(yàn)尾就可以得知當(dāng)前是否已經(jīng)接收到了一幀完整的數(shù)據(jù),然后在將數(shù)據(jù)從隊(duì)列中取出即可。原理圖如下:
每來(lái)一個(gè)數(shù)據(jù)就往隊(duì)列里面加:
?當(dāng)接收到一幀完整數(shù)據(jù)時(shí)隊(duì)列頭和數(shù)據(jù)校驗(yàn)頭重合:
????此時(shí)只需要從隊(duì)列中取出有效數(shù)據(jù)即可。
如果有數(shù)據(jù)尾校驗(yàn),僅僅只需要添加一個(gè)校驗(yàn)尾即可,如下圖所示:
? ??好,分析結(jié)束,開(kāi)始編碼。
首先需要一個(gè)隊(duì)列,為了保證通用性,隊(duì)列底層使用類似于雙向鏈表的實(shí)現(xiàn)(當(dāng)然也可以使用數(shù)組實(shí)現(xiàn)),需要封裝的結(jié)構(gòu)有隊(duì)列容量、隊(duì)列大小、隊(duì)頭節(jié)點(diǎn)和隊(duì)尾節(jié)點(diǎn),需要實(shí)現(xiàn)的操作有隊(duì)列初始化、數(shù)據(jù)入隊(duì)、數(shù)據(jù)出隊(duì)、清空隊(duì)列和釋放隊(duì)列,具體代碼如下:
/* queue.h */
#ifndef _QUEUE_H_
#define _QUEUE_H_
#ifndef NULL
#define NULL ((void *)0)
#endif
typedef unsigned char uint8;
/* 隊(duì)列節(jié)點(diǎn) */
typedef struct Node
{
uint8 data;
struct Node *pre_node;
struct Node *next_node;
} Node;
/* 隊(duì)列結(jié)構(gòu) */
typedef struct Queue
{
uint8 capacity; // 隊(duì)列總?cè)萘? uint8 size; // 當(dāng)前隊(duì)列大小
Node *front; // 隊(duì)列頭節(jié)點(diǎn)
Node *back; // 隊(duì)列尾節(jié)點(diǎn)
} Queue;
/* 初始化一個(gè)隊(duì)列 */
Queue *init_queue(uint8 _capacity);
/* 數(shù)據(jù)入隊(duì) */
uint8 en_queue(Queue *_queue, uint8 _data);
/* 數(shù)據(jù)出隊(duì) */
uint8 de_queue(Queue *_queue);
/* 清空隊(duì)列 */
void clear_queue(Queue *_queue);
/* 釋放隊(duì)列 */
void release_queue(Queue *_queue);
#endif
/* queue.c */
#include
#include "parser.h"
/**
* 初始化一個(gè)隊(duì)列
*
* @_capacity: 隊(duì)列總?cè)萘? */
Queue *init_queue(uint8 _capacity)
{
Queue *queue = (Queue *)malloc(sizeof(Queue));
queue->capacity = _capacity;
queue->size = 0;
return queue;
}
/**
* 數(shù)據(jù)入隊(duì)
*
* @_queue: 隊(duì)列
* @_data: 數(shù)據(jù)
**/
uint8 en_queue(Queue *_queue, uint8 _data)
{
if(_queue->size < _queue->capacity)
{
Node *node = (Node *)malloc(sizeof(Node));
node->data = _data;
node->next_node = NULL;
if(_queue->size == 0)
{
node->pre_node = NULL;
_queue->back = node;
_queue->front = _queue->back;
}
else
{
node->pre_node = _queue->back;
_queue->back->next_node = node;
_queue->back = _queue->back->next_node;
}
_queue->size++;
}
else
{
Node *temp_node = _queue->front->next_node;
_queue->front->pre_node = _queue->back;
_queue->back->next_node = _queue->front;
_queue->back = _queue->back->next_node;
_queue->back->data = _data;
_queue->back->next_node = NULL;
_queue->front = temp_node;
}
return _queue->size-1;
}
/**
* 數(shù)據(jù)出隊(duì)
*
* @_queue: 隊(duì)列
*
* @return: 出隊(duì)的數(shù)據(jù)
*/
uint8 de_queue(Queue *_queue)
{
uint8 old_data = 0;
if(_queue->size > 0)
{
old_data = _queue->front->data;
if(_queue->size == 1)
{
free(_queue->front);
_queue->front = NULL;
_queue->back = NULL;
}
else
{
_queue->front = _queue->front->next_node;
free(_queue->front->pre_node);
_queue->front->pre_node = NULL;
}
_queue->size--;
}
return old_data;
}
/**
* 清空隊(duì)列
*
* @_queue: 隊(duì)列
*/
void clear_queue(Queue *_queue)
{
while(_queue->size > 0)
{
de_queue(_queue);
}
}
/**
* 釋放隊(duì)列
*
* @_queue: 隊(duì)列
*/
void release_queue(Queue *_queue)
{
clear_queue(_queue);
free(_queue);
_queue = NULL;
}
其次是解析器,需要封裝的結(jié)構(gòu)有解析數(shù)據(jù)隊(duì)列、數(shù)據(jù)校驗(yàn)頭、數(shù)據(jù)校驗(yàn)尾、解析結(jié)果以及指向解析結(jié)果的指針,需要實(shí)現(xiàn)的操作有解析器初始化、添加數(shù)據(jù)解析、獲取解析結(jié)果、重置解析器和釋放解析器,具體代碼如下:
/* parser.h */
#ifndef _PARSER_H_
#define _PARSER_H_
#include "queue.h"
typedef enum
{
RESULT_FALSE,
RESULT_TRUE
} ParserResult;
/* 解析器結(jié)構(gòu) */
typedef struct DataParser
{
Queue *parser_queue; // 數(shù)據(jù)解析隊(duì)列
Node *resule_pointer; // 解析結(jié)果數(shù)據(jù)指針
uint8 *data_header; // 數(shù)據(jù)校驗(yàn)頭指針
uint8 header_size; // 數(shù)據(jù)校驗(yàn)頭大小
uint8 *data_footer; // 數(shù)據(jù)校驗(yàn)尾指針
uint8 footer_size; // 數(shù)據(jù)校驗(yàn)尾大小
uint8 result_size; // 解析數(shù)據(jù)大小
ParserResult parserResult; // 解析結(jié)果
} DataParser;
/* 初始化一個(gè)解析器 */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size);
/* 將數(shù)據(jù)添加到解析器中進(jìn)行解析 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data);
/* 解析成功后從解析器中取出解析結(jié)果 */
int parser_get_data(DataParser *_parser, uint8 _index);
/* 重置解析器 */
void parser_reset(DataParser *_parser);
/* 釋放解析器 */
void parser_release(DataParser *_parser);
#endif
/* parser.c */
#include
#include "parser.h"
/**
* 初始化一個(gè)解析器
*
* @_data_header: 數(shù)據(jù)頭指針
* @_header_size: 數(shù)據(jù)頭大小
* @_data_footer: 數(shù)據(jù)尾指針
* @_foot_size: 數(shù)據(jù)尾大小
* @_data_frame_size: 一幀完整數(shù)據(jù)的大小
*
* @return: 解析器
*/
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size)
{
if((_header_size+_foot_size) > _data_frame_size || (_header_size+_foot_size) == 0)
return NULL;
DataParser *parser = (DataParser *)malloc(sizeof(DataParser));
parser->parser_queue = init_queue(_data_frame_size);
parser->resule_pointer = NULL;
parser->data_header = _data_header;
parser->header_size = _header_size;
parser->data_footer = _data_footer;
parser->footer_size = _foot_size;
parser->result_size = _data_frame_size - parser->header_size - parser->footer_size;
parser->parserResult = RESULT_FALSE;
while(_data_frame_size-- > 0)
{
en_queue(parser->parser_queue, 0);
}
return parser;
}
/**
* 將數(shù)據(jù)添加到解析器中進(jìn)行解析
*
* @_parser: 解析器
* @_data: 要解析的數(shù)據(jù)
*
* @return: 當(dāng)前解析結(jié)果,返回 RESULT_TRUE 代表成功解析出一幀數(shù)據(jù)
*/
ParserResult parser_put_data(DataParser *_parser, uint8 _data)
{
uint8 i;
Node *node;
if(_parser == NULL)
return RESULT_FALSE;
en_queue(_parser->parser_queue, _data);
/* 校驗(yàn)數(shù)據(jù)尾 */
node = _parser->parser_queue->back;
for(i = _parser->footer_size; i > 0; i--)
{
if(node->data != _parser->data_footer[i-1])
goto DATA_FRAME_FALSE;
node = node->pre_node;
}
/* 校驗(yàn)數(shù)據(jù)頭 */
node = _parser->parser_queue->front;
for(i = 0; i < _parser->header_size; i++)
{
if(node->data != _parser->data_header[i])
goto DATA_FRAME_FALSE;
node = node->next_node;
}
if(_parser->resule_pointer == NULL && _parser->result_size > 0)
_parser->resule_pointer = node;
if(_parser->parserResult != RESULT_TRUE)
_parser->parserResult = RESULT_TRUE;
return _parser->parserResult;
DATA_FRAME_FALSE:
if(_parser->resule_pointer != NULL)
_parser->resule_pointer = NULL;
if(_parser->parserResult != RESULT_FALSE)
_parser->parserResult = RESULT_FALSE;
return _parser->parserResult;
}
/**
* 解析成功后從解析器中取出解析結(jié)果
*
* @_parser: 解析器
* @_index: 解析結(jié)果集合中的第 _index 個(gè)數(shù)據(jù)
*
* @return: 獲取解析成功的數(shù)據(jù),返回 -1 代表數(shù)據(jù)獲取失敗
*/
int parser_get_data(DataParser *_parser, uint8 _index)
{
Node *node;
if(_parser == NULL
|| _parser->parserResult != RESULT_TRUE
|| _index >= _parser->result_size
|| _parser->resule_pointer == NULL)
return -1;
node = _parser->resule_pointer;
while(_index > 0)
{
node = node->next_node;
_index--;
}
return node->data;
}
/**
* 重置解析器
*
* @_parser: 解析器
*/
void parser_reset(DataParser *_parser)
{
uint8 _data_frame_size;
if(_parser == NULL)
return;
_data_frame_size = _parser->parser_queue->size;
while(_data_frame_size-- > 0)
{
en_queue(_parser->parser_queue, 0);
}
_parser->resule_pointer = NULL;
_parser->parserResult = RESULT_FALSE;
}
/**
* 釋放解析器
*
* @_parser: 解析器
*/
void parser_release(DataParser *_parser)
{
if(_parser == NULL)
return;
release_queue(_parser->parser_queue);
free(_parser);
_parser = NULL;
}
接下來(lái)編寫(xiě)測(cè)試代碼測(cè)試一下:
/* main.c */
#include
#include "parser.h"
int main()
{
uint8 i;
// 數(shù)據(jù)頭
uint8 data_header[] = {0xAA, 0xAA, 0x04, 0x80, 0x02};
// 要解析的數(shù)據(jù),測(cè)試用
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
};
/**
* 初始化一個(gè)解析器
* 第一個(gè)參數(shù)是數(shù)據(jù)頭
* 第二個(gè)參數(shù)是數(shù)據(jù)頭長(zhǎng)度
* 第三個(gè)參數(shù)是數(shù)據(jù)尾指針
* 第四個(gè)參數(shù)是數(shù)據(jù)尾大小
* 第五個(gè)參數(shù)是一整幀數(shù)據(jù)的大小
*/
DataParser *data_parser = parser_init(data_header, sizeof(data_header), NULL, 0, 8);
// 將要解析的數(shù)據(jù)逐個(gè)取出,添加到解析器中
for(i = 0; i < sizeof(data); i++)
{
// 解析數(shù)據(jù),返回 RESULT_TRUE 代表成功解析出一組數(shù)據(jù)
if(parser_put_data(data_parser, data[i]) == RESULT_TRUE)
{
printf("成功解析出一幀數(shù)據(jù)...
");
/* 一位一位取出解析后的數(shù)據(jù) */
printf("第一個(gè)數(shù)據(jù)是:0x%x
", parser_get_data(data_parser, 0));
printf("第二個(gè)數(shù)據(jù)是:0x%x
", parser_get_data(data_parser, 1));
printf("第三個(gè)數(shù)據(jù)是:0x%x
", parser_get_data(data_parser, 2));
}
}
// 當(dāng)不再需要解析器時(shí),應(yīng)該把解析器釋放掉,回收內(nèi)存,避免造成內(nèi)存泄漏
parser_release(data_parser);
return 0;
}
測(cè)試結(jié)果如下:

從上面可以看出,解析的結(jié)果與目標(biāo)一致。
github地址:
https://github.com/528787067/DataFrameParser
審核編輯:湯梓紅
-
單片機(jī)
+關(guān)注
關(guān)注
6074文章
45322瀏覽量
662901 -
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7314瀏覽量
93914 -
代碼
+關(guān)注
關(guān)注
30文章
4940瀏覽量
73074 -
數(shù)據(jù)解析
+關(guān)注
關(guān)注
0文章
14瀏覽量
3676
原文標(biāo)題:一種單片機(jī)數(shù)據(jù)解析方法
文章出處:【微信號(hào):c-stm32,微信公眾號(hào):STM32嵌入式開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
介紹一種簡(jiǎn)單的數(shù)據(jù)解析方法
單片機(jī)多機(jī)并行通訊的一種方法
用VB6.0數(shù)字示波單片機(jī)實(shí)時(shí)監(jiān)測(cè)數(shù)據(jù)的一種方法
一種PC與單片機(jī)多機(jī)RS232串口通信設(shè)計(jì)
一種基于單片機(jī)的電子密碼鎖的設(shè)計(jì)
一種簡(jiǎn)單多功能單片機(jī)系統(tǒng)設(shè)計(jì)
一種單片機(jī)系統(tǒng)RAM的低功耗測(cè)試方法
一種單片機(jī)數(shù)據(jù)解析方法
一種單片機(jī)多機(jī)通信系統(tǒng)的設(shè)計(jì)

一種單片機(jī)數(shù)據(jù)解析方法
評(píng)論