当前位置:首页 > 公众号精选 > 嵌入式大杂烩
[导读]前阵子一朋友使用单片机与某外设进行通信时,外设返回的是一堆格式如下的数据。

来源 | 网络

前阵子一朋友使用单片机与某外设进行通信时,外设返回的是一堆格式如下的数据:

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、 容易出现误判  。

对此,这里提出了一种新的解决方案,可以通用与所有类似的数据解析,原理如下:

使用一个固定容量的队列用来缓存接收到的数据,队列容量等于一帧数据的大小,每来一个数据就将数据往队列里面加,当完整接收到一帧数据时此时队列中的全部数据也就是一帧完整的数据,因此只需要判断队列是否是数据校验头,队列尾是否是数据校验尾就可以得知当前是否已经接收到了一帧完整的数据,然后在将数据从队列中取出即可。原理图如下:

每来一个数据就往队列里面加:

当接收到一帧完整数据时队列头和数据校验头重合:

此时只需要从队列中取出有效数据即可。

如果有数据尾校验,仅仅只需要添加一个校验尾即可,如下图所示:

好,分析结束,开始编码。

首先需要一个队列,为了保证通用性,队列底层使用类似于双向链表的实现(当然也可以使用数组实现),需要封装的结构有队列容量、队列大小、队头节点和队尾节点,需要实现的操作有队列初始化、数据入队、数据出队、清空队列和释放队列,具体代码如下:

/* queue.h */ #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 
/* queue.c */ #include  #include "parser.h" /**
 * 初始化一个队列
 *
 * @_capacity: 队列总容量
 */ Queue *init_queue(uint8 _capacity) {
 Queue *queue = (Queue *)malloc(sizeof(Queue)); queue->capacity = _capacity; queue->size = 0; return queue;
} /**
 * 数据入队
 *
 * @_queue: 队列
 * @_data: 数据
 **/ 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;
} /**
 * 数据出队
 *
 * @_queue: 队列
 *
 * @return: 出队的数据
 */ 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;
} /**
 * 清空队列
 *
 * @_queue: 队列
 */ void clear_queue(Queue *_queue) { while(_queue->size > 0)
    {
        de_queue(_queue);
    }
} /**
 * 释放队列
 *
 * @_queue: 队列
 */ void release_queue(Queue *_queue) {
    clear_queue(_queue); free(_queue);
    _queue = NULL;
}

其次是解析器,需要封装的结构有解析数据队列、数据校验头、数据校验尾、解析结果以及指向解析结果的指针,需要实现的操作有解析器初始化、添加数据解析、获取解析结果、重置解析器和释放解析器,具体代码如下:

/* parser.h */ #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 
/* parser.c */ #include  #include "parser.h" /**
 * 初始化一个解析器
 *
 * @_data_header: 数据头指针
 * @_header_size: 数据头大小
 * @_data_footer: 数据尾指针
 * @_foot_size: 数据尾大小
 * @_data_frame_size: 一帧完整数据的大小
 *
 * @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;
} /**
 * 将数据添加到解析器中进行解析
 *
 * @_parser: 解析器
 * @_data: 要解析的数据
 *
 * @return: 当前解析结果,返回 RESULT_TRUE 代表成功解析出一帧数据
 */ ParserResult parser_put_data(DataParser *_parser, uint8 _data) {
    uint8 i;
    Node *node; if(_parser == NULL) return RESULT_FALSE;
 
    en_queue(_parser->parser_queue, _data); /* 校验数据尾 */ 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;
 } /* 校验数据头 */ 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;
 
} /**
 * 解析成功后从解析器中取出解析结果
 *
 * @_parser: 解析器
 * @_index: 解析结果集合中的第 _index 个数据
 *
 * @return: 获取解析成功的数据,返回 -1 代表数据获取失败
 */ 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;
}

接下来编写测试代码测试一下:

/* main.c */ #include  #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++)
    { // 解析数据,返回 RESULT_TRUE 代表成功解析出一组数据 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

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