1, Define queue_ Data type of T
#define QUEUESIZE 1000 typedef struct Queue { u8 queue[QUEUESIZE]; int queue_read; int queue_write; enum { no_read = 0, //No read operation was performed reading, //Performing read operation reread, //The read pointer has been overridden } queue_mark; } Queue_t;
Queue is the array storing the circular queue, QUEUESIZE is the size of the array storing the circular queue, and queue_read is the head of the queue_write is the end of the queue. The elements defined by the enumeration type represent the state of the queue.
2, The macro definition determines whether the circular queue is full
//Determine whether the circular queue is full #define is_queue_full(queue) ((((queue)->queue_write + 1) % QUEUESIZE == (queue)->queue_read) ? 1 : 0)
When the pointer at the head of the team moves one step forward, it is equal to the pointer at the end of the team, that is, the team is full.
Supplementary knowledge points:% operator. Modulo operator, remainder after division. For example, 3% 5 results in 2.% 2 The numbers before and after can be reversed.
3, The macro definition determines whether the circular queue is empty
//Judge whether the circular queue is empty #define is_queue_empty(queue) (((queue)->queue_read == (queue)->queue_write) ? 1 : 0)
When the pointer at the head of the queue is equal to the pointer at the end of the queue, it is empty.
4, The macro definition moves the array subscript in a circular queue
//Moves the array subscript in a circular queue #define queue_skip(p, n) do {(p) = ((p) + (n)); (p) = (p) < QUEUESIZE ? (p) : (p) - QUEUESIZE;} while(0)
When the index of pointer P exceeds the length of the array. p - QUEUESIZE is returned, which implements a circular queue. For example, QUEUESIZE = 50, P currently points to 49, n = 2. Then p = 51 - 50 = 1 is returned.
Supplementary knowledge: #define macro definition. The macro definition only uses the macro name to represent a string, and replaces the macro name with the string when the macro is expanded. This is just a simple substitution. Because the preprocessor does not check the macro, if there is an error in the definition and use of the macro, it is difficult to find it before compilation. It can only be found when compiling the source program that has been expanded by the macro. Here, a simple example is given to illustrate the difference between using macro definition to represent data type and typedef to define data specifier in C language. Macro definition is just a simple string substitution, which is completed in preprocessing, while typedef is processed at compile time. It is not a simple substitution, but renames the type specifier. Take the following example:
#define P1 int * typedef (int *) P2;
In terms of form, the two are similar, but they are different in practical use. We can see the difference between P1 and P2 when describing variables:
P1 a,b;Become after macro substitution: int *a,b; //Indicates that a is a pointer variable pointing to an integer and b is an integer variable.
P2 a,b;After macro substitution, it becomes: int *a,*b; //Indicates that a and B are pointer variables pointing to integers.
5, The macro definition calculates the amount of stored data in the circular queue
//Calculates the amount of stored data in the circular queue #define queue_count(queue) (((queue)->queue_write - (queue)->queue_read >= 0) ? ((queue)->queue_write - (queue)->queue_read) : ((queue)->queue_write - (queue)->queue_read + QUEUESIZE))
If the tail pointer minus the head pointer is greater than 0, the amount of data stored in the circular queue is the tail pointer (queue_write) minus the head pointer (queue_read). Otherwise, the tail pointer (queue_write) minus the head pointer (queue_read) plus QUEUESIZE.
6, Function to initialize the circular queue
/** * Cyclic queue initialization * * It mainly initializes the read-write pointer */ static int queue_init(Queue_t *queue) { //queue->queue[QUEUESIZE] = {0}; queue->queue_read = 0; queue->queue_write = 0; queue->queue_mark = no_read; return 0; }
Initializes the head and tail pointers. And the read status of the circular queue is no_read.
7, Function to implement queue operation
//Queue operation /** * Add val to the circular queue G_ In queue * * Return value: 0: success * 1: fail */ static inline int en_queue(Queue_t *queue, uint8_t val) { if(is_queue_full(queue)) { if (queue->queue_mark == reading) //Judge whether there is reading { queue->queue_mark = reread; } queue_skip(queue->queue_read, 1); } queue->queue[queue->queue_write] = val; queue_skip(queue->queue_write, 1); return 0; }
First, judge whether the circular queue is full. If it is full, judge whether there is data reading. If there is data reading, the read pointer will be rewritten.
The head of the team moved forward one step. Save the data to the end of the queue, and the end of the queue pointer moves one step forward.
If the circular queue is not full, the data is directly saved to the end of the queue, and the end of the queue pointer moves one step forward.
8, Function to implement out of queue operation
//Out of queue operation of circular queue static inline int out_queue(Queue_t *queue, uint8_t *pVal) { if(is_queue_empty(queue)) { return 0; } else { *pVal = queue->queue[queue->queue_read]; queue_skip(queue->queue_read, 1); return 1; } }
*pVal is the address where the element will be stored after leaving the queue. If the loop queue is empty, 0 is returned. Otherwise, subscript queue in the array_ The read element is taken out, and then the queue_ The read pointer moves one step forward. Return to 1.
Supplementary knowledge: inline keyword. Functions decorated with the inline keyword will be called differently from ordinary function calls. Ordinary function calls usually go through the process of "saving the instruction location, jumping the program flow to the sub function, executing the sub function, and returning to the previous instruction location". However, for small programs that are often called, such calling process will affect the execution efficiency of the program. The calling process of the inline function will tell the compiler to put the compiled machine code of these small functions where the function is called, which saves the time of function calling. The inline keyword is not mandatory. It just "implies" that the compiler can treat the function as an inline function, but the compiler is likely to ignore it.
9, Function to save the data in the message queue
static inline int queue_get_all(Queue_t *queue, uint8_t *msg, int maxsize) { int len = 0; uint8_t ret = 0; do { ret = out_queue(queue, &msg[len]); len += ret; if (len > maxsize) break; } while(ret != 0); return len; }
*msg is the message queue to be saved. When the out of queue element is equal to the desired data length, it jumps out of the loop and returns the data length stored in the message queue.
10, Function to calculate the checksum of data
/************************************************************************ Function: Total Description: Check Summing Calls: nothing Data Accessed: nothing Data Updated: nothing Input: point: First address of data buffer with calculation length: Number to be calculated Output: nothing Return: Calculated checksum Others: nothing *************************************************************************/ static inline uint8_t queue_total(Queue_t *queue, int start, int count) { uint8_t total = 0; int left = count; int p = start; if (count < 0) return 0; while(left-- > 0) { total += queue->queue[p]; queue_skip(p, 1); } total = (uint8_t)(0x100 - total); return total; }