隊列是一種任務到任務、任務到中斷、中斷到任務數據交流的一種機制。在隊列中可以存 儲數量有限、大小固定的多個數據,隊列中的每一個數據叫做隊列項目,隊列能夠存儲隊列項 目的最大數量稱為隊列的長度,在創建隊列的時候,就需要指定所創建隊列的長度及隊列項目 的大小。
因為隊列是用來在任務與任務或任務于中斷之間傳遞消息的一種機制,因此隊列也叫 做消息隊列。 基于隊列,FreeRTOS 實現了多種功能,其中包括隊列集、互斥信號量、計數型信號量、二 值信號量、遞歸互斥信號量,因此很有必要深入了解 FreeRTOS 的隊列。
數據存儲
隊列的原則是先進先出(FIFO),當有新的數據寫入隊列時,都是寫入到隊列的尾部,當讀出數據時,先從隊列的頭部進行讀取。FreeRTOS也支持將數據寫到隊列的頭部,也可以覆蓋之前寫入的數據。
多任務訪問
隊列不屬于某個特定的任務,可以在任何的任務或中斷中往隊列中寫入消息,或者從隊列 中讀取消息。
隊列的寫入阻塞
當想某一個隊列寫入數據時,如果此時的隊列已經滿了,就是沒有空位置了,這時任務實際上是阻塞住了,因為想要寫入但是無法寫入,我們可以設定一個等待阻塞時間,等待的時間在設定范圍內,任務就會被添加到阻塞列表中進行等待,等待隊列中有空位置。如果在阻塞超時時間到達之前,隊列有空閑的位置,那么隊列寫入阻塞任務將會解除阻塞, 并往隊列中寫入消息,如果達到指定的阻塞超時時間,隊列依舊沒有空閑的位置寫入消息,那 么隊列寫入阻塞任務將會自動轉移到就緒態任務列表中,但不會往隊列中寫入消息。這個阻塞時間可以是0,可以是在系統規定的最大阻塞時間范圍之內。
注意:如果多個任務寫入同一個隊列時,那么多個任務會一起等待,會按照阻塞的先后和任務的優先級,決定應該解除哪一個隊列寫入阻塞任務,也就是同一個優先級先來的先解除,高優先級比低優先級先解除。
隊列的讀取阻塞
同寫入相同,當想某一個隊列讀取數據時,如果此時的隊列是空的,因為沒有數據讓任務讀取,這時任務實際上是阻塞住了,因為想要讀取卻沒有數據,我們可以設定一個等待阻塞時間,等待的時間在設定范圍內,任務就會被添加到阻塞列表中進行等待,等待隊列中有空位置。在任務從隊列讀取消息時,可以指定一個阻塞超時時間。如果任務在讀取隊列時,隊列為 空,這時任務將被根據指定的阻塞超時時間添加到阻塞態任務列表中進行阻塞,以等待隊列中 有可用的消息。當有其他任務或中斷將消息寫入隊列中,因等待隊列而阻塞任務將會被添加到 就緒態任務列表中,并讀取隊列中可用的消息。如果任務因等待隊列而阻塞的時間超過指定的 阻塞超時時間,那么任務也將自動被轉移到就緒態任務列表中,但不再讀取隊列中的數據。 因為同一個隊列可以被多個任務讀取,因此可能會有多個任務因等待同一個隊列,而被阻 塞,在這種情況下,如果隊列中有可用的消息,那么也只有一個任務會被解除阻塞并讀取到消 息,并且會按照阻塞的先后和任務的優先級,決定應該解除哪一個隊列讀取阻塞任務。
隊列的結構體
typedef struct QueueDefinition
{int8_t * pcHead; /* 存儲區域的起始地址 */int8_t * pcWriteTo; /* 下一個寫入的位置 *//* 信號量是由隊列實現的,* 此結構體能用于隊列和信號量,* 當用于隊列時,使用聯合體中的 xQueue,* 當用于信號量時,使用聯合體中的 xSemaphore*/union{QueuePointers_t xQueue;SemaphoreData_t xSemaphore;} u;List_t xTasksWaitingToSend; /* 寫入阻塞任務列表 */List_t xTasksWaitingToReceive; /* 讀取阻塞任務列表 */volatile UBaseType_t uxMessagesWaiting; /* 非空閑項目的數量 */UBaseType_t uxLength; /* 隊列的長度 */UBaseType_t uxItemSize; /* 隊列項目的大小 */volatile int8_t cRxLock; /* 讀取上鎖計數器 */volatile int8_t cTxLock;
}
結構體示意圖
前面說過 FreeRTOS 基于隊列實現了互斥信號量和遞歸互斥信號量功能,在隊列的結構體 中,就包含了一個聯合體 u,當隊列結構體用作隊列時,使用聯合體 u 中的 xQueue
typedef struct QueuePointers
{int8_t * pcTail; /* 存儲區域的結束地址 */int8_t * pcReadFrom; /* 最后一次讀取隊列的位置 */
} QueuePointers_t;
pcHead是存儲區的起始地址,pcWriteTo是下一個寫入的地址
pcTail是存儲區的結束地址,pcReadFrom是最后一次讀取隊列的位置
?xTasksWaitingToSend,當寫入發生阻塞時,任務會被添加到此列表中。
?xTasksWaitingToReceive,當讀取發生阻塞時,任務會被添加到此列表中。
uxMessagesWaiting時隊列中已經有數據的項目數量?
uxLength就是隊列的長度
uxItemSize就是隊列項目的大小?
隊列的鎖
在FreeRTOS中,隊列是一種用于在任務之間傳遞數據的通信機制。它可以實現生產者任務將數據發送到隊列中,然后消費者任務從隊列中接收數據。隊列的上鎖和解鎖操作是用來保護隊列數據的完整性和一致性的。
當一個任務要向隊列發送數據時,首先需要對隊列進行上鎖操作。這是為了防止其他任務同時訪問隊列,從而導致數據的錯誤讀寫。在上鎖期間,其他任務無法訪問隊列,直到上鎖任務完成發送操作并解鎖隊列。類似地,當一個任務要從隊列接收數據時,也需要對隊列進行上鎖操作。這是為了保證在接收數據的過程中,隊列中的數據不會被其他任務修改。在上鎖期間,其他任務無法修改隊列中的數據,直到上鎖任務完成接收操作并解鎖隊列。
通過對隊列進行上鎖和解鎖操作,可以確保在多任務環境下,隊列的數據操作是安全和可靠的。
?