FreeRTOS 隊列
Queue 簡介
數據存儲
FreeRTOS的Queue是個FIFO先入先出的緩衝區。隊列長度在隊列創建時被指定。
上圖展示了隊列的使用方法。
在FreeRTOS的Queue實現中,採用的是複製而不是引用。這樣的好處是:
- 數據可以直接發送到隊列中保存,而不用擔心原數據會被覆蓋或修改
- 不用事先開闢緩衝區存儲,直接複製到隊列即可
- 發送和接收是獨立的
- 也可以直接將指針作爲元素髮送,可以看做是引用
- FreeRTOS負責給數據分配存儲空間
- 某些內存保護系統中,RAM的訪問是被限制的。發送和接收任務不一定都可以訪問RAM
多任務訪問
隊列可以被任何任務或者中斷服務函數訪問。任一任務都可以寫入隊列和讀取隊列。
讀取等待
當任務企圖讀取隊列時,可以指定一個阻塞時間等待數據。在這段時間中,任務會被阻塞。當隊列中有新數據或者超時,被阻塞的任務會自動進入就緒態。一個隊列會有很多受衆,但是在這種情況下,只有一個任務會解除阻塞態,這個任務是所有等待任務中優先級最高的那個。如果任務優先級相同,則選取已經等待時間較長的那個任務解除阻塞。
寫入等待
當隊列已滿,可以指定一個阻塞時間等待寫入。同樣,有可能存在多個任務等待寫入隊列。這時,只有一個任務可以解除阻塞。這個任務通常是優先級最高的任務,如果優先極相同,則選取等待時間最長的任務解除阻塞。
多隊列阻塞
隊列可以組成集合,一個任務可以等待一個隊列集中所有的隊列數據同時就緒。
APl
xQueueCreate() 創建隊列
隊列可以用句柄表示,數據類型是QueueHandle_t
在FreeRTOS V9.0 中,xQueueCreateStatic()可以用來創建一個靜態隊列,他的大小在編譯就被指定。
xQueueSendToBack() and xQueueSendToFront() 插入元素
這兩個API指定數據位置,插入隊首還是隊尾
xQueueSend()等同於xQueueSendToBack()
注意,這兩個函數不能在中斷中調用!!,如需中斷中調用,須使用xQueueSendToFrontFromISR() 和 xQueueSendToBackFromISR()
xQueueReceive() 接收數據
注意,不能在中斷中使用! 中斷中須使用,xQueueReceiveFromISR()
uxQueueMessagesWaiting() 查詢隊列元素數
不能在中斷使用!!,替代:uxQueueMessagesWaitingFromISR()
示例
圖解
- Receiver優先級最高首先搶佔CPU,由於隊列爲空,Receiver隨即進入阻塞態
- Sender2發送數據到隊列
- Receiver檢測到隊列中有數據,開始讀數。隊列隨即變空,Receiver又進入阻塞態
- Sender1發送數據到隊列
- Receiver檢測到隊列中有數據,開始讀數。隊列隨即變空,Receiver又進入阻塞態
- …
從多個數據源接收數據 Receiving Data From Multiple Sources
由於一個隊列可能接收來源不同的數據,如果我們想要知道每個數據的身份信息,一個較爲簡單的方式就是將結構體作爲隊列的元素,如下圖所示
- eDataID代表數據身份信息
- IDataValue則是數據值
示例
定義數據元素結構體:
創建發送任務:
創建接收任務:
只有隊列滿時,纔會處理
圖解
- t2時刻隊列已滿,此時Receiver從隊列中取出一個數據
- t4時刻隊列出現一個空位,由於Sender1/2優先級相同,調度器將控制權交給等待時間最長的任務Sender1
- t5時刻,Sender1將隊列填滿,Receiver開始取數據
- …
處理大量的或者不同長度的數據 Working with Large or Variable Sized Data
處理大量數據
Queuing Pointers 隊列指針
如果數據量很大,那麼我們一般選擇傳遞指針而不是複製數據。傳遞指針對於處理時間和內存消耗都是高效的。但是,需要確保兩點:
- 指針指向的存儲空間地址是有定義的
指針指向的地址,一般來說,只要相應發送和接收任務纔可以寫入和讀取,而且需要是有效、連續的! - 指針指向的地址仍然有效
任何時候不要把任務堆棧的地址作爲指針傳遞,因爲任務堆棧會在任務修改時自動釋放
示例
處理類型或長度不同的數據 Using a Queue to Send Different Types and Lengths of Data
將結構體和指針相結合,隊列可以傳遞不同類型的數據
示例 FreeRTOS+TCP/IP
從多個隊列中接收 Receiving From Multiple Queues
隊列集 Queue Sets
隊列集允許任務從多個隊列中接收數據而不用知道哪個有數據。隊列集並不比使用單一隊列傳遞結構體更加高效,所以隊列集一般只在必要的時候使用。
xQueueCreateSet() 創建隊列集
xQueueAddToSet() 添加到隊列集
將一個隊列或者信號量添加到隊列集
xQueueSelectFromSet() 從一個隊列集中讀取隊列句柄
在讀取數據前,我們需要先調用這個函數獲取集合中某個隊列或信號量的句柄,然後才能繼續讀取數據!!
示例 1
示例 2
隊列集中包含了二值信號量,uint32_t變量,指針
利用隊列創建郵箱 Using a Queue to Create a Mailbox
本文中,郵箱指的是長度爲1的隊列,之所以叫郵箱,是因爲他們作用相似
- 發送-接收模式
- 郵件可以被任一個任務或ISR讀取
API
xQueueOverwrite() 隊列覆寫
用來發送數據到隊列,與xQueueSendToBack()的區別在於 可以覆寫
這個函數應該僅被用於隊列長度爲1的情況
注意!不能在中斷中使用!!
示例
xQueuePeek() 不取出元素查看
示例