Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Characteristics and related operations of FreeRTOS queues

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "the characteristics and related operations of the FreeRTOS queue". In the daily operation, I believe that many people have doubts about the characteristics of the FreeRTOS queue and related operation problems. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "the characteristics and related operations of the FreeRTOS queue". Next, please follow the editor to study!

Message queuing support for FreeRTOS

Queue data structure is used in FreeRTOS to realize task asynchronous communication, which has the following characteristics:

Messages support first-in, first-out queuing and asynchronous read-write mode.

Both read and write queues support the timeout mechanism.

Messages support last-in-first-out queuing and send messages to the head of the queue (LIFO).

Any type of message of different length (not exceeding the maximum value of the queue node) can be allowed.

A task can receive and send messages from any message queue.

Multiple tasks can receive and send messages from the same message queue.

When the queue is finished, it can be deleted by deleting the queue function.

Characteristics of FreeRTOS queues

Generally speaking, you can't have both fish and bear's paw, if there is too much data, the speed of data transmission will inevitably slow down, and if you use the method of reference transmission, when the original data is modified, the data will become insecure, but FreeRTOS supports copy and reference to transfer data, which becomes more flexible. Queues pass data through copies, but this does not prevent queues from passing data through references. When the size of the information reaches a critical point, it is impractical to copy the whole information byte by byte. You can define a pointer to the data area and pass the pointer. This method is very commonly used in the Internet of things.

Message queue control block

In fact, message queue is not only used as a message queue, FreeRTOS also uses it as a data structure of semaphores.

Typedef struct QueueDefinition {int8_t * pcHead; / * points to the beginning of the queue store, that is, the first queue item * / int8_t * pcTail; / * points to the next byte * / int8_t * pcWriteTo after the end of the queue store. / * point to the next idle location of the lower queue store * / union / * use the consortium to ensure that two mutually exclusive structure members do not appear at the same time * / {int8_t * pcReadFrom; / * when the structure is used in the queue, this field points to the last one in the dequeuing item. * / UBaseType_t uxRecursiveCallCount;/* when the structure is used for mutexes, it is used as a counter to save the number of times recursive mutexes are "obtained". * /} u; List_t xTasksWaitingToSend; / * the list of tasks blocked due to waiting to join the queue, store the list of tasks blocked by waiting for queue items in priority order, and store * / volatile UBaseType_t uxMessagesWaiting;/* in priority order

< 当前队列的队列项数目 */ UBaseType_t uxLength; /* 队列项的数目 */ UBaseType_t uxItemSize; /* 每个队列项的大小 */ volatile BaseType_t xRxLock; /* 队列上锁后,存储从队列收到的列表项数目,如果队列没有上锁,设置为queueUNLOCKED */ volatile BaseType_t xTxLock; /* 队列上锁后,存储发送到队列的列表项数目,如果队列没有上锁,设置为queueUNLOCKED */ /* 删除部分源码 */} xQUEUE; typedef xQUEUE Queue_t; 先过一遍消息队列的数据结构,其实没啥东西的,记不住也没啥大问题,下面会用到就行了。 创建消息队列 FreeRTOS创建队列API函数是xQueueCreate(),但其实这是一个宏。真正被执行的函数是xQueueGenericCreate(),我们称这个函数为通用队列创建函数。 QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) { Queue_t *pxNewQueue; size_t xQueueSizeInBytes; uint8_t *pucQueueStorage; configASSERT( uxQueueLength >

(UBaseType_t) 0); if (uxItemSize = = (UBaseType_t) 0) {/ * if uxItemSize is 0, that is, the size of a single message space is 0, so there is no need to apply for memory, then xQueueSizeInBytes can also be set to 0, 0 is OK, and this can be set to 0 when used as a semaphore. * / xQueueSizeInBytes = (size_t) 0;} else {/ * allocate sufficient message storage space to queue length * single message size * / xQueueSizeInBytes = (size_t) (uxQueueLength * uxItemSize) / * lint! E961 MISRA exception as the casts are only redundant for some ports. * /} / * FreeRTOS calls the pvPortMalloc () function to request memory space from the system. The memory size is the message queue control block size plus the message storage space size, because this memory space needs to be guaranteed to be continuous * / pxNewQueue = (Queue_t *) pvPortMalloc (sizeof (Queue_t) + xQueueSizeInBytes). If (pxNewQueue! = NULL) {/ * calculate the starting address of the message storage space * / pucQueueStorage = ((uint8_t *) pxNewQueue) + sizeof (Queue_t) # if (configSUPPORT_STATIC_ALLOCATION = = 1) {pxNewQueue- > ucStaticallyAllocated = pdFALSE;} # endif / * configSUPPORT_STATIC_ALLOCATION * / prvInitialiseNewQueue (uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue) } return pxNewQueue;}

The real initialization is in the following function:

BaseType_t xQueueGenericReset (QueueHandle_t xQueue, BaseType_t xNewQueue) {Queue_t * const pxQueue = (Queue_t *) xQueue; configASSERT (pxQueue); taskENTER_CRITICAL (); {/ * related initialization of message queuing data structure * / pxQueue- > pcTail = pxQueue- > pcHead + (pxQueue- > uxLength * pxQueue- > uxItemSize); pxQueue- > uxMessagesWaiting = (UBaseType_t) 0U PxQueue- > pcWriteTo = pxQueue- > pcHead; pxQueue- > u.pcReadFrom = pxQueue- > pcHead + ((pxQueue- > uxLength-(UBaseType_t) 1U) * pxQueue- > uxItemSize); pxQueue- > cRxLock = queueUNLOCKED; pxQueue- > cTxLock = queueUNLOCKED If (xNewQueue = = pdFALSE) {if (listLIST_IS_EMPTY (& (pxQueue- > xTasksWaitingToSend)) = = pdFALSE) {if (xTaskRemoveFromEventList (& (pxQueue- > xTasksWaitingToSend))! = pdFALSE) { QueueYIELD_IF_USING_PREEMPTION () } else {mtCOVERAGE_TEST_MARKER () }} else {mtCOVERAGE_TEST_MARKER () }} else {/ * Ensure the event queues start in the correct state. * / vListInitialise (& (pxQueue- > xTasksWaitingToSend)); vListInitialise (& (pxQueue- > xTasksWaitingToReceive));}} taskEXIT_CRITICAL (); return pdPASS;}

After the initialization is completed, in order to let everyone understand what the message queue looks like, a diagram is given. The yellow part is the control block of the message queue, while the green part is the place where the message queue is stored. At the time of creation, we know the length of the message queue and the size of a single message space.

Message queuing sending

Either the task or the interrupt service program can send a message to the message queue. When sending a message, if the queue is not full or override is allowed, FreeRTOS will copy the message to the end of the message queue, otherwise, it will block according to the blocking timeout specified by the user. During this period, if the queue is not allowed, the task will remain blocked to wait for the queue to be allowed to join the queue. When other tasks read data from the queue they are waiting for (the queue is not full), the task will automatically change from blocking to ready. When the waiting time of a task exceeds the specified blocking time, even if it is not allowed to join the queue, the task will automatically transfer from the blocking state to the ready state, and the task or interrupt program that sends the message will receive an error code errQUEUE_FULL. The process of sending an emergency message is almost the same as sending a message, except that when an emergency message is sent, it is sent at the beginning of the message queue rather than at the end of the queue, so that the recipient can receive the emergency message first. so that the message can be processed in time. The following is the sending API interface of the message queue, and the FromISR in the function indicates that it is used in the interrupt.

1 / *-* / 2 BaseType_t xQueueGenericSend (QueueHandle_t xQueue, (1) 3 const void * const pvItemToQueue (2) 4 TickType_t xTicksToWait, (3) 5 const BaseType_t xCopyPosition) (4) 6 {7 BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired 8 TimeOut_t xTimeOut; 9 Queue_t * const pxQueue = (Queue_t *) xQueue;10 11 / * some assertion operations * / 12 13 for (;;) {14 taskENTER_CRITICAL (); (5) 15 {16 / * queue not full * / 17 if ((pxQueue- > uxMessagesWaiting)

< pxQueue->

UxLength) 18 | | (xCopyPosition = = queueOVERWRITE) {(6) 19 traceQUEUE_SEND (pxQueue); 20 xYieldRequired = 21 prvCopyDataToQueue (pxQueue, pvItemToQueue, xCopyPosition) (7) 22 23 / * deleted partial code using queue set * / 24 / * if a task is waiting to get this message queue * / 25 if (listLIST_IS_EMPTY (& (pxQueue- > xTasksWaitingToReceive)) = = pdFALSE) {(8) 26 / * recover the task from blocking * / 27 If (xTaskRemoveFromEventList (28 & (pxQueue- > xTasksWaitingToReceive))! = pdFALSE) {(9) 29 / * if the priority of the restored task is higher than that of the currently running task 30 then a task switch is required * / 31 queueYIELD_IF_USING_PREEMPTION () (10) 32} else {33 mtCOVERAGE_TEST_MARKER () 34} 35} else if (xYieldRequired! = pdFALSE) {36 / * if there is no waiting task, task switching is required for successful copy * / 37 queueYIELD_IF_USING_PREEMPTION () (11) 38} else {39 mtCOVERAGE_TEST_MARKER (); 40} 41 42 taskEXIT_CRITICAL (); (12) 43 return pdPASS 44} 45 / * queue full * / 46 else {(13) 47 if (xTicksToWait = = (TickType_t) 0) {48 / * if the user does not specify a blocking timeout, exit * / 49 taskEXIT_CRITICAL () (14) 50 traceQUEUE_SEND_FAILED (pxQueue); 51 return errQUEUE_FULL 52} else if (xEntryTimeSet = = pdFALSE) {53 / * initialize the blocking timeout structure variable, initialize the time xTickCount to enter 54 blocking and the number of overflows xNumOfOverflows * / 55 vTaskSetTimeOutState (& xTimeOut); (15) 56 xEntryTimeSet = pdTRUE 57} else {58 mtCOVERAGE_TEST_MARKER (); 59} 60} 61} 62 taskEXIT_CRITICAL (); (16) 63 / * pending scheduler * / 64 vTaskSuspendAll () 65 / * queue locked * / 66 prvLockQueue (pxQueue) 67 68 / * check whether the timeout has passed * / 69 if (xTaskCheckForTimeOut (& xTimeOut, & xTicksToWait) = = pdFALSE) {(17) 70 / * if the queue is still full * / 71 if (prvIsQueueFull (pxQueue)! = pdFALSE) {(18) 72 traceBLOCKING_ON_QUEUE_SEND (pxQueue) 73 / * add the current task to the queue's waiting send list 74 and the blocking delay list, which is the user-specified timeout xTicksToWait * / 75 vTaskPlaceOnEventList (76 & (pxQueue- > xTasksWaitingToSend), xTicksToWait) (19) 77 / * queue unlocking * / 78 prvUnlockQueue (pxQueue); (20) 79 80 / * recovery scheduler * / 81 if (xTaskResumeAll () = = pdFALSE) {82 portYIELD_WITHIN_API () 83} 84} else {85 / * queue has free message space, allowed to join the queue * / 86 prvUnlockQueue (pxQueue); (21) 87 (void) xTaskResumeAll () 88} 89} else {90 / * timeout has expired, exit * / 91 prvUnlockQueue (pxQueue); (22) 92 (void) xTaskResumeAll (); 93 94 traceQUEUE_SEND_FAILED (pxQueue); 95 return errQUEUE_FULL 96} 97} 98} 99 / *

If the blocking time is not 0, the task will enter the block because it is waiting to join the queue. In the process of setting the task to block, the system does not want other tasks and interruptions to operate the queue's xTasksWaitingToReceive list and xTasksWaitingToSend list, because it may cause other tasks to unblock, which may cause priority reversal. For example, task A has a lower priority than the current task, but when the current task enters blocking, task An is unblocked for other reasons, which is obviously absolutely prohibited. So FreeRTOS uses the suspend scheduler to prevent other tasks from operating on the queue, because suspending the scheduler means that the task cannot be switched and is not allowed to call API functions that may cause the task to switch. However, suspending the scheduler does not prohibit interrupts, and the interrupt service function can still manipulate the queue event list, which may unblock tasks and may cause context switching, which is also not allowed. Therefore, the solution is not only to suspend the scheduler, but also to lock the queue to prohibit any interrupts from manipulating the queue. Then borrow the flow chart carefully made by Zhu Gong to understand: the picture is from: https://blog.csdn.net/zhzht19861011/article/details/51510384

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report