[FreeRTOS learning 04] Detailed explanation of the use of Queue Management message queue that Xiaobai can understand

Message queues play an essential role as synchronization between tasks;

1 Introduction

Synchronization between tasks (synchronization is data interaction between tasks or communication between two tasks). The synchronization between tasks and interrupts can rely on message queues to achieve asynchronous processing. FreeRTOSThe queue is used FIFO(first advanced Out) buffer, as shown in the figure below;


Insert picture description here

2 xQUEUE

FreeRTOSThe realization of the message queue is mainly that queue.cit needs to include the header file queue.h. Let's take a look at queue.cthe data type xQUEUEin the following. The source code is as follows;

typedef struct QueueDefinition
{
	int8_t *pcHead;					/*< Points to the beginning of the queue storage area. */
	int8_t *pcTail;					/*< Points to the byte at the end of the queue storage area.  Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
	int8_t *pcWriteTo;				/*< Points to the free next place in the storage area. */

	union							/* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
	{
		int8_t *pcReadFrom;			/*< Points to the last place that a queued item was read from when the structure is used as a queue. */
		UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
	} u;

	List_t xTasksWaitingToSend;		/*< List of tasks that are blocked waiting to post onto this queue.  Stored in priority order. */
	List_t xTasksWaitingToReceive;	/*< List of tasks that are blocked waiting to read from this queue.  Stored in priority order. */

	volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */
	UBaseType_t uxLength;			/*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
	UBaseType_t uxItemSize;			/*< The size of each items that the queue will hold. */

	volatile int8_t cRxLock;		/*< Stores the number of items received from the queue (removed from the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */
	volatile int8_t cTxLock;		/*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;	/*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
	#endif

	#if ( configUSE_QUEUE_SETS == 1 )
		struct QueueDefinition *pxQueueSetContainer;
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxQueueNumber;
		uint8_t ucQueueType;
	#endif

} xQUEUE;

This article does not need to go into the source code for the time being. queue.hThe interface is already encapsulated quite well, and there is no need to pay too much attention to details. The following is a summary of the use of commonly used interfaces. If English is good, it is also a good choice to look directly at the function comments in the source code. .

3 Related concepts

3.1 Data structure

The queue can hold a limited number of data units with a certain length . The maximum number of units that a queue can hold is called the " depth " of the queue . When creating a queue, you need to set its depth and the size of each unit. Normally, the queue is used as a FIFO (first-in first-out) , that is, data is written from the end of the queue and read from the first of the queue. Of course, it is also possible to write from the head of the queue. To write data to the queue, copy the data to the queue through byte copying; read the data from the queue to
delete the data copy in the queue. 1 as shown in the figure below;


laizi
Note
that the
data unit mentioned above
can be one charor inttype of number, but the relatively reasonable design is to encapsulate it into a reasonable class, or structure, which can clarify the data of the current data unit Type, data source (from which task), etc., because a queue can be read and sent by multiple tasks, which avoids confusion in the transmission of data. Usually the design is that a queue is written by multiple tasks, and then there is a task to read. It is temporarily called multiple writes and reads . On the contrary, read more and writes are less encountered.

3.2 Send and receive data jam

When a task attempts to read or write a queue, it can specify a blocking timeout period,

  • Read: When the
    task reads data, if the queue is empty during the set congestion timeout period, the task will remain blocked to wait for the queue data to be valid. When other tasks or interrupt service routines
    write data to their waiting queues, the task will automatically transition from the blocked state to the ready state .

  • Write: If the queue is written by multiple tasks, it will cause multiple tasks to block to wait for the queue to be valid. When the queue is valid, the task with the highest priority among these tasks will first enter the ready state.

4 Common functions

The common interfaces of FreeRTOS 'message queue are encapsulated in queue.h, and the naming style of interface functions is unified through macro definitions; the details are shown in the following figure;


Insert picture description here

There are two things to note here;

  • Task to task synchronization : For example , the API at in the figure is suitable for task synchronization;
    • xQueueSendToFront
    • xQueueSendToFront
    • xQueueSend
    • xQueueOverwrite
  • Synchronization between tasks and interrupts : The API at ② in the figure is suitable for synchronization between tasks and interrupts.xxxISR() The functions of the suffix are all FreeRTOS to ensure thread safety;
    • xQueueSendToFrontFromISR
    • xQueueSendToBackFromISR
    • xQueueOverwriteFromISR
    • xQueueSendFromISR

4.1 Create a queue

  • QueueHandle_t
    QueueHandle_t is avoidtype of pointer variable, defined inqueue.h, as follows;
/**
 * Type by which queues are referenced.  For example, a call to xQueueCreate()
 * returns an QueueHandle_t variable that can then be used as a parameter to
 * xQueueSend(), xQueueReceive(), etc.
 */
typedef void * QueueHandle_t;

