FreeRTOS应用——消息队列
13. 消息隊列
消息隊列是一種常用于任務鍵通信的數據結構,隊列可以在任務與任務間、中斷與任務間傳遞信息,實現了任務接收來自其他任務或者中斷的不定長數據。
任務能從隊列中讀取信息,當隊列中的消息為空時,讀取消息的任務將被阻塞。用戶還可以指定阻塞的任務時間xTicksToWait,在這段時間中,如果隊列為空,該任務將保持阻塞狀態以等待隊列數據有效,當隊列中有新消息時,被阻塞的任務會被喚醒并處理新消息。當等待的時間超過指定的阻塞時間,即使隊列中尚無有效數據,任務也會自動從阻塞態轉為就緒態。
13.1 消息隊列運作機制
FreeRTOS的消息隊列控制塊由多個元素組成,當消息隊列被創建時系統會為控制塊分配對應的內存空間,用于保存消息隊列的一些信息,如信息的存儲位置、頭指針pcHead、尾指針pcTail、消息大小uxItemSize以及隊列長度uxLength等
任務或者中斷服務程序都可以給消息隊列發送消息,發送消息時如果隊列未滿或者允許覆蓋入隊,FreeRTOS會將消息復制到消息隊列隊尾,否則會根據用戶指定的阻塞超時時間進行阻塞,如果時間超時到了,那么任務會從阻塞態轉換為就緒態,并且此時發送信息的任務或者中斷會收到一個錯誤代碼“errQUEUE_FULL”。如果因為隊列滿了被阻塞,那么在等待的時候如果有別的任務讀取了隊列中的信息,那么被阻塞的任務將會有空間來發送信息,同時解除阻塞態進入就緒態。
任務同樣也可以讀取信息,當隊列中沒有信息的時候進行信息的讀取,那么任務會被阻塞,我們同樣可以指定超時時間。如果在阻塞的時候有任務向隊列中發送了消息,那么阻塞會被解除。
隊列還可以發送緊急消息,發送緊急消息時發送的位置是隊列的隊頭而非隊尾,這樣接受者就能夠優先接收緊急消息,從而及時進行消息處理
當消息隊列不再被使用時,應該將其刪除以釋放系統資源,一旦操作完成,隊列將被永久刪除。
假如有多個任務阻塞在一個消息隊列中,那么這些阻塞的任務將按照任務優先級進行排序,優先級高的任務將優先獲得隊列的訪問權。
只有在任務中發送消息才允許進入阻塞態,而在中斷中發送消息不允許帶有阻塞機制,需要調用在中斷中發送消息的API函數接口。
13.2 消息隊列控制塊
typedef struct QueueDefinition {int8_t *pcHead; /* 指向隊列消息存儲區起始位置,即第一個消息空間 */int8_t *pcTail; /* 指向隊列消息存儲區結束位置地址 */int8_t *pcWriteTo; /* 指向隊列消息存儲區下一個可用消息空間 */union /* pcReadFrom與uxRecursiveCallCount是一對互斥變量,使用聯合體來確保兩個互斥的結構體成員不會同時出現。當結構體用于隊列式,pcReadFrom指向出隊消息空間的最后一個,也就是讀取消息時是從pcReadFrom指向的空間讀取消息內容 */{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; /* 當結構體用于互斥量時,uxRecursiveCallCount用于計數,記錄遞歸互斥量被調用的次數 */} u;List_t xTasksWaitingToSend; /* 發送消息阻塞列表,用于保存阻塞在此隊列的任務,任務按照優先級進行排序。 */List_t xTasksWaitingToReceive; /* 獲取消息阻塞列表,用于保存阻塞在此的隊列任務,任務按照優先級進行排序。 */volatile UBaseType_t uxMessagesWaiting; /* 用于記錄當前消息隊列的消息個數,如果消息隊列用于信號量,這個值表示有效信號量個數 */UBaseType_t uxLength; /* 表示隊列的長度,也就是能存放多少消息 */UBaseType_t uxItemSize; /* 單個消息的大小 */volatile int8_t cRxLock; /* 隊列上鎖后,存儲從隊列收到的列表項數目,也就是出隊的數量;如果隊列沒有上鎖,則設置為queueUNLOCKED */volatile int8_t cTxLock; /* 隊列上鎖后,存儲發送到隊列的列表項數目,也就是入隊的數量;如果隊列沒有上鎖,則設置為queueUNLOCKED*/#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;/* The old xQUEUE name is maintained above then typedefed to the new Queue_t name below to enable the use of older kernel aware debuggers. */ typedef xQUEUE Queue_t;13.3 相關函數
13.3.1 隊列創建與刪除
13.3.1.1 xQueueCreate() 動態創建消息隊列
創建隊列時真正使用的函數是xQueueGenericCreate()
QueueHandle_t xQueueCreate( uxQueueLength, uxItemSize ) 參數: uxQueueLength:隊列中能夠存儲的最大消息單元數目,即隊列長度uxItemSize:隊列中消息單元的大小,以字節為單位 返回值:如果創建成功,則返回一個隊列句柄,用于訪問創建的隊列;如果創建不成功,則返回NULL,可能的原因是創建隊列需要的RAM無法分配成功。注意事項:
13.3.1.2 xQueueCreaetStatic() 靜態創建消息隊列
QueueHandle xQueueCreateStatic( UBaseType_t uxQueueLength, /* 隊列能夠存儲的最大單元數目,即隊列函數 */UBaseType_t uxItemSize, /* 隊列中數據單元的長度,以字節為單位 */uint8_t *pucQueueStorageBuffer, /* 指針,指向一個uint8_t類型的數組,數組的大小至少有uxQueueLength* uxItemSize個字節,當uxItemSize為0時,pucQueueStorageBuffer可以為NULL */StaticQueue_t *pxQueueBuffer /* 指向StaticQueue_t類型的變量,該變量用于存儲隊列的數據結構 */) 返回值:如果創建成功,則返回一個隊列句柄,用于訪問創建的隊列;如果創建不成功,則返回NULL,可能的原因是創建隊列需要的RAM無法分配成功。注意事項:
13.3.1.3 vQueueDelete() 刪除消息隊列
void vQueueDelete(QueueHandle_t xQueue)13.3.2 消息的發送
13.3.2.1 xQueueSend() 發送隊列消息
BaseType_t xQueueSend(QueueHandle_t xQueue, /* 隊列句柄 */const void * pvItemToQueue, /* 指針,指向要發送到的隊列尾部的隊列消息 */TickType_t xTicksToWait /* 隊列滿時,等待隊列空閑的最大超時時間。如果隊列滿并且xTicksToWait被設置成0,函數立刻返回。如果INCLUDE_vTaskSuspend設置成1,并且指定延時為portMAX_DELAY,將一直等待等到空閑 */ )返回值:發送成功:pdTURE發送失敗:errQUEUE_FULL注意事項:
13.3.2.2 xQueueSendFromISR() 中斷中發送消息隊列
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, /* 隊列句柄*/const void *pvItemToQueue, /* 指針,指向要發送到隊列尾部的消息 */BaseType_t *pxHigherPriorityTaskWoken /* 如果入隊導致一個任務解鎖,并且解鎖的任務優先級高于當前被中斷的任務,則將*pxHigherPriorityTaskWoken設置成pdTRUE,然后在中斷退出前進行一次上下文切換,去執行比喚醒任務的優先級更高的任務,從FreeRTOS V7.3.0起,pxHigherPriorityTaskWorken作為一個可選參數,可以設置為NULL */) 返回值: 發送成功:pdTRUE發送失敗:errQUEUE_FULL注意事項:
13.3.2.2.1 示例
void vBufferISR(void) {char cIn;BaseType_t xHigherPriorityTaskWoken;/*在ISR開始時,我們并沒有喚醒任務*/xHigherPriorityTaskWoken = pdFALSE;/* 直到緩沖區為空 */do {/* 從緩沖區獲取一個字節的數據 */cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );/* 發送這個數據 */xQueueSendFromISR(xRxQueue,&cIn,&xHigherPriorityTaskWoken);} while(portINPUT_BYTE(BUFFER_COUNT));/* 這時buffer已經為空,如果需要,則進行上下文切換 */if(xHigherPriorityTaskWoken){/* 上下文切換,這是一個宏,不同的處理器,具體的方法不一樣 */taskYIELD_FROM_ISR();} }13.3.2.3 xQueueSendToFront() 向隊列隊首發送消息
BaseType_t xQueueSendToFront( QueueHandle_t xQueue, /* 隊列句柄 */const void * pvItemToQueue, /* 指針,指向要發送到隊首的消息 */TickType_t xTicksToWait /* 隊列滿時,等待隊列空閑的最大超時時間。如果隊列滿并且xTicksToWait被設置成0,函數立刻返回。如果INCLUDE_vTaskSuspend設置成1,并且指定延時為portMAX_DELAY,將一直等待等到空閑 */ ) 返回值: 發送成功:pdTRUE發送失敗:errQUEUE_FULL13.3.2.4 xQueueSendToFrontFromISR() 中斷中向隊列隊首發送消息
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, /* 隊列句柄*/const void *pvItemToQueue, /* 指針,指向要發送到隊列尾部的消息 */BaseType_t *pxHigherPriorityTaskWoken /* 如果入隊導致一個任務解鎖,并且解鎖的任務優先級高于當前被中斷的任務,則將*pxHigherPriorityTaskWoken設置成pdTRUE,然后在中斷退出前進行一次上下文切換,去執行比喚醒任務的優先級更高的任務,從FreeRTOS V7.3.0起,pxHigherPriorityTaskWorken作為一個可選參數,可以設置為NULL */) 返回值: 發送成功:pdTRUE發送失敗:errQUEUE_FULL13.3.3 消息的接收
13.3.3.1 xQueueReceive() 任務接收消息并刪除消息
BaseType_t xQueueReceive( QueueHandle_t xQueue, /* 隊列句柄 */void *pvBuffer, /* 指針,指向收到的要保存的數據 */TickType_t xTicksToWait ) /* 隊列為空時,阻塞超時的最大時間 */返回值: 接收成功:pdTRUE接收失敗:pdFALSE注意事項:
關于阻塞時間:如果該參數設置為0,函數立即返回,超時時間的單位為系統單位周期,常量portTICK_PERIOD_MS()用于輔助計算真是的時間,單位為ms。如果INCLUDE_vTaskSuspend設置為1,并且指定延時為portMAX_DELAY,將導致任務無限期阻塞
起始xQueueReceive是一個宏定義函數
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )13.3.3.2 xQueuePeek() 任務接收消息但不刪除消息
#define xQueuePeek( xQueue, pvBuffer, xTicksToWait )用法和xQueueReceive是一樣的,只是不會刪除消息。
13.3.3.3 xQueueReceiveFromISR() 中斷接收消息并刪除消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, /* 隊列句柄 */void * const pvBuffer, /* 指針,指向收到的要保存的數據 */BaseType_t * const pxHigherPriorityTaskWoken )/* 如果xQueueReceiveFromISR函數導致一個任務解除阻塞,那么 *pxHigherPriorityTaskWoken將被設置成pdTRUR,否則*pxHigherPriorityTaskWoken的值將不變。從FreeRTOS V7.3.0起,pxHigherPriorityTaskWorken作為一個可選參數,可以設置為NULL */返回值: 接收成功:pdTRUE接收失敗:pdFALSE13.3.3.4 xQueuePeekFromISR() 中斷接收消息但不刪除消息
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, /* 隊列句柄 */void * pvBuffer) /* 指針,指向接收到要保存的數據 */13.4 示例
13.4.1 消息隊列發送消息示例
13.4.1.1 從任務中發送消息
static void Send_Task(void* parameter) {BaseType_t xReturn = pdPASS;uint32_t send_data = 1;while(1){if(判斷條件){xReturn = xQueueSend( Test_Queue, /*消息隊列句柄*/&send_data, /* 發送的消息內容 */0 /* 等待時間0 */)}if(xReturn == pdPASS){printf("消息發送成功");}} }?
13.4.1.2 從中斷中發送消息
void vBufferISR(void){char cIn;BaseType_t xHigherPriorityTaskWoken; //這里是一個重點要注意的xHigherPriorityTaskWoken = pdFALSE;cIn = 獲取的值;/* 發送這個數據 */xQueueSendFromISR(xRxQueue,&cIn,&xHigherPriorityTaskWoken);/* 手動判斷是否需要上下文切換*/if(xHigherPriorityTaskWoken){taskYIELD(); //任務調度是調用這個函數來進行的。}}13.4.2 消息隊列接收消息示例
13.4.2.1 從任務中接收消息
static void Receive_Task(void* parameter) {BaseType_t xReturn = pdTRUE;uint32_t r_queue;while(1){xReturn = xQueueReceive( Test_Queue,&r_queue,portMAX_DELAY);if(xReturn == pdTRUE){printf("成功");}else{printf("出錯");}} }如果不想刪除數據 就將xQueueReceive函數改用xQueuePeek即可
13.4.2.2 從中斷中接收消息
QueueHandle_t xQueue; char cValueToPost = 'a'; const TickType_t xTicksToWait = (TickType_T)0xff; xQueue = xQueueCreate(10,sizeof(char)); xQueueSend(xQueue,(void*)&cValueToPost,xTicksToWait);void vISR_Routine(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;char cRxedChar;xQueueReceiveFromISR(xQueue,(void*) &cRxedChar,&xHigherPriorityTaskWoken)輸出這個值...if(xHigherPriorityTaskWoken!=pdFAALSE){taskYIELD();}}總結
以上是生活随笔為你收集整理的FreeRTOS应用——消息队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: numpy的squeeze函数
- 下一篇: Java中的队列