1 前言
任務(wù)之間的同步(同步就是任務(wù)之間做數(shù)據(jù)交互,或為兩個任務(wù)之間的通訊),任務(wù)和中斷之間的同步都可以依靠消息隊列,從而實現(xiàn)異步處理,FreeRTOS
的隊列采用FIFO
(先進(jìn)先出)緩沖區(qū),具體如下圖所示;
2 xQUEUE
FreeRTOS
消息隊列的實現(xiàn)主要是queue.c
,需要包含頭文件queue.h
,下面先看一下queue.c
中的數(shù)據(jù)類型xQUEUE
,源碼如下所示;
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;
本文暫時不需要深入源碼,在queue.h
的接口已經(jīng)封裝得相當(dāng)好,無需對細(xì)節(jié)太過于關(guān)注,下面會是對常用接口的使用的總結(jié),如果英文好,直接看源碼中的函數(shù)注釋也是很好的選擇。
3 相關(guān)概念
3.1 數(shù)據(jù)結(jié)構(gòu)
隊列可以保存有限個具有確定長度的數(shù)據(jù)單元。隊列可以保存的最大單元數(shù)目被稱為隊列的“深度”。在隊列創(chuàng)建時需要設(shè)定其深度和每個單元的大小。通常情況下,隊列被作為 FIFO(先進(jìn)先出)使用,即數(shù)據(jù)由隊列尾寫入,從隊列首讀出。當(dāng)然,由隊列首寫入也是可能的。往隊列寫入數(shù)據(jù)是通過字節(jié)拷貝把數(shù)據(jù)復(fù)制存儲到隊列中;從隊列讀出數(shù)據(jù)使得把隊列中的數(shù)據(jù)拷貝刪除。1 如下圖所示;
注意上面提到的數(shù)據(jù)單元可以是一個char
或int
類型的數(shù),但是相對比較合理的設(shè)計是,封裝成一個合理的類,或者稱之為結(jié)構(gòu)體,可以明確當(dāng)前數(shù)據(jù)單元的數(shù)據(jù)類型,數(shù)據(jù)來源(來自哪個任務(wù))等等,因為一個隊列可以被多個任務(wù)進(jìn)行讀取和發(fā)送函數(shù),這樣就避免了傳輸數(shù)據(jù)出現(xiàn)混淆的情況。通常設(shè)計是一個隊列被多個任務(wù)寫入數(shù)據(jù),然后有一個任務(wù)讀取,暫時稱之為多寫一讀,反之,多讀一寫則較少遇到。
3.2 收發(fā)數(shù)據(jù)堵塞
當(dāng)某個任務(wù)試圖讀或者寫一個隊列時,其可以指定一個阻塞超時時間,
-
讀取:任務(wù)讀取數(shù)據(jù)時,在設(shè)置堵塞超時時間內(nèi),如果隊列為空,該任務(wù)將保持阻塞狀態(tài)以等待隊列數(shù)據(jù)有效。當(dāng)其它任務(wù)或中斷服務(wù)例程往其等待的隊列中寫入了數(shù)據(jù),該任務(wù)將自動由阻塞態(tài)轉(zhuǎn)移為就緒態(tài)。
-
寫入:如果隊列被多個任務(wù)寫入,那么將導(dǎo)致多個任務(wù)堵塞以等待隊列有效,當(dāng)隊列有效的時候,這些任務(wù)中的優(yōu)先級最高的任務(wù)優(yōu)先進(jìn)入就緒態(tài)。
4 常用函數(shù)
FreeRTOS的消息隊列常用接口都封裝在queue.h
中,通過宏定義統(tǒng)一將接口函數(shù)的命名風(fēng)格整理得十分統(tǒng)一;具體如下圖所示;
這里有兩種需要注意;
- 任務(wù)與任務(wù)之間同步:例如圖中①處的API適用于任務(wù)間同步;
xQueueSendToFront
xQueueSendToFront
xQueueSend
xQueueOverwrite
- 任務(wù)與中斷之間同步:圖中②處的API適用于任務(wù)于中斷間同步,
xxxISR()
后綴的函數(shù)都是FreeRTOS中保證了線程安全的;xQueueSendToFrontFromISR
xQueueSendToBackFromISR
xQueueOverwriteFromISR
xQueueSendFromISR
4.1 創(chuàng)建隊列
- QueueHandle_t
QueueHandle_t
是一個void
類型的指針變量,定義在queue.h
中,具體如下;
/**
* 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;
基本上每個隊列函數(shù)都會使用這個變量,這里我們統(tǒng)一稱為隊列的句柄;
- xQueueCreate這個函數(shù)可以創(chuàng)建一個隊列,創(chuàng)建成功則會返回一個隊列句柄,如果創(chuàng)建失敗則返回
NULL
,其函數(shù)原型是xQueueGenericCreate
,具體如下所示;
#define xQueueCreate( uxQueueLength, uxItemSize ) \
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
xQueueCreate
如下所示;
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize
);
4.2 發(fā)送數(shù)據(jù)
下面都是從任務(wù)發(fā)送數(shù)據(jù)到隊列,
- xQueueSendToFront將數(shù)據(jù)發(fā)送至隊首,函數(shù)聲明如下;
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);
- xQueueSendToBack將數(shù)據(jù)發(fā)送至隊尾,函數(shù)聲明如下;
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
- xQueueSend與
xQueueSendToBack
入隊順序相同,函數(shù)聲明如下所示;
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
具體的參數(shù)描述如下:
xTicksToWait
設(shè)為0 ,且隊列已滿,則xQueueSendToFront()與xQueueSendToBack()均會立即返回。阻塞時間是以系統(tǒng)心跳周期為單位的,所以絕對時間取決于系統(tǒng)心跳頻率。常量 portTICK_RATE_MS 可以用來把心跳時間單位轉(zhuǎn)換為毫秒時間單位。xTicksToWait
設(shè)置為portMAX_DELAY
, 并且在FreeRTOSConig.h 中設(shè)定INCLUDE_vTaskSuspend
為 1,那么阻塞等待將沒有超時限制。
4.3 接收數(shù)據(jù)
- xQueueReceivexQueueReceive()用于從隊列中接收(讀?。?shù)據(jù)單元。接收到的單元同時會從隊列中刪除。函數(shù)聲明如下;
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait
);
- xQueuePeekxQueuePeek()也是從從隊列中接收數(shù)據(jù)單元,不同的是并不從隊列中刪出接收到的單元。 xQueuePeek()從隊列首接收到數(shù)據(jù)后,不會修改隊列中的數(shù)據(jù),也不會改變數(shù)據(jù)在隊列中的存儲序順。函數(shù)聲明如下;
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
具體的參數(shù)描述如下:
- uxQueueSpacesAvailableuxQueueSpacesAvailable()用于查詢隊列中可用的空閑空間數(shù)量;函數(shù)聲明如下;
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
- uxQueueMessagesWaitinguxQueueMessagesWaiting()用于查詢隊列中當(dāng)前有效數(shù)據(jù)單元個數(shù);函數(shù)聲明如下;
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
4.4 刪除隊列
- vQueueDelete用來刪除一個隊列,直接傳入已創(chuàng)建的隊列句柄即可,函數(shù)聲明如下;
void vQueueDelete( QueueHandle_t xQueue );
5 舉例
多個任務(wù)寫入一個任務(wù)讀取的時候應(yīng)該怎么做呢?如下圖所示2;
這里有三個任務(wù),所以為了搞清楚數(shù)據(jù)來自哪個任務(wù),因此將數(shù)據(jù)單元封裝起來,使用iMeaning
表示數(shù)據(jù)單元的源頭,當(dāng)然這里還是比較簡單的應(yīng)用。
6 總結(jié)
本文介紹了FreeRTOS的消息隊列比價常用的方法,當(dāng)然是相對簡單的,側(cè)重在了解概念上,需要實際的應(yīng)用從而加深理解,更加詳細(xì)已經(jīng)靈活的應(yīng)用可以參考FreeRTOS作者撰寫的Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide。