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