Serial Ring Buffer experiment
Introduction to 1.1 experiment
The simplest method of serial port data processing is that the data is received and sent back in the same mechanism: successfully received a number, triggered into an interrupt, read the data in the interrupt function, and then immediately. This kind of data processing mechanism is "non-buffered interrupt mode", although this data processing method does not consume time, but this kind of data processing method serious disadvantage is: no buffer, if the previously received data if not sent completed (processing completed), and then the serial port and receive new data, The newly received data overwrites the data that has not yet been processed, resulting in "packet loss".
The simplest way to "data drops" is to use an array to receive the data: the array subscript offset for each data received. Although this can play a certain "buffering effect", but the space of the array is not well utilized, the processed data will still occupy the original data space, until the array is "full" (each element of the array holds valid data), the entire array of data processing completed, re-clear the zero group, To open a new round of data reception.
So, what good data reception processing mechanism can play "buffering" effect, but also can effectively use "array space". The answer is: Yes, that's the ring buffer.
A ring buffer is an array with a "head pointer" and a "tail pointer". The head pointer points to the readable data in the ring buffer, and the tail pointer points to the writable buffer space in the ring buffer. The data read and write of the buffer can be realized by moving the "head pointer" and "tail pointer". In general, the application reading the ring buffer data only affects the "head pointer", and the serial port receives the data only affects the "tail pointer". When a new array is received by the serial port, the array is saved to the ring buffer, and the "tail pointer" is added 1 to save the next data; When the application reads the data, the "head pointer" adds 1 to read the next data. When the tail pointer exceeds the size of the array, the tail pointer re-points to the first element of the array, forming a "ring buffer". , the valid data range is between the head pointer and the tail pointer. As shown in the following figure.
Of course, the "head pointer" and "tail pointer" of the ring buffer can be replaced by "head variable" and "tail variable", because the element space of the switch array, in addition to the use of "pointer offset" method, you can also use "element group subscript offset method." When the serial port receives a new array, the array is saved in the ring buffer, and the "tail variable" is added one to save the next data; When the application reads the data, the "header variable" is added one to read the next data.
The advantage of the "ring buffer" data reception processing mechanism is: the use of the characteristics of the queue, a one-way, one-out, non-impact, in the data into (into the memory), the other side can also read the data, and read out the data, leaving the vacancy, but also can increase the storage space, This avoids the problem of losing data or data when the data is being processed in the same way as data is being received while processing it.
If only one thread reads the data in the ring buffer, only one serial port writes the data to the ring buffer, it is not necessary to add the mutex protection mechanism to guarantee the correctness of the data.
Note that if the serial port receives x bytes of data to be processed once, the buffer array size of the ring buffer must be n times the x, the specific n is how many, need to combine the specific data receive rate and processing rate, appropriate adjustment. This is a good analogy, the kettle is always bigger than the cup, this way the kettle can store a lot of water.
If you feel that the previous text is obscure, then let's discuss the status and implementation of the ring queue. The ring queue built below uses the "header variable" "Tail variable" to control the storage and reading of the queue.
First, we construct a struct and define a struct variable.
#define MAX_SIZE 12//Buffer size typedef struct { unsigned char head; Buffer Head Position unsigned char tail; Buffer trailing position unsigned char ringbuf[max_size]; //buffer array } ringbuffer_t; ringbuffer_t buffer; Define a struct body |
Defining a struct header indicates that a new message queue has been created. Create a new queue, the head pointer and the tail pointer tail are all elements that point to an array of 0. As shown in the following illustration, Message Queuing at this time is an "empty queue".
When there are 11 data Abcdefghijk stored in the buffer queue, the following figure shows:
When L joins a queue, the buffer queue is fully loaded, as shown in the following figure: If the new data is received at this point and needs to be saved, tail needs to be zeroed, the received data is stored in the first element space of the array, and if data for an element space of the buffer array has not been read, the data is overwritten with the newly received data At the same time head needs to be increased by 1, modifying the head node offset position to discard earlier data.
After reading the 11 data in the buffer queue, the status is as follows:
When all the data in the message queue is read out, the ring queue is empty, as shown in the following figure. It can be concluded from the graph that if tail and head are equal, the buffer queue is empty.
1.3 Software Design 1.3.1 ringbuffer.c 1. Constructing ring buffers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23 24 |
/********************************************************************************************** Description: Ring buffer read/write Author: Jahol Fan Version: V1.0 Modify: Finish Date: Notice: This procedure is for study only and cannot be used for any other purpose without the author's permission. All rights reserved, piracy must be traced. ***********************************************************************************************/ #include "Ringbuffer.h"
#define BUFFER_MAX 36//Buffer size
typedef struct { unsigned char headposition; Buffer Head Position unsigned char Tailpositon; Buffer trailing position unsigned char Ringbuf[buffer_max]; Buffer array } ringbuffer_t;
ringbuffer_t buffer; Define a struct body |
First, you need to construct a struct ringbuffer_t, if you define a struct variable buffer, it means creating a ring buffer. 2. saving data to the ring buffer
1 2 3 4 5 6 7 8 9 All One |
/** * @brief Write a byte to ring buffer * @param data: Data to be written * @return none */ Void ringbuf_write (unsigned char data) { buffer.ringbuf[ buffer.tailpositon]=data; //Append if from the tail (++buffer.tailpositon>= Buffer_max) //Tail node offset buffer.tailpositon=0; //is greater than the array maximum length zero forms the ring queue if ( Buffer.tailpositon == buffer.headposition)//If the tail node catches up to the head node, modify the head node offset position to discard the earlier data if (++buffer.headposition>=buffer_max) buffer.headposition=0; } |
8 Rows: Stores the data in the element space pointed to by Tailposition.
9 rows: The tailposition variable is increased by 1, and it is determined that if it is greater than the maximum buffer, the tailposition is zeroed.
11 rows: If the Tailpositon is equal to Headposition, the data is stored at a speed greater than the data fetch speed, from the "rear end". At this time headposition need to increase by 1 to discard the early data, which is the data "coverage phenomenon", this phenomenon is not allowed to exist, the solution to this phenomenon is to put the buffer queue space to open up a bit.
13 Rows: If Headposition is also larger than the maximum array, the headposition needs to be zeroed out. 3. reading data from a ring buffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * @brief read one byte of data in the ring buffer * @param *pdata: Pointer to hold the data read * @return 1 indicates that the buffer is empty and 0 indicates that the read data was successful */ U8 ringbuf_read (unsigned char* pData) { if (buffer.headposition = = Buffer.tailpositon)//If the tail contact indicates that the buffer is empty { return 1; Returns 1, the ring buffer is empty } Else { *pdata=buffer.ringbuf[buffer.headposition]; Takes the head node value and offsets the head node if the buffer is not empty if (++buffer.headposition>=buffer_max) buffer.headposition=0; return 0; Returns 0, indicating that the read data was successful } } |
8 rows: First determine whether headposition equals Tailpositon, if equal, indicates that the buffer is empty at this time.
10 line: The buffer is empty, then return directly, do not execute the subsequent program
12 rows: If the buffer is not empty, then the condition is set and executed
14 rows: Reads the data for the element space of the ring buffer queue that the headposition points to.
15 rows: Headposition self-increment 1 to read the next data. If the headposition is greater than the maximum value of Buffer_max, the headposition is zeroed.
17 rows: Returns 0, indicating that the read data was successful. 1.3.2 hal_uart.c
1 2 3 4 5 6 7 8 9 All + |
/** * @brief Serial 1 interrupt function * @param none * @return none */ Void usart1_irqhandler (void) { if (usart_getitstatus (usart1, usart_it_rxne) != reset) //determine if the receive flag bit is 1 { usart_clearitpendingbit (Usart1,usart_it_rxne); //clear Flag bit ringbuf_write (USART_ Receivedata (USART1)); //blocking wait until transfer complete while (Usart_getflagstatus (USART1, USART_FLAG_TXE) == reset); } } |
11 rows: The data is received successfully, then the data is stored in the ring buffer queue. 1.3.3 main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21st 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/** ************************************************************ * @file MAIN.C * @brief MCU Entry file * @author Jaholfan * @date 2017-11-20 * @version V010100 * @note ***********************************************************/ #include "Hal_led/hal_led.h" #include "Hal_delay/delay.h" #include "Hal_key/hal_key.h" #include "Hal_relay/hal_relay.h" #include "Hal_usart/hal_uart.h" #include "Ringbuffer.h" /** * @brief Program Entry * @param None * @return None */ int main (void) { U8 data = 0; Systeminit (); System clock Initialization Delayinit (72); Tick timer initialization Uartxinit (); while (1) { if (0 = = Ringbuf_read (&data))//read data from the ring buffer { Usart_senddata (Usart1,data); Reads the received data and sends back the data } & |