μC/OS-II的任務之間的通訊與同步
{
INT8Uerr;
for(;;){
OSMboxPost(MboxTimeDly,(void*)1);/*取消任務1的延時*/
.
.
}
}
6.8 消息隊列
消息隊列是μC/OS-II中另一種通訊機制, 它可以使一個任務或者中斷服務子程序向另一個任務發送以指針方式定義的變量。因具體的應用有所不同,每個指針指向的數據結構變量也有所不同。為了使用μC/OS-II的消息隊列功能,需要在OS_CFG.H文件中,將OS_Q_EN常數設置為1,并且通過常數OS_MAX_QS來決定μC/OS-II支持的最多消息隊列數。
在使用一個消息隊列之前, 必須先建立該消息隊列。 這可以通過調用OSQCreate()函數 (見
6.07.01節),并定義消息隊列中的單元數(消息數)來完成。
μC/OS-II提供了7個對消息隊列進行操作的函數:OSQCreate(),OSQPend(),OSQPost(),
OSQPostFront(),OSQAccept(),OSQFlush()和OSQQuery()函數。圖F6.7是任務、中斷服務子程序和消息隊列之間的關系。其中,消息隊列的符號很像多個郵箱。實際上,我們可以將消息隊列看作時多個郵箱組成的數組,只是它們共用一個等待任務列表。每個指針所指向的數據結構是由具體的應用程序決定的。N代表了消息隊列中的總單元數。當調用OSQPend()或者OSQAccept()之前,調用N次OSQPost()或者OSQPostFront()就會把消息隊列填滿。從圖F6.7中可以看出,一個任務或者中斷服務子程序可以調用OSQPost(),OSQPostFront(),OSQFlush()或者OSQAccept()函數。但是,只有任務可以調用OSQPend()和OSQQuery()函數。

圖F6.7任務、中斷服務子程序和消息隊列之間的關系——Figure6.7
圖F6.8是實現消息隊列所需要的各種數據結構。這里也需要事件控制塊來記錄等待任務列表[F6.8(1)],而且,事件控制塊可以使多個消息隊列的操作和信號量操作、郵箱操作相同的代碼。當建立了一個消息隊列時,一個隊列控制塊(OS_Q結構,見OS_Q.C文件)也同時被建立,并通過OS_EVENT中的.OSEventPtr域鏈接到對應的事件控制塊[F6.8(2)]。 在建立一個消息隊列之前,必須先定義一個含有與消息隊列最大消息數相同個數的指針數組[F6.8(3)]。數組的起始地址以及數組中的元素數作為參數傳遞給OSQCreate()函數。事實上,如果內存占用了連續的地址空間,也沒有必要非得使用指針數組結構。
文件OS_CFG.H中的常數OS_MAX_QS定義了在μC/OS-II中可以使用的最大消息隊列數,這個值最小應為2。μC/OS-II在初始化時建立一個空閑的隊列控制塊鏈表,如圖F6.9所示。

圖F6.8用于消息隊列的數據結構——Figure6.8

圖F6.9空閑隊列控制塊鏈表——Figure6.9
隊列控制塊是一個用于維護消息隊列信息的數據結構,它包含了以下的一些域。這里,仍然在各個變量前加入一個[.]來表示它們是數據結構中的一個域。
.OSQPtr在空閑隊列控制塊中鏈接所有的隊列控制塊。一旦建立了消息隊列,該域就不再有用了。
.OSQStart是指向消息隊列的指針數組的起始地址的指針。用戶應用程序在使用消息隊列之前必須先定義該數組。
.OSQEnd是指向消息隊列結束單元的下一個地址的指針。該指針使得消息隊列構成一個循環的緩沖區。
.OSQIn 是指向消息隊列中插入下一條消息的位置的指針。當.OSQIn和.OSQEnd相等時,.OSQIn被調整指向消息隊列的起始單元。
.OSQOut 是指向消息隊列中下一個取出消息的位置的指針。當.OSQOut和.OSQEnd相等時,.OSQOut被調整指向消息隊列的起始單元。
.OSQSize 是消息隊列中總的單元數。該值是在建立消息隊列時由用戶應用程序決定的。在μC/OS-II中,該值最大可以是65,535。
.OSQEntries 是消息隊列中當前的消息數量。當消息隊列是空的時,該值為0。當消息隊列滿了以后,該值和.OSQSize值一樣。在消息隊列剛剛建立時,該值為0。
消息隊列最根本的部分是一個循環緩沖區,如圖F6.10。其中的每個單元包含一個指針。
隊列未滿時,.OSQIn[F6.10(1)]指向下一個存放消息的地址單元。如果隊列已滿(.OSQEntries
與.OSQSize相等),.OSQIn[F6.10(3)]則與.OSQOut指向同一單元。如果在.OSQIn指向的單元
插入新的指向消息的指針,就構成 FIFO(First-In-First-Out)隊列。相反,如果在.OSQOut
指向的單元的下一個單元插入新的指針,就構成LIFO隊列(Last-In-First-Out)[F6.10(2)]。
當.OSQEntries和.OSQSize相等時,說明隊列已滿。消息指針總是從.OSQOut[F6.10(4)]指向
的單元取出。指針.OSQStart和.OSQEnd [F6.10(5)]定義了消息指針數組的頭尾,以便在.OSQIn
和.OSQOut到達隊列的邊緣時,進行邊界檢查和必要的指針調整,實現循環功能。

圖F6.10消息隊列是一個由指針組成的循環緩沖區——Figure6.10
6.8.1 建立一個消息隊列,OSQCreate()
程序清單L6.21是OSQCreate()函數的源代碼。該函數需要一個指針數組來容納指向各個消息的指針。該指針數組必須聲名為void類型。
OSQCreate()首先從空閑事件控制塊鏈表中取得一個事件控制塊(見圖F6.3)[L6.21(1)],并對剩下的空閑事件控制塊列表的指針做相應的調整,使它指向下一個空閑事件控制塊[L6.21(2)]。 接著, OSQCreate()函數從空閑隊列控制塊列表中取出一個隊列控制塊[L6.21(3)]。
如果有空閑隊列控制塊是可以的,就對其進行初始化[L6.21(4)]。然后該函數將事件控制塊的類型設置為OS_EVENT_TYPE_Q[L6.21(5)],并使其.OSEventPtr指針指向隊列控制塊[L6.21(6)]。OSQCreate()還要調用OSEventWaitListInit()函數對事件控制塊的等待任務列表初始化[見6.01節,初始化一個事件控制塊,OSEventWaitListInit()][L6.21(7)]。因為此時消息隊列正在初始化,顯然它的等待任務列表是空的。最后,OSQCreate()向它的調用函數返回一個指向事件控制塊的指針[L6.21(9)]。該指針將在調用OSQPend(),OSQPost(),OSQPostFront(),OSQFlush(),OSQAccept()和OSQQuery()等消息隊列處理函數時使用。因此,該指針可以被看作是對應消息隊列的句柄。值得注意的是,如果此時沒有空閑的事件控制塊,OSQCreate()函數將返回一個NULL指針。如果沒有隊列控制塊可以使用,為了不浪費事件控制塊資源,OSQCreate()函數將把剛剛取得的事件控制塊重新返還給空閑事件控制塊列表[L6.21(8)]。
評論