在FreeRTOS中,隊(duì)列是實(shí)現(xiàn)任務(wù)之間同步、互斥和通信的一種重要方法(其他的實(shí)現(xiàn)方法有:任務(wù)通知、事件組、信號(hào)量、互斥量)。
任何任務(wù)都可以向隊(duì)列里存放任何數(shù)據(jù),任何任務(wù)也可以從隊(duì)列里讀取數(shù)據(jù),實(shí)現(xiàn)不同任務(wù)之間的通信。
1
隊(duì)列特性
隊(duì)列的數(shù)據(jù)的操作采用先進(jìn)先出的方法(FIFO,F(xiàn)irst In First Out):寫(xiě)數(shù)據(jù)時(shí)放到尾部,讀數(shù)據(jù)時(shí)從頭部讀,邏輯順序如下圖所示。
使用隊(duì)列傳輸數(shù)據(jù)時(shí)有兩種方法:
- 拷貝:把數(shù)據(jù)、把變量的值復(fù)制進(jìn)隊(duì)列里
- 引用:把數(shù)據(jù)、把變量的地址復(fù)制進(jìn)隊(duì)列里
FreeRTOS中的隊(duì)列一般都使用拷貝的方式傳輸數(shù)據(jù),局部變量的值可以發(fā)送到隊(duì)列中,后續(xù)即使函數(shù)退出、局部變量被回收,也不會(huì)影響隊(duì)列中的數(shù)據(jù),發(fā)送任務(wù)、接收任務(wù)解耦時(shí),接收任務(wù)不需要知道這數(shù)據(jù)是誰(shuí)的、也不需要發(fā)送任務(wù)來(lái)釋放數(shù)據(jù)。
如果數(shù)據(jù)實(shí)在太大,還是可以使用隊(duì)列傳輸它的地址。
2
隊(duì)列函數(shù)
1.創(chuàng)建
隊(duì)列的創(chuàng)建有兩種方法:動(dòng)態(tài)分配內(nèi)存、靜態(tài)分配內(nèi)存。
一般都用動(dòng)態(tài)分配內(nèi)存的方法,使用函數(shù):xQueueCreate()
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
參數(shù)解釋:
- uxQueueLength :隊(duì)列長(zhǎng)度
- uxItemSize:每個(gè)數(shù)據(jù)的大小,以字節(jié)為單位
- 返回值:非0:成功,返回句柄,以后使用句柄來(lái)操作隊(duì)列;NULL:失敗,因?yàn)閮?nèi)存不足
2.刪除
刪除隊(duì)列的函數(shù)為 vQueueDelete() ,只能刪除使用動(dòng)態(tài)方法創(chuàng)建的隊(duì)列,它會(huì)釋放內(nèi)存。
void vQueueDelete( QueueHandle_t xQueue );
參數(shù)解釋:
- xQueue:隊(duì)列句柄
3.寫(xiě)隊(duì)列
可以把數(shù)據(jù)寫(xiě)到隊(duì)列頭部,也可以寫(xiě)到尾部,這些函數(shù)有兩個(gè)版本:在任務(wù)中使用、在 ISR 中使用。
在任務(wù)中使用:
BaseType_t xQueueSend( QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait );
在ISR中使用:
BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken );
參數(shù)解釋:
- xQueue :隊(duì)列句柄,要寫(xiě)哪個(gè)隊(duì)列
- pvItemToQueue : 數(shù)據(jù)指針,這個(gè)數(shù)據(jù)的值會(huì)被復(fù)制進(jìn)隊(duì)列
- xTicksToWait :如果隊(duì)列滿則無(wú)法寫(xiě)入新數(shù)據(jù),可以讓任務(wù)進(jìn)入阻塞狀態(tài),xTicksToWait表示阻塞的最大時(shí)間(Tick Count)。如果被設(shè)為0,無(wú)法寫(xiě)入數(shù)據(jù)時(shí)函數(shù)會(huì)立刻返回;如果被設(shè)為portMAX_DELAY,則會(huì)一直阻塞直到有空間可寫(xiě)
- 返回值:pdPASS:數(shù)據(jù)成功寫(xiě)入了隊(duì)列;errQUEUE_FULL:寫(xiě)入失敗,因?yàn)殛?duì)列滿了。
4.讀隊(duì)列
使用 xQueueReceive() 函數(shù)讀隊(duì)列,讀到一個(gè)數(shù)據(jù)后,隊(duì)列中該數(shù)據(jù)會(huì)被移除。這個(gè)函數(shù)有兩個(gè)版 本:在任務(wù)中使用、在ISR 中使用。
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken );
參數(shù)解釋:
- xQueue :隊(duì)列句柄,要寫(xiě)哪個(gè)隊(duì)列
- pvBuffffer: bufer 指針,隊(duì)列的數(shù)據(jù)會(huì)被復(fù)制到這個(gè) buffer
- xTicksToWait :如果隊(duì)列空則無(wú)法讀出數(shù)據(jù),可以讓任務(wù)進(jìn)入阻塞狀態(tài),xTicksToWait表示阻塞的最大時(shí)間(Tick Count)。如果被設(shè)為0,無(wú)法讀出數(shù)據(jù)時(shí)函數(shù)會(huì)立刻返回;如果被設(shè)為portMAX_DELAY,則會(huì)一直阻塞直到有數(shù)據(jù)可寫(xiě)
- 返回值:pdPASS:從隊(duì)列讀出數(shù)據(jù)入;errQUEUE_EMPTY:讀取失敗,因?yàn)殛?duì)列空了。
5.其他
復(fù)位:隊(duì)列剛被創(chuàng)建時(shí),里面沒(méi)有數(shù)據(jù);使用過(guò)程中可以調(diào)用 xQueueReset() 把隊(duì)列恢復(fù)為初始狀態(tài)。
/*
pxQueue : 復(fù)位哪個(gè)隊(duì)列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);
查詢:可以查詢隊(duì)列中有多少個(gè)數(shù)據(jù)、有多少空余空間。
/** 返回隊(duì)列中可用數(shù)據(jù)的個(gè)數(shù) */
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/** 返回隊(duì)列中可用空間的個(gè)數(shù) */
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
覆蓋:當(dāng)隊(duì)列長(zhǎng)度為 1 時(shí),可以使用 xQueueOverwrite() 或 xQueueOverwriteFromISR() 來(lái)覆蓋數(shù)據(jù)。注意,隊(duì)列長(zhǎng)度必須為1。當(dāng)隊(duì)列滿時(shí),這些函數(shù)會(huì)覆蓋里面的數(shù)據(jù),這也以為著這些函數(shù)不會(huì)被阻塞。
/* 覆蓋隊(duì)列
* xQueue: 寫(xiě)哪個(gè)隊(duì)列
* pvItemToQueue: 數(shù)據(jù)地址
* 返回值: pdTRUE表示成功, pdFALSE表示失敗
*/
BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void * pvItemToQueue );
BaseType_t xQueueOverwriteFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
偷看:如果想讓隊(duì)列中的數(shù)據(jù)供多方讀取,也就是說(shuō)讀取時(shí)不要移除數(shù)據(jù),要留給后來(lái)人。那么可以使用" 窺 視" ,也就是 xQueuePeek() 或 xQueuePeekFromISR() 。這些函數(shù)會(huì)從隊(duì)列中復(fù)制出數(shù)據(jù),但是不移除數(shù)據(jù)。這也意味著,如果隊(duì)列中沒(méi)有數(shù)據(jù),那么" 偷看 " 時(shí)會(huì)導(dǎo)致阻塞;一旦隊(duì)列中有數(shù)據(jù),以后每次 " 偷看" 都會(huì)成功。
/* 偷看隊(duì)列
* xQueue: 偷看哪個(gè)隊(duì)列
* pvItemToQueue: 數(shù)據(jù)地址, 用來(lái)保存復(fù)制出來(lái)的數(shù)據(jù)
* xTicksToWait: 沒(méi)有數(shù)據(jù)的話阻塞一會(huì)
* 返回值: pdTRUE表示成功, pdFALSE表示失敗
*/
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void *pvBuffer, );
3
隊(duì)列實(shí)驗(yàn)
代碼:
/* vSenderTask被用來(lái)創(chuàng)建2個(gè)任務(wù),用于寫(xiě)隊(duì)列
* vReceiverTask被用來(lái)創(chuàng)建1個(gè)任務(wù),用于讀隊(duì)列
*/
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* 隊(duì)列句柄, 創(chuàng)建隊(duì)列時(shí)會(huì)設(shè)置這個(gè)變量 */
QueueHandle_t xQueue;
int main( void )
{
prvSetupHardware();
/* 創(chuàng)建隊(duì)列: 長(zhǎng)度為5,數(shù)據(jù)大小為4字節(jié)(存放一個(gè)整數(shù)) */
xQueue = xQueueCreate( 5, sizeof( int32_t ) );
if( xQueue != NULL )
{
/* 創(chuàng)建2個(gè)任務(wù)用于寫(xiě)隊(duì)列, 傳入的參數(shù)分別是100、200
* 任務(wù)函數(shù)會(huì)連續(xù)執(zhí)行,向隊(duì)列發(fā)送數(shù)值100、200
* 優(yōu)先級(jí)為1
*/
xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );
/* 創(chuàng)建1個(gè)任務(wù)用于讀隊(duì)列
* 優(yōu)先級(jí)為2, 高于上面的兩個(gè)任務(wù)
* 這意味著隊(duì)列一有數(shù)據(jù)就會(huì)被讀走
*/
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );
/* 啟動(dòng)調(diào)度器 */
vTaskStartScheduler();
}
else
{
/* 無(wú)法創(chuàng)建隊(duì)列 */
}
/* 如果程序運(yùn)行到了這里就表示出錯(cuò)了, 一般是內(nèi)存不足 */
return 0;
}
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
static void vSenderTask( void *pvParameters )
{
int32_t lValueToSend;
BaseType_t xStatus;
/* 我們會(huì)使用這個(gè)函數(shù)創(chuàng)建2個(gè)任務(wù)
* 這些任務(wù)的pvParameters不一樣
*/
lValueToSend = ( int32_t ) pvParameters;
/* 無(wú)限循環(huán) */
for( ;; )
{
/* 寫(xiě)隊(duì)列
* xQueue: 寫(xiě)哪個(gè)隊(duì)列
* &lValueToSend: 寫(xiě)什么數(shù)據(jù)? 傳入數(shù)據(jù)的地址, 會(huì)從這個(gè)地址把數(shù)據(jù)復(fù)制進(jìn)隊(duì)列
* 0: 不阻塞, 如果隊(duì)列滿的話, 寫(xiě)入失敗, 立刻返回
*/
xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );
if( xStatus != pdPASS )
{
printf( "Could not send to the queue.rn" );
}
}
}
/*-----------------------------------------------------------*/
static void vReceiverTask( void *pvParameters )
{
/* 讀取隊(duì)列時(shí), 用這個(gè)變量來(lái)存放數(shù)據(jù) */
int32_t lReceivedValue;
BaseType_t xStatus;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
/* 無(wú)限循環(huán) */
for( ;; )
{
/* 讀隊(duì)列
* xQueue: 讀哪個(gè)隊(duì)列
* &lReceivedValue: 讀到的數(shù)據(jù)復(fù)制到這個(gè)地址
* xTicksToWait: 如果隊(duì)列為空, 阻塞一會(huì)
*/
xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );
if( xStatus == pdPASS )
{
/* 讀到了數(shù)據(jù) */
printf( "Received = %drn", lReceivedValue );
}
else
{
/* 沒(méi)讀到數(shù)據(jù) */
printf( "Could not receive from the queue.rn" );
}
}
}
在這個(gè)程序中,有一個(gè)接收隊(duì)列數(shù)據(jù)的任務(wù),兩個(gè)發(fā)送隊(duì)列數(shù)據(jù)的任務(wù),接收隊(duì)列數(shù)據(jù)的任務(wù)優(yōu)先級(jí)高,先執(zhí)行,但是這時(shí)隊(duì)列為空,觸發(fā)該任務(wù)阻塞,這時(shí)低優(yōu)先級(jí)的任務(wù)交替執(zhí)行,向隊(duì)列中發(fā)送數(shù)據(jù),接收任務(wù)發(fā)現(xiàn)隊(duì)列不為空后(解除觸發(fā)的事件),立刻被喚醒從隊(duì)列中讀取數(shù)據(jù)并打印出來(lái),實(shí)驗(yàn)結(jié)果和邏輯圖如下:
-
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62410 -
STM32F103
+關(guān)注
關(guān)注
33文章
479瀏覽量
63895 -
ISR
+關(guān)注
關(guān)注
0文章
38瀏覽量
14471 -
FIFO存儲(chǔ)
+關(guān)注
關(guān)注
0文章
103瀏覽量
6038 -
調(diào)度器
+關(guān)注
關(guān)注
0文章
98瀏覽量
5299
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論