freertos---队列管理
一、queue在freertos中有什么作用?
Queues’ provide a task-to-task, task-to-interrupt, and interrupt-to-task communication? mechanism.隊(duì)列提供了一種任務(wù)間或者任務(wù)和中斷間的通訊機(jī)制。
二、什么是隊(duì)列?
隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),可以保存固定大小的數(shù)據(jù)。在創(chuàng)建隊(duì)列時(shí),隊(duì)列的長(zhǎng)度和大小就確認(rèn)下來(lái)了。通常情況下隊(duì)列是先進(jìn)先出(First In First Out),即新數(shù)據(jù)被發(fā)送到隊(duì)列的尾部,從頭部取數(shù)據(jù)。不過(guò)數(shù)據(jù)也可以發(fā)送到隊(duì)列頭部,取數(shù)據(jù)也是從頭部。
如下示意圖,一個(gè)隊(duì)列用于TaskA和TaskB之間的通訊,該隊(duì)列能夠保存五個(gè)整數(shù)。TaskA先后向隊(duì)列寫入了“1”、“2”、“3”三個(gè)消息。TaskB依次從quene中取了三條消息。
多個(gè)任務(wù)可以對(duì)同一個(gè)隊(duì)列操作(讀、寫)
運(yùn)行過(guò)程主要有以下兩種情況:
如果放數(shù)據(jù)的速度快于取數(shù)據(jù)的速度,那么會(huì)出現(xiàn)消息隊(duì)列存放滿的情況,FreeRTOS的消息存放函數(shù)xQueueSend支持超時(shí)等待,用戶可以設(shè)置超時(shí)等待,直到有空間可以存放消息或者設(shè)置的超時(shí)時(shí)間溢出。隊(duì)列可以有多個(gè)寫入者,因此一個(gè)隊(duì)列可能會(huì)阻塞多個(gè)任務(wù)以等待完成發(fā)送操作。 在這種情況下,當(dāng)隊(duì)列上的空間可用時(shí),優(yōu)先級(jí)最高的任務(wù)會(huì)被解除阻塞,從而向隊(duì)列寫數(shù)據(jù);如果被阻塞的任務(wù)具有相同的優(yōu)先級(jí),那么等待空間最長(zhǎng)的任務(wù)將被解除阻塞。
如果放數(shù)據(jù)的速度慢于取數(shù)據(jù)的速度,那么會(huì)出現(xiàn)消息隊(duì)列為空的情況,FreeRTOS的消息獲取函數(shù)xQueueReceive支持超時(shí)等待,用戶可以設(shè)置超時(shí)等待,任務(wù)將保持在阻塞狀態(tài)以等待隊(duì)列中可用數(shù)據(jù)或者設(shè)置的超時(shí)時(shí)間溢出。如果多個(gè)任務(wù)都處在阻塞狀態(tài)等待數(shù)據(jù),那么一旦數(shù)據(jù)準(zhǔn)備完畢,優(yōu)先級(jí)最高的任務(wù)可以首先獲得數(shù)據(jù)。如果優(yōu)先級(jí)相同,等待時(shí)間越長(zhǎng)的任務(wù)先獲得數(shù)據(jù)。
三、使用隊(duì)列
freertos中使用一個(gè)結(jié)構(gòu)體來(lái)管理隊(duì)列,下面的所有API函數(shù)都是圍繞這個(gè)結(jié)構(gòu)體展開(kāi),結(jié)構(gòu)體如下:
typedef struct QueueDefinition * QueueHandle_t; typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ {int8_t * pcHead; /*< Points to the beginning of the queue storage area. */int8_t * pcWriteTo; /*< Points to the free next place in the storage area. */union{QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */} 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;| API原型 | 函數(shù)說(shuō)明 | 參數(shù)說(shuō)明 | 返回值 |
| QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ); | 用于創(chuàng)建一個(gè)隊(duì)列,并返回一個(gè)?xQueueHandle?句柄 。當(dāng)創(chuàng)建隊(duì)列時(shí),?FreeRTOS?從堆空間中分配內(nèi)存空間(用于存儲(chǔ)隊(duì)列數(shù)據(jù)結(jié)構(gòu)本身以及隊(duì)列中包含的數(shù)據(jù)單元),并進(jìn)行相關(guān)初始化 ? |
| NULL?表示沒(méi)有足夠的堆空間分配給隊(duì)列而導(dǎo)致創(chuàng)建失敗。 非?NULL?值表示隊(duì)列創(chuàng)建成功。此返回值應(yīng)當(dāng)保存下來(lái),以作為 操作此隊(duì)列的句柄 |
| BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); | xQueueSendToBack()用于將數(shù)據(jù)發(fā)送到隊(duì)列尾。一般都是用的這種方式。中斷中使用xQueueSendToBackFromISR |
| pdPASS:數(shù)據(jù)被成功發(fā)送到隊(duì)列 errQUEUE_FULL |
| BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); | 用于將數(shù)據(jù)發(fā)送到隊(duì)列首,中斷中使用xQueueSendToFrontFromISR | 同上 | 同上 |
| xQueueOverwrite( QueueHandle_t xQueue, const void * pvItemToQueue ) | 該API僅僅在隊(duì)列長(zhǎng)度為1的情況下使用(也就是mailbox)。和xQueueSendToBack不同的是,當(dāng)隊(duì)列滿的時(shí)候,直接覆蓋寫。 |
| 只有pdPASS |
| BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ); | 用于從隊(duì)列頭部接收(讀取)數(shù)據(jù)元素。接收到的元素同時(shí)會(huì)從隊(duì)列中刪除。中斷中使用xQueueReceiveFromISR函數(shù) |
| pdPASS:成功從隊(duì)列中讀取到數(shù)據(jù) errQUEUE_EMPTY |
| BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) | 從隊(duì)列頭部讀取數(shù)據(jù)元素。和xQueueReceive不同的是,讀取之后,不會(huì)將隊(duì)列中的內(nèi)容刪除掉。在中斷中使用 xQueuePeekFromISR() | 同上 | 同上 |
| uxQueueMessagesWaiting | ?用于查詢隊(duì)列中當(dāng)前有效數(shù)據(jù)單元個(gè)數(shù)。中斷中使用uxQueueMessagesWaitingFromISR |
四、使用示例
示例1、
| Task | 發(fā)送/接收 | 指定超時(shí)時(shí)間 | 優(yōu)先級(jí) |
| Sender1 | 循環(huán)發(fā)送100 | 0 | 1 |
| Sender2 | 循環(huán)發(fā)送200 | 0 | 1 |
| Receiver | 接收 | 100ms | 2 |
源碼:
QueueHandle_t xQueue; int main( void ) {xQueue = xQueueCreate( 5, sizeof( int32_t ) );if( xQueue != NULL ){xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );vTaskStartScheduler();}for( ;; ); }static void vSenderTask( void *pvParameters ) {int32_t lValueToSend;BaseType_t xStatus;lValueToSend = ( int32_t ) pvParameters;for( ;; ){xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );if( xStatus != pdPASS ){vPrintString( "Could not send to the queue.\r\n" );}} }static void vReceiverTask( void *pvParameters ) {int32_t lReceivedValue;BaseType_t xStatus;const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );for( ;; ){/* This call should always find the queue empty because this task willimmediately remove any data that is written to the queue. */if( uxQueueMessagesWaiting( xQueue ) != 0 ){vPrintString( "Queue should have been empty!\r\n" );}xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );if( xStatus == pdPASS ){vPrintStringAndNumber( "Received = ", lReceivedValue );}else{vPrintString( "Could not receive from the queue.\r\n" );}} }執(zhí)行結(jié)果:
分析:
- 兩個(gè)發(fā)送任務(wù)的優(yōu)先級(jí)相等,因此,輪流被調(diào)度
- 接受任務(wù)優(yōu)先級(jí)高于發(fā)送隊(duì)列優(yōu)先級(jí),這就導(dǎo)致隊(duì)列中元素個(gè)數(shù)永遠(yuǎn)小于等于1,因?yàn)橐坏┯袛?shù)據(jù)放入隊(duì)列,接受任務(wù)就會(huì)立馬搶占,將數(shù)據(jù)取走
示例2、接受處理多個(gè)源發(fā)送的數(shù)據(jù)
一項(xiàng)任務(wù)從多個(gè)源接收數(shù)據(jù)是很常見(jiàn)的。 接收任務(wù)需要知道數(shù)據(jù)來(lái)自哪里,以確定應(yīng)該如何處理數(shù)據(jù)。 一個(gè)簡(jiǎn)單的設(shè)計(jì)解決方案是使用單個(gè)隊(duì)列來(lái)傳輸結(jié)構(gòu)體,結(jié)構(gòu)體中包含數(shù)據(jù)值和數(shù)據(jù)源信息。?
| Task | 發(fā)送/接收 | 指定超時(shí)時(shí)間 | 優(yōu)先級(jí) |
| Sender1 | 循環(huán)發(fā)送xStructsToSend[0] | 100ms | 2 |
| Sender2 | 循環(huán)發(fā)送xStructsToSend[1] | 100ms | 2 |
| Receiver | 接收 | 0 | 1 |
源碼:
typedef enum {eSender1,eSender2 } DataSource_t;typedef struct {uint8_t ucValue;DataSource_t eDataSource; } Data_t;static const Data_t xStructsToSend[ 2 ] = {{ 100, eSender1 }, /* Used by Sender1. */{ 200, eSender2 } /* Used by Sender2. */ };int main( void ) {xQueue = xQueueCreate( 3, sizeof( Data_t ) );if( xQueue != NULL ){xTaskCreate( vSenderTask, "Sender1", 1000, &( xStructsToSend[ 0 ] ), 2, NULL );xTaskCreate( vSenderTask, "Sender2", 1000, &( xStructsToSend[ 1 ] ), 2, NULL );xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );vTaskStartScheduler();}for( ;; ); }static void vSenderTask( void *pvParameters ) {BaseType_t xStatus;const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );for( ;; ){xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );if( xStatus != pdPASS ){vPrintString( "Could not send to the queue.\r\n" );}} }static void vReceiverTask( void *pvParameters ) {Data_t xReceivedStructure;BaseType_t xStatus;for( ;; ){if( uxQueueMessagesWaiting( xQueue ) != 3 ){vPrintString( "Queue should have been full!\r\n" );}xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 );if( xStatus == pdPASS ){if( xReceivedStructure.eDataSource == eSender1 ){vPrintStringAndNumber( "From Sender 1 = ", xReceivedStructure.ucValue );}else{vPrintStringAndNumber( "From Sender 2 = ", xReceivedStructure.ucValue );}}else{vPrintString( "Could not receive from the queue.\r\n" );}} }執(zhí)行結(jié)果:
分析:
- t1:sender1 開(kāi)始發(fā)送數(shù)據(jù)到queue
- t2:由于sender1發(fā)送了三次數(shù)據(jù)導(dǎo)致queue滿,從而sender1進(jìn)行Blocked state;此時(shí)sender2具有最高優(yōu)先級(jí),sender2進(jìn)入Running state
- t3:sender2發(fā)現(xiàn)queue滿,也進(jìn)入Blocked state。此時(shí),receiver進(jìn)入Running state
- t4:receiver從隊(duì)列中取出一個(gè)元素后,該任務(wù)立馬sender1被搶占(sender1和sender2優(yōu)先級(jí)相等,sender1等待的時(shí)間比sender2長(zhǎng))。
- t5:sender1向隊(duì)列中寫入一個(gè)元素后,隊(duì)列滿,又進(jìn)入阻塞狀態(tài)。此時(shí)sender2也處于阻塞狀態(tài),因此不會(huì)被調(diào)度器調(diào)度。從而receiver進(jìn)入Running state,從隊(duì)列中取出一個(gè)元素后,立馬被sender2搶占(sender2等待的時(shí)間比sender1長(zhǎng))。
- t6:重復(fù)以上過(guò)程
和示例1不同的是,示例2的隊(duì)列幾乎一直是滿的!
?示例3、如何使用隊(duì)列傳輸大數(shù)據(jù)?
?隊(duì)列中保存的數(shù)據(jù)很大,更好的辦法是不直接傳輸數(shù)據(jù)本身,傳輸數(shù)據(jù)的指針。在使用的時(shí)候需要注意兩點(diǎn):
- 確保發(fā)送任務(wù)和接受任務(wù)不會(huì)同時(shí)修改指針指向的內(nèi)存
- 如果指針指向的內(nèi)存是動(dòng)態(tài)分配的,則要確保使用這塊內(nèi)存之前沒(méi)有被釋放
?示例4、如何使用隊(duì)列傳輸數(shù)據(jù)類型和數(shù)據(jù)大小不確定的數(shù)據(jù)?
將示例2和示例3的方法結(jié)合起來(lái),就可以達(dá)到傳輸數(shù)據(jù)類型與大小不確定的數(shù)據(jù)!
如freertos中TCP/IP stack的源碼所示:
typedef enum {eNetworkDownEvent = 0, eNetworkRxEvent, eTCPAcceptEvent, } eIPEvent_t;typedef struct IP_TASK_COMMANDS {eIPEvent_t eEventType;/* A generic pointer that can hold a value, or point to a buffer. */void *pvData; } IPStackEvent_t;五、源碼解析
1、創(chuàng)建隊(duì)列
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) #endif/*@param uxQueueLength:隊(duì)列長(zhǎng)度,也就是隊(duì)列最多能存儲(chǔ)多少個(gè)元素@param uxItemSize:元素大小,隊(duì)列中每個(gè)元素占多少byte@param ucQueueType :隊(duì)列類型,內(nèi)部使用,不必太多關(guān)心#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U )#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U )#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U )#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U )#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U )@return QueueHandle_t 類型的結(jié)構(gòu)體,也就是隊(duì)列管理的句柄 */ QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType ) {Queue_t * pxNewQueue;size_t xQueueSizeInBytes;uint8_t * pucQueueStorage;/*分配隊(duì)列結(jié)構(gòu)體和隊(duì)列元素存儲(chǔ)空間*/xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); if( pxNewQueue != NULL )//分配空間成功{pucQueueStorage = ( uint8_t * ) pxNewQueue;pucQueueStorage += sizeof( Queue_t ); //定位到存儲(chǔ)隊(duì)列元素內(nèi)容的區(qū)域#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){pxNewQueue->ucStaticallyAllocated = pdFALSE;}#endif /* configSUPPORT_STATIC_ALLOCATION */prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );}else{traceQUEUE_CREATE_FAILED( ucQueueType );mtCOVERAGE_TEST_MARKER();}return pxNewQueue; }待補(bǔ)充。。。。。。
ref:
FreeRTOS高級(jí)篇5---FreeRTOS隊(duì)列分析_朱工的專欄-CSDN博客
FreeRTOS--消息隊(duì)列 - M&D - 博客園
freeRTOS中文實(shí)用教程2--隊(duì)列 - jasonactions - 博客園
總結(jié)
以上是生活随笔為你收集整理的freertos---队列管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: eclipse hadoop1.2.0配
- 下一篇: Lua ipairs与pairs的区别