Analysis of checking data at the beginning and end of data frame received by single chip microcomputer

A while ago, when a friend used a single chip microcomputer to communicate with a peripheral, the peripheral returned a pile of data in the following format:

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 04 80 02 is the data verification header, and the last three bits are valid data. Ask me how to get valid data from the data continuously returned by the peripheral.

The easiest way to think of this problem is to use a flag bit to mark the number of bits of data currently being parsed to a frame, and then judge whether the currently received data is consistent with the verification data. If so, add one flag bit, otherwise re judge the flag position 0. The code for parsing data using this method is as follows:

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;
}

Using the above method is the easiest and simplest method to think of. Baidu basically uses similar methods for data analysis, but this method has the following disadvantages:

1. Extensive use of judgment can easily lead to logical confusion.

2. High code repetition rate and low degree of abstraction. From the above codes, we can see that a large number of codes are only different from the judgment data, and other codes are completely consistent.

3. Poor code reusability. The written code cannot be used on other similar peripherals. If there are multiple peripherals, you need to write multiple copies of similar code.

4. Low scalability. If the peripheral has a data verification tail to be verified or the data verification head is changed, multiple judgments need to be written again for verification, which cannot be extended on the original code.

5. Prone to miscalculation.

In this regard, a new solution is proposed, which can be used for all similar data analysis. The principle is as follows:

A fixed capacity queue is used to cache the received data. The queue capacity is equal to the size of a frame of data. For each data, the data is added to the queue. When a frame of data is completely received, all the data in the queue is a frame of complete data. Therefore, it is only necessary to judge whether the queue is a data verification head, Whether the end of the queue is the end of data verification can tell whether a complete frame of data has been received, and then take the data out of the queue. The schematic diagram is as follows:

For each data, add to the queue:


When a frame of complete data is received, the queue header coincides with the data check header:

At this time, you only need to take valid data from the queue.
If there is a data tail check, you only need to add a check tail, as shown in the following figure:

OK, the analysis is over and the coding begins.

First, a queue is needed. In order to ensure universality, the bottom layer of the queue uses an implementation similar to a two-way linked list (of course, it can also be implemented by using an array). The structures to be encapsulated include queue capacity, queue size, queue head node and queue tail node. The operations to be implemented include queue initialization, data in, data out, clearing the queue and releasing the queue. The specific codes are as follows:

/* queue.h */
 
#ifndef _QUEUE_H_
#define _QUEUE_H_
 
#ifndef NULL
#define NULL ((void *)0)
#endif
 
typedef unsigned char uint8;
 
/* Queue node */
typedef struct Node
{
 uint8 data;
 struct Node *pre_node;
 struct Node *next_node;
} Node;
 
/* Queue structure */
typedef struct Queue
{
 uint8 capacity;     // Total queue capacity
 uint8 size;         // Current queue size
 Node *front;        // Queue header node
 Node *back;         // Queue tail node
} Queue;
 
/* Initialize a queue */
Queue *init_queue(uint8 _capacity);
/* Data entry */
uint8 en_queue(Queue *_queue, uint8 _data);
/* Data out of line */
uint8 de_queue(Queue *_queue);
/* Empty queue */
void clear_queue(Queue *_queue);
/* Release queue */
void release_queue(Queue *_queue);
 
#endif
/* queue.c */
 
#include <stdlib.h>
#include "parser.h"
 
/**
 * Initialize a queue
 *
 * @_capacity: Total queue capacity
 */
Queue *init_queue(uint8 _capacity)
{
 Queue *queue = (Queue *)malloc(sizeof(Queue));
 queue->capacity = _capacity;
 queue->size = 0;
 return queue;
}
 
/**
 * Data queue
 *
 * @_queue: queue
 * @_data: 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;
}
 
/**
 * Data out of line
 *
 * @_queue: queue
 *
 * @return: Outgoing data
 */
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;
}
 
/**
 * Empty queue
 *
 * @_queue: queue
 */
void clear_queue(Queue *_queue)
{
    while(_queue->size > 0)
    {
        de_queue(_queue);
    }
}
 
/**
 * Release queue
 *
 * @_queue: queue
 */
void release_queue(Queue *_queue)
{
    clear_queue(_queue);
    free(_queue);
    _queue = NULL;
}

The second is the parser. The structures to be encapsulated include parsing data queue, data verification header, data verification tail, parsing result and pointer to parsing result. The operations to be realized include parser initialization, adding data parsing, obtaining parsing result, resetting parser and releasing parser. The specific codes are as follows:

/* parser.h */
 
#ifndef _PARSER_H_
#define _PARSER_H_
 
#include "queue.h"
 
typedef enum
{
    RESULT_FALSE,
    RESULT_TRUE
} ParserResult;
 
/* Parser structure */
typedef struct DataParser
{
    Queue *parser_queue;   // Data parsing queue
    Node *resule_pointer;   // Parse result data pointer
    uint8 *data_header;    // Data check header pointer
    uint8 header_size;    // Data check header size
    uint8 *data_footer;    // Data check tail pointer
    uint8 footer_size;    // Data check tail size
    uint8 result_size;    // Parse data size
    ParserResult parserResult;  // Analytical results
} DataParser;
 
/* Initialize a parser */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size);
/* Add the data to the parser for parsing */
ParserResult parser_put_data(DataParser *_parser, uint8 _data);
/* After successful parsing, take the parsing result from the parser */
int parser_get_data(DataParser *_parser, uint8 _index);
/* Reset parser */
void parser_reset(DataParser *_parser);
/* Release parser */
void parser_release(DataParser *_parser);
 
#endif
/* parser.c */
 
#include <stdlib.h>
#include "parser.h"
 
/**
 * Initialize a parser
 *
 * @_data_header: Header pointer
 * @_header_size: Header size
 * @_data_footer: Tail pointer
 * @_foot_size: Data tail size
 * @_data_frame_size: Size of a frame of complete data
 *
 * @return: Parser
 */
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;
}
 
/**
 * Add the data to the parser for parsing
 *
 * @_parser: Parser
 * @_data: Data to parse
 *
 * @return: Current parsing result, return RESULT_TRUE indicates that a frame of data has been successfully parsed
 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data)
{
    uint8 i;
    Node *node;
 
 if(_parser == NULL)
  return RESULT_FALSE;
 
    en_queue(_parser->parser_queue, _data);
 
 /* Check data tail */
 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;
 }
 
 /* Check data header */
    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;
 
}
 
/**
 * After successful parsing, take the parsing result from the parser
 *
 * @_parser: Parser
 * @_index: The second in the parsing result set_ index data
 *
 * @return: Obtain the data that has been parsed successfully. If - 1 is returned, it means that the data acquisition fails
 */
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;
}
 
/**
 * Reset parser
 *
 * @_parser: 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;
}
 
/**
 * Release parser
 *
 * @_parser: Parser
 */
void parser_release(DataParser *_parser)
{
 if(_parser == NULL)
  return;
    release_queue(_parser->parser_queue);
    free(_parser);
    _parser = NULL;
}

Next, write test code to test:

/* main.c */
 
#include <stdio.h>
#include "parser.h"
 
int main()
{
    uint8 i;
    // Data header
    uint8 data_header[] = {0xAA, 0xAA, 0x04, 0x80, 0x02};
    // Data to be parsed, test with
    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
    };
 
    /**
     * Initialize a parser
     * The first parameter is the data header
     * The second parameter is the header length
     * The third parameter is the tail pointer
     * The fourth parameter is the data tail size
     * The fifth parameter is the size of a whole frame of data
     */
    DataParser *data_parser = parser_init(data_header, sizeof(data_header), NULL, 0, 8);
 
    // Take out the data to be parsed one by one and add it to the parser
    for(i = 0; i < sizeof(data); i++)
    {
        // Parse data and return RESULT_TRUE indicates that a set of data has been successfully parsed
        if(parser_put_data(data_parser, data[i]) == RESULT_TRUE)
        {
            printf("Successfully parsed a frame of data...\n");
 
            /* Take out the parsed data bit by bit */
            printf("The first data is: 0 x%x\n", parser_get_data(data_parser, 0));
            printf("The second data is: 0 x%x\n", parser_get_data(data_parser, 1));
            printf("The third data is: 0 x%x\n\n\n", parser_get_data(data_parser, 2));
        }
    }
 
    // When the parser is no longer needed, the parser should be released to reclaim memory to avoid memory leakage
    parser_release(data_parser);
 
    return 0;
}

The test results are as follows:

As can be seen from the above, the analysis result is consistent with the target.
github address:

https://github.com/528787067/DataFrameParser

reminder

Since WeChat official account has changed the push rules recently, if you want to see our articles frequently, you can click a "Zan" or "look" below the page after each reading, so that each push will appear in your subscription list for the first time.

Disclaimer: This article comes from the Internet and conveys knowledge free of charge. The copyright belongs to the original author. If the copyright of the work is involved, please contact me to delete it.

Tags: stm32

Posted by michaelk46 on Sun, 17 Apr 2022 21:13:40 +0930