Basically, every queue function will use this variable, here we are collectively called the handle of the queue;

  • The xQueueCreate
    function can create a queue. If it is created successfully, a queue handle will be returned. If the creation fails, it will be returnedNULL. The function prototype isxQueueGenericCreateas follows:
#define xQueueCreate( uxQueueLength, uxItemSize ) \
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )

xQueueCreateAs follows;

 QueueHandle_t xQueueCreate(
							  UBaseType_t uxQueueLength,
							  UBaseType_t uxItemSize
						  );
parameter description
uxQueueLength The maximum number of units that the queue can store, that is, the queue depth
uxItemSize The length of the data unit in the queue, in bytes
retval NULL: Creation failed; otherwise, creation succeeded

4.2 Sending data

The following are all sending data from the task to the queue,

  • xQueueSendToFront
    sends data to the head of the team, the function declaration is as follows;
 BaseType_t xQueueSendToBack(
								   QueueHandle_t	xQueue,
								   const void		*pvItemToQueue,
								   TickType_t		xTicksToWait
							   );
  • xQueueSendToBack
    sends data to the end of the queue, the function declaration is as follows;
 BaseType_t xQueueSendToFront(
							  QueueHandle_t xQueue,
							  const void * pvItemToQueue,
							  TickType_t xTicksToWait
						 );
  • xQueueSend
    isxQueueSendToBackin the same order as enqueue , the function declaration is as follows;
 BaseType_t xQueueSend(
							  QueueHandle_t xQueue,
							  const void * pvItemToQueue,
							  TickType_t xTicksToWait
						 );

The specific parameters are described as follows:

parameter description
xQueue Handle returned when creating the queue
pvItemToQueue The data that needs to be sent from the task to the queue
xTicksToWait Blocking timeout. If the queue is full at the time of sending, this time is the longest waiting time when the task is blocked and waiting for the queue space to be valid.
retval pdPass : send data successfully
errQUEUE_FULL : unable to write data

onxTicksToWait

  • xTicksToWaitSet to 0 and the queue is full, both xQueueSendToFront () and xQueueSendToBack () will return immediately. The blocking time is based on the system heartbeat period, so the absolute time depends on the system heartbeat frequency. The constant portTICK_RATE_MS can be used to convert heartbeat time units to millisecond time units .
  • xTicksToWaitSet portMAX_DELAY, and the set FreeRTOSConig.h INCLUDE_vTaskSuspendis 1, then there will be no obstruction to wait timeout limit.

4.3 Receive data

  • xQueueReceive
    xQueueReceive () is used to receive (read) the data unit from the queue. The received unit will also be
    deleted from the queue
    . The function declaration is as follows;
 BaseType_t xQueueReceive(
								 QueueHandle_t xQueue,
								 void *pvBuffer,
								 TickType_t xTicksToWait
							);
  • xQueuePeek
    xQueuePeek () is also to receive data units from the queue, the difference is that the received units are not deleted from the queue. After xQueuePeek () receives data from the head of the queue , it will not modify the data in the queue or change the storage order of the data in the queue . The function declaration is as follows;
 BaseType_t xQueuePeek(
							 QueueHandle_t xQueue,
							 void * const pvBuffer,
							 TickType_t xTicksToWait
						 );

The specific parameters are described as follows:

parameter description
xQueue Queue handle
pvBuffer Receive buffer pointer. It points to a section of memory used to receive data copied from the queue
xTicksToWait Blocking timeout
retavl pdPASS : reception success
errQUEUE_FULL : failure to receive
  • uxQueueSpacesAvailable
    uxQueueSpacesAvailable () is used to query the amount of free space available in the queue; the function declaration is as follows;
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
parameter description
xQueue Queue handle
retval The number of free data units in the current queue, 0 means the queue is full
  • uxQueueMessagesWaiting
    uxQueueMessagesWaiting () is used to query the number of current valid data units in the queue; the function declaration is as follows;
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
parameter description
xQueue Queue handle
retval The number of free data units in the current queue, 0 means the queue is empty

4.4 Delete queue

  • vQueueDelete is
    used to delete a queue, just pass the created queue handle directly, the function declaration is as follows;
void vQueueDelete( QueueHandle_t xQueue );

5 Examples

What should I do when multiple tasks are written to one task and read? As shown in the following figure 2 ;
Insert picture description here
There are three tasks here, so in order to figure out which task the data comes from, so the data unit is encapsulated and
iMeaningthe source of the data unit is used. Of course, this is a relatively simple application.

6 Summary

This article introduces the common methods of FreeRTOS's message queue comparison. Of course, it is relatively simple. It focuses on understanding the concept and requires practical applications to deepen the understanding. For more detailed and flexible applications, please refer to Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide written by the author of FreeRTOS .


  1. Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf ↩︎

  2. FREERTOS real-time kernel practical guide, Zou Changjun ↩︎

Published 129 original articles · praised 826 · 190,000 views

Guess you like

Origin blog.csdn.net/u010632165/article/details/105458235