日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

freertos---队列管理

發(fā)布時(shí)間:2023/12/14 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 freertos---队列管理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、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)初始化
?
  • uxQueueLength:隊(duì)列能夠存儲(chǔ)的最大單元數(shù)目,即隊(duì)列深度。
  • uxItemSize:隊(duì)列中數(shù)據(jù)單元的長(zhǎng)度,以字節(jié)為單位
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
  • xQueue:目標(biāo)隊(duì)列的句柄
  • pvItemToQueue:發(fā)送數(shù)據(jù)的指針
  • xTicksToWait:阻塞超時(shí)時(shí)間,設(shè)置為0表示不等待

pdPASS:數(shù)據(jù)被成功發(fā)送到隊(duì)列

errQUEUE_FULL
:如 果 由 于 隊(duì) 列 已 滿 而 無(wú) 法 將 數(shù) 據(jù) 寫 入(超時(shí)時(shí)間過(guò)了依舊不能寫入)

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í)候,直接覆蓋寫
  • xQueue:目標(biāo)隊(duì)列的句柄
  • pvItemToQueue:發(fā)送數(shù)據(jù)的指針
只有pdPASS
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );

用于從隊(duì)列頭部接收(讀取)數(shù)據(jù)元素。接收到的元素同時(shí)會(huì)從隊(duì)列中刪除。中斷中使用xQueueReceiveFromISR函數(shù)

  • xQueue:目標(biāo)隊(duì)列的句柄
  • pvBuffer:接受數(shù)據(jù)的指針
  • xTicksToWait:阻塞超時(shí)時(shí)間,設(shè)置為0表示不等待,立即返回

pdPASS:成功從隊(duì)列中讀取到數(shù)據(jù)

errQUEUE_EMPTY
:由 于 隊(duì) 列 為空 導(dǎo)致讀取數(shù)據(jù)失敗

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ā)送10001
Sender2 循環(huán)發(fā)送20001
Receiver 接收100ms2

源碼:

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]100ms2
Sender2 循環(huán)發(fā)送xStructsToSend[1]100ms2
Receiver 接收01

源碼:

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)有被釋放
QueueHandle_t xPointerQueue; xPointerQueue = xQueueCreate( 5, sizeof( char * ) );void vStringSendingTask( void *pvParameters ) {char *pcStringToSend;const size_t xMaxStringLength = 50;BaseType_t xStringNumber = 0;for( ;; ){//申請(qǐng)存儲(chǔ)字符串的內(nèi)存空間,可能是動(dòng)態(tài)申請(qǐng),也可能是靜態(tài)申請(qǐng)pcStringToSend = ( char * ) prvGetBuffer( xMaxStringLength );/* 向申請(qǐng)到的空間中寫數(shù)據(jù) */snprintf( pcStringToSend, xMaxStringLength, "String number %d\r\n", xStringNumber );xStringNumber++;/* 向隊(duì)列中寫地址 */ xQueueSend( xPointerQueue, &pcStringToSend, portMAX_DELAY );} }void vStringReceivingTask( void *pvParameters ) {char *pcReceivedString;for( ;; ){/* Receive the address of a buffer. */xQueueReceive( xPointerQueue, &pcReceivedString, portMAX_DELAY );/* The buffer holds a string, print it out. */vPrintString( pcReceivedString );/* The buffer is not required any more - release it so it can be freed, or re-used. */prvReleaseBuffer( pcReceivedString );} }

?示例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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。