Basically Queues are the same as circular buffers but this time with a small modification Queues shall be able to handle any type of data from 8-bit variables to any kind of structure. The use of void pointers and the memcpy function from the standard library can help us create queues that can handle any type of element. In this way, we can create waiting queues that store any type of element with a single algorithm. The basic principle behind it is very simple, each element of the X type is made up of N bytes so we will only have to copy the N bytes that make up each element when writing or reading in the queue.

The queues or circular buffers make a copy of the elements to be written and always make a copy of the elements read, that is why it is necessary to pass them the address of where the information to be written is or where to place the information to be read. The queue will store these copies in its own delimited memory space which can be calculated by the size in bytes of each element multiplied by the number of elements we wish to handle, typically we will declare an array to serve as the memory space.

  • Create a driver that configures and controls a queue of n elements of type undefined, the driver will be written to the files Queue.h and Queue.c
  • It must have an interface that initializes the queue
void Queue_Init( QueueType *Queue, void *Buffer, uint32_t Elements, uint8_t Size );

Initializes the queue by setting the read and write pointers to the beginning of the queue, the queue is empty. so the Full and Empty flags are set accordingly to FALSE and TRUE.

  • It should have an interface that allows writing an element to the queue.
uint8_t Queue_WriteData( QueueType *Queue, void *Data );

Copies the information referenced by the empty pointer data to the buffer controlled by queue, the number of bytes to copy is indicated by the Size element of the AppQue_Queue type structure, if the write is successful the function returns a TRUE, and if not a FALSE. This function should determine when the queue is full by the current write operation and prevent the writing of a new element when it is.

  • It should have an interface that allows reading an element from the queue.
uint8_t Queue_ReadData( QueueType *Queue, void *Data );

Reads data from the buffer controlled by queue, the data is copied into the data type referenced by the empty pointer data, the number of bytes to copy is indicated by the Size element of the AppQue_Queue type structure, if the read is successful the function returns a TRUE and otherwise a FALSE. This function should determine when the queue is emptied by the current read operation and prevent the reading of a new element when it is.

  • It should have an interface that indicates if the queue is empty.
uint8_t Queue_isQueueEmpty( QueueType *Queue);

The function returns a one if there are no more elements that can be read from the queue and zero if there is at least one element that can be read.

  • It must have an interface that empties the queue.
void Queue_FlushQueue( QueueType *Queue);

The function must empty the queue in case it has elements inside it, the information will be discarded

  • The queue control structure must have the following elements:
typedef struct _QueueType 
{
    void        *Buffer;  //pointer to array that store buffer data
    uint32_t    Elements; //number of elements to store (the queue lenght) 
    uint8_t     Size;     //size of the elements to store
    uint8_t     Head;     //variable to signal the next queue space to write 
    uint8_t     Tail;     //variable to signal the next queue space to read
    uint8_t     Empty;    //flag to indicate if the queue is empty
    uint8_t     Full;     //flag to indicate if the queue is full
} QueueType;

This is a very basic example of how to use the queue with elements of structure type

//Declare the message to be handle by the queue
typedef struct
{
    uint8_t msg;
    uint8_t value; 
} MsgType_Message;

int main( void )
{
    MsgType_Message MsgToWrite;
    MsgType_Message MsgToRead;
    
    MsgType_Message buffer[ 10u ]; // array of then elements to use as queue memory space
    QueueType Queue;    // queue control structure 

    Queue_Init( &Queue, buffer, 10, sizeof( MsgType_Message ) );     // Init queue
    
    //write a message
    MsgToWrite.msg = NEW;
    MsgToWrite.value = 100u;
    Queue_WriteData( &Queue, &MsgToWrite );
    
    //read all the messages 
    while( Queue_isQueueEmpty( &Queue ) == 0u )
    {
        Queue_ReadData( &Queue, &MsgToRead );
        printf( "msg read from the queue %d\n", MsgToRead.msg );
        printf( "value read from the queue %d\n", MsgToRead.value );
    }
}