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

評論