STM32 serial communication link list receives variable length data frames

Data frame description

STM32 data register is usartx - > Dr register

It can be seen that only [8:0] bits of the DR register can be used, and the 8th bit is used for parity, that is, the DR register can only accept 8 bits, i.e. 1 byte of data at a time.

An inappropriate analogy

For example, a basket (DR register) can only hold 8 (bit) items,
We use this basket to put the fruit in our warehouse (MCU),
Others put items into the basket one by one. When they are full of 8, we put the basket into the warehouse.
But we think it's too messy. We delimited a piece of ground in the warehouse, pasted an apple label on it, and put the things we received here in the future.

However, there are not only apples in the warehouse, and we are not sure whether the apples are put in my basket. That is to say, when we receive the data for a single time, there is basically no way to judge the reliability of the data, and the data is single.

Well, let's make a rule. If you put three apples and five oranges in my basket, you'll start sending useful data,

You put apples in the second basket,

You put oranges in the third basket,

If the fourth basket contains three oranges and five apples, the reception is over,
Then I don't have to check which basket is an apple and which is an orange,
You can directly put the second basket in the apple position and the third basket in the orange position.

The analogy is inappropriate, but it basically means that we can receive a variety of data in one frame, and improve the reliability of data by determining the start and end protocols.

But the problem comes again. We only have one basket and can't receive so many baskets of data at once,

What should we do? We set up a buffer area in the warehouse to store the data sent by others, that is, we will process the data after receiving it. However, this buffer area is generally defined by array, that is, it should be specified in advance. If you send me 8 baskets, I will set 8 baskets. Once we start receiving data, I can't change the size of the row in advance, because the length should be given in advance when defining the array.

Na, is there a way to divide the space while receiving? I've been looking at the related knowledge of linked lists recently, so I thought of using linked lists for data buffers, so that I can open up space while receiving data.

Data buffer linked list structure

struct Frame
{
    u8 data;            //Data domain
	struct Frame *next; //The pointer field points to the next node
 };

The linked list I use is relatively simple. On the one hand, it is used to save space. On the other hand, I don't know much.

The data field is used to store 8bit data from the serial port,

The pointer field is used to point to the next node to connect the two nodes. The last node points to NULL, representing the end of the linked list.

The basic structure is like this:

The head node is used to point to the next data. Data stores the number of nodes in a frame.

For example, the data of one frame is A5 34 56 5A

Then: head - > data = 4, that is, a total of 4 nodes are received after removing the head node

Be careful!! Unused pointers must point to NULL to prevent memory leakage caused by wild pointers.

Then we define a global header pointer that can be used anywhere in the program

struct Frame *Head=NULL; //Head pointer
extern struct Frame *Head;//Declare global variables

The header pointer now has no storage space because it is only address information

/**
**********************************
* Function name: Head_Init
* Description: initializes the header node
* Input: None
* Output: None
* Note:
**********************************
*/
void Head_Init(void)
{
    Head=(struct Frame *)malloc(sizeof(struct Frame));
    if(Head==NULL)
        exit(1);

    Head->data=0;
    Head->next=NULL;
}

After we allocate memory to the head pointer, the head pointer is the head node,
The data of the head node can store the node length.

Then we can write our protocol in the serial port interrupt

#define Frame_Head 0xA5 / / frame header
#define Frame_ End 0x5a / / end of frame

/**********Frame header and frame footer flag bits**********/
bool Frame_Head_sta=0;
bool Frame_End_sta=0;

/**
**********************************
* Function name: USART1_IRQHandler
* Description: the accepted data format is: data len gt h - > frame header - > Data - > Data... - > End of frame
*                         Example: 4 A5 12 34 56 5A
* Input: None
* Output: None
* Note:
**********************************
*/
void USART1_IRQHandler(void)               
{
    u8 Res;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
    {
        Res =USART_ReceiveData(USART1);	//Read received data
        //Determine whether the frame header is received
        if(Res==Frame_Head)
        {
            Frame_Head_sta=1;
        }//Judge whether the frame end is accepted - > if the frame head is not accepted, the frame end is not accepted
        else if(Res==Frame_END)
        {
            Frame_End_sta=1;
        }

        //Frame header or end has been received
        if((Frame_Head_sta==1)||(Frame_End_sta==1))
        {
            //Data length plus one
            Head->data+=1;
            //Add data into linked list - > tail interpolation
            Head=Add_Data(&Head,Res);
        }
    }
}

The agreement is relatively simple. You can basically understand it by looking at the notes

The tail insertion method is adopted for the insertion method of the linked list. In fact, the head insertion method is faster and does not need rotation training data, but I feel that it is not comfortable to process the data in this way, so I wrote the tail insertion method.

/**
**********************************
* Function name: Add_Data
* Description: insert nodes using tail interpolation
* Input: (the data to be inserted by the pointer to the header node)
* Output: None
* Note:
**********************************
*/
struct Frame* Add_Data(struct Frame **head,u8 Res)
{
    struct Frame *data,*temp;
    //Allocate memory
    data=(struct Frame *)malloc(sizeof(struct Frame));
    //The data field stores serial port data
    data->data=Res;       
    //The pointer field points to NULL
    data->next=NULL;
    //The position pointed by the pointer to the header is not null - > other nodes have been connected behind the header node
    if(*head!=NULL)
    {
        //Pass the pointer to the header node to temp - > ensure that * head does not change
        temp=*head;
        //Query one node after another until temp - > next = null, that is, the location of the last node
        while(temp->next)
        {
            temp=temp->next;
        }
        //Change the position pointed to by the last node to data, that is, insert a new node in the last node
        temp->next=data;
    }
    else//The node has not been inserted after the header node
    {
        *head=data;
    }
    return *head;
}

After a frame of data is received, it must be processed in time to release memory space, otherwise it will cause memory overflow and program flying.

Here I have another problem. The data I accept is of variable length, so if I store it, it should be of variable length

But I didn't have a good idea at that time, so I wrote the following code

/**
/**
**********************************
* Function name: Frame_Manage
* Description: linked list data processing
* Input: None
* Output: None
* Note:
**********************************
*/
void Frame_Manage(  u8 *data0,u8 *data1,u8 *data2,
                    u8 *data3,u8 *data4,u8 *data5,
                    u8 *data6,u8 *data7,u8 *data8)
{
    u8 i;
    //Received frame header and end
    if(Frame_Head_sta&&Frame_End_sta)
    {
        //Flag bit reset
        Frame_Head_sta=0;
        Frame_End_sta=0;
        //Traverse the linked list and store data
        while(Head!=NULL)
        {   
            switch(i)
            {
                case 0: *data0=Head->data;break;
                case 1: *data1=Head->data;break;
                case 2: *data2=Head->data;break;
                case 3: *data3=Head->data;break;
                case 4: *data4=Head->data;break;
                case 5: *data5=Head->data;break;
                case 6: *data6=Head->data;break;
                case 7: *data7=Head->data;break;
                case 8: *data8=Head->data;break;
            }
            //Free node memory
            free(Head);
            //Address down
            Head=Head->next;
            i++;
        }
        //Allocate memory to the head node again because the memory of the speculative point has been released
        Head_Init();
    }
}

This code is really uncomfortable. It is an indefinite length acceptance, and then it becomes a fixed length storage

Later, I thought about the length of data stored in the header node,
You can directly use this data length and use malloc to open up a space of the same length to store frame data,

Use the u8 type pointer to point to the opened memory space for storing a frame of data,

Record the location of the first address and pass through the frame_ After the manage() function,

Our frame data is stored in * frame_ Space pointed to by data

The next time the frame data comes, you can free up space and update the data length again

The modified code is as follows:

u8 *Frame_data=NULL;     //Frame data buffer
u8 *Frame_data_Head=NULL;//It is only used to store the first address of the buffer

extern u8 *Frame_data;     //Frame data storage
extern u8 *Frame_data_Head;//It is only used to store the first address of the buffer

/**
**********************************
* Function name: Frame_Manage
* Description: linked list data processing
* Input: None
* Output: None
* Note:
**********************************
*/
void Frame_Manage(void)
{
    //If the header and footer are received
    if(Frame_Head_sta&&Frame_End_sta)
    {       
        //Free the last buffer space - > put in Frame_data_Head
        free(Frame_data_Head);
        //Point to null - > prevent random pointing
        Frame_data=NULL;
        //Size of one frame (variable)
        Frame_data=(u8 *)malloc(sizeof(u8)*(Head->data));
        //First address of storage space - > for free and reading data
        Frame_data_Head=Frame_data;
        //Clear flag bit
        Frame_Head_sta=0;
        Frame_End_sta=0;
        while(Head!=NULL)
        {   
            //Store frame data
            *Frame_data=(Head->data);
            //Address - > move back
            Frame_data++;
            free(Head);
            Head=Head->next;
        }
        Head_Init();
        //Return space first address
        Frame_data=Frame_data_Head;
    }
}

The usage of this space is the same as that of a pointer to an array,

Through the following code, you can read the data at any point of the data frame

/**
**********************************
* Function name: Find_Frame
* Description: read fixed position data
* Input: location, starting address
* Output: u8 data
* Frame format: 4 A5 34 56 5A
* Location: 0 1 2 3 4
**********************************
*/
u8 Find_Frame(u8 team,u8 *head)
{
    head+=team;
    return *head;
}

Obviously, the 8-bit unsigned type is obviously not enough
It is written that the two u8 s merge into u16, and the fusion ideas of other types are basically similar

/**
**********************************
* Function name: Fusion
* Description: u8 fusion u16
* Input: team0 high 8-bit position team1 low 8-bit position
* Output: None
* Note:
**********************************
*/
u16 Fusion(u8 team0,u8 team1)
{
    team0=Find_Frame(team0,Frame_data);
    team1=Find_Frame(team1,Frame_data);
    return ((team0<<8)+team1);
}

Effect display

We directly send the hexadecimal data frame containing the frame header (A5) and the frame tail (5A) through the serial port, and STM32 can receive the data frame and print it.

It can be seen that when the length of the data frame we send changes, STM32 can also receive the data frame of that length without modifying the program. It is very convenient, and it is also very user-friendly.

The file is placed below. The chip type is F103VCT6

Engineering documents

Extraction code: qqy7

Keywords: stm32

Added by bmbc on Tue, 04 Jan 2022 01:50:18 +0200