FreeRTOS官方翻譯文檔——第二章 隊列管理


2.1 概覽
基於 FreeRTOS 的應用程序由一組獨立的任務構成——每個任務都是具有獨立權
限的小程序。這些獨立的任務之間很可能會通過相互通信以提供有用的系統功能。
FreeRTOS 中所有的通信與同步機制都是基於隊列實現的。

2.2隊列的特性
數據存儲
隊列可以保存有限個具有確定長度的數據單元。隊列可以保存的最大單元數目被稱
為隊列的深度。在隊列創建時需要設定其深度和每個單元的大小。
通常情況下,隊列被作為 FIFO(先進先出)使用,即數據由隊列尾寫入,從隊列首讀
出。當然,由隊列首寫入也是可能的。
往隊列寫入數據是通過字節拷貝把數據復制存儲到隊列中;從隊列讀出數據使得把
隊列中的數據拷貝刪除。 圖 19 展現了隊列的寫入與讀出過程,以及讀寫操作對隊列中
數據的影響。

可被多任務存取
隊列是具有自己獨立權限的內核對象,並不屬於或賦予任何任務。所有任務都可以
向同一隊列寫入和讀出。一個隊列由多方寫入是經常的事,但由多方讀出倒是很少遇到。

讀隊列時阻塞
當某個任務試圖讀一個隊列時,其可以指定一個阻塞超時時間。在這段時間中,如
果隊列為空,該任務將保持阻塞狀態以等待隊列數據有效。當其它任務或中斷服務例程
往其等待的隊列中寫入了數據,該任務將自動由阻塞態轉移為就緒態。當等待的時間超
過了指定的阻塞時間,即使隊列中尚無有效數據,任務也會自動從阻塞態轉移為就緒態。
由於隊列可以被多個任務讀取,所以對單個隊列而言,也可能有多個任務處於阻塞
狀態以等待隊列數據有效。這種情況下,一旦隊列數據有效,只會有一個任務會被解除
阻塞,這個任務就是所有等待任務中優先級最高的任務。而如果所有等待任務的優先級
相同,那么被解除阻塞的任務將是等待最久的任務。

寫隊列時阻塞
同讀隊列一樣,任務也可以在寫隊列時指定一個阻塞超時時間。這個時間是當被寫
隊列已滿時,任務進入阻塞態以等待隊列空間有效的最長時間。
由於隊列可以被多個任務寫入,所以對單個隊列而言,也可能有多個任務處於阻塞
狀態以等待隊列空間有效。這種情況下,一旦隊列空間有效,只會有一個任務會被解除
阻塞,這個任務就是所有等待任務中優先級最高的任務。而如果所有等待任務的優先級
相同,那么被解除阻塞的任務將是等待最久的任務。

 

2.3 使用隊列
xQueueCreate() API 函數
隊列在使用前必須先被創建。
隊列由聲明為 xQueueHandle 的變量進行引用。 xQueueCreate()用於創建一個隊
列,並返回一個 xQueueHandle 句柄以便於對其創建的隊列進行引用。
當創建隊列時, FreeRTOS 從堆空間中分配內存空間。分配的空間用於存儲隊列數
據結構本身以及隊列中包含的數據單元。如果內存堆中沒有足夠的空間來創建隊列,
xQueueCreate()將返回 NULL

 

xQueueSendToBack() xQueueSendToFront() API 函數
如同函數名字面意思所期望的一樣, xQueueSendToBack()用於將數據發送到隊列
尾;而 xQueueSendToFront()用於將數據發送到隊列首。
xQueueSend()完全等同於 xQueueSendToBack()
但 切 記 不 要 在 中 斷 服 務 例 程 中 調 用 xQueueSendToFront()
xQueueSendToBack()。系統提供中斷安全版本的 xQueueSendToFrontFromISR()
xQueueSendToBackFromISR()用於在中斷服務中實現相同的功能。

 

xQueueReceive()xQueuePeek() API 函數
xQueueReceive()用於從隊列中接收(讀取)數據單元。接收到的單元同時會從隊列中刪除。

xQueuePeek()也是從從隊列中接收數據單元,不同的是並不從隊列中刪出接收到
的單元。 xQueuePeek()從隊列首接收到數據后,不會修改隊列中的數據,也不會改變
數據在隊列中的存儲序順。
切記不要在中斷服務例程中調用 xQueueRceive()xQueuePeek()

 

uxQueueMessagesWaiting() API 函數
uxQueueMessagesWaiting()用於查詢隊列中當前有效數據單元個數。
切記不要在中斷服務例程中調用 uxQueueMessagesWaiting()。應當在中斷服務中
使用其中斷安全版本 uxQueueMessagesWaitingFromISR()

使用隊列傳遞復合數據類型
一個任務從單個隊列中接收來自多個發送源的數據是經常的事。通常接收方收到數
據后,需要知道數據的來源,並根據數據的來源決定下一步如何處理。一個簡單的方式
就是利用隊列傳遞結構體,結構體成員中就包含了數據信息和來源信息。 圖 23 對這一方案進行了展現。

 

從圖 23 中可以看出:
創建一個隊列用於保存類型為 xData 的結構體數據單元。結構體成員包括了一個數
據值和表示數據含義的編碼,兩者合為一個消息可以一次性發送到隊列。
中央控制任務用於完成主要的系統功能。 其必須對隊列中傳來的輸入和其它系統狀
態的改變作出響應。
• CAN 總線任務用於封裝 CAN 總線的接口功能。當 CAN 總線任務收到並解碼一個消
息后,其將把解碼后的消息放到 xData 結構體中發往控制任務。結構體的 iMeaning
成員用於讓中央控制任務知道這個數據是用來干什么的 從圖中的描述可以看
出,這個數據表示電機速度。結構體的 iValue 成員可以讓中央控制任務知道電機的
實際速度值。
人機接口(HMI)任務用於對所有的人機接口功能進行封裝。設備操作員可能通過各種
方式進行命令輸入和參數查詢,人機接口任務需要對這些操作進行檢測並解析。當
接收到一個新的命令后,人機接口任務通過 xData 結構將命令發送到中央控制任務。
結構體的 iMeaning 成員用於讓中央控制任務知道這個數據是用來干什么的
圖中的描述可以看出,這個數據表示一個新的參數設置。結構體的 iValue 成員可以
讓中央控制任務知道具體的設置值。

 工作於大型數據單元
如果隊列存儲的數據單元尺寸較大,那最好是利用隊列來傳遞數據的指針而不是對
數據本身在隊列上一字節一字節地拷貝進或拷貝出。傳遞指針無論是在處理速度上還是
內存空間利用上都更有效。但是,當你利用隊列傳遞指針時,一定要十分小心地做到以下兩點:

1. 指針指向的內存空間的所有權必須明確
當任務間通過指針共享內存時,應該從根本上保證所不會有任意兩個任務同時
修改共享內存中的數據,或是以其它行為方式使得共享內存數據無效或產生一致性
問題。原則上,共享內存在其指針發送到隊列之前,其內容只允許被發送任務訪問;
共享內存指針從隊列中被讀出之后,其內容亦只允許被接收任務訪問。
2. 指針指向的內存空間必須有效
如果指針指向的內存空間是動態分配的,只應該有一個任務負責對其進行內存
釋放。當這段內存空間被釋放之后,就不應該有任何一個任務再訪問這段空間。
切忌用指針訪問任務棧上分配的空間。因為當棧幀發生改變后,棧上的數據將不再有效。
 這個時候,再回過頭去看看上一篇隨筆,為什么官方demo使用結構體指針,因為這樣效率更高。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM