μC/OS-II的任務(wù)之間的通訊與同步
{
INT8Uerr;
for(;;){
OSMboxPost(MboxTimeDly,(void*)1);/*取消任務(wù)1的延時*/
.
.
}
}
6.8 消息隊列
消息隊列是μC/OS-II中另一種通訊機制, 它可以使一個任務(wù)或者中斷服務(wù)子程序向另一個任務(wù)發(fā)送以指針方式定義的變量。因具體的應(yīng)用有所不同,每個指針指向的數(shù)據(jù)結(jié)構(gòu)變量也有所不同。為了使用μC/OS-II的消息隊列功能,需要在OS_CFG.H文件中,將OS_Q_EN常數(shù)設(shè)置為1,并且通過常數(shù)OS_MAX_QS來決定μC/OS-II支持的最多消息隊列數(shù)。
在使用一個消息隊列之前, 必須先建立該消息隊列。 這可以通過調(diào)用OSQCreate()函數(shù) (見
6.07.01節(jié)),并定義消息隊列中的單元數(shù)(消息數(shù))來完成。
μC/OS-II提供了7個對消息隊列進行操作的函數(shù):OSQCreate(),OSQPend(),OSQPost(),
OSQPostFront(),OSQAccept(),OSQFlush()和OSQQuery()函數(shù)。圖F6.7是任務(wù)、中斷服務(wù)子程序和消息隊列之間的關(guān)系。其中,消息隊列的符號很像多個郵箱。實際上,我們可以將消息隊列看作時多個郵箱組成的數(shù)組,只是它們共用一個等待任務(wù)列表。每個指針?biāo)赶虻臄?shù)據(jù)結(jié)構(gòu)是由具體的應(yīng)用程序決定的。N代表了消息隊列中的總單元數(shù)。當(dāng)調(diào)用OSQPend()或者OSQAccept()之前,調(diào)用N次OSQPost()或者OSQPostFront()就會把消息隊列填滿。從圖F6.7中可以看出,一個任務(wù)或者中斷服務(wù)子程序可以調(diào)用OSQPost(),OSQPostFront(),OSQFlush()或者OSQAccept()函數(shù)。但是,只有任務(wù)可以調(diào)用OSQPend()和OSQQuery()函數(shù)。
圖F6.7任務(wù)、中斷服務(wù)子程序和消息隊列之間的關(guān)系——Figure6.7
圖F6.8是實現(xiàn)消息隊列所需要的各種數(shù)據(jù)結(jié)構(gòu)。這里也需要事件控制塊來記錄等待任務(wù)列表[F6.8(1)],而且,事件控制塊可以使多個消息隊列的操作和信號量操作、郵箱操作相同的代碼。當(dāng)建立了一個消息隊列時,一個隊列控制塊(OS_Q結(jié)構(gòu),見OS_Q.C文件)也同時被建立,并通過OS_EVENT中的.OSEventPtr域鏈接到對應(yīng)的事件控制塊[F6.8(2)]。 在建立一個消息隊列之前,必須先定義一個含有與消息隊列最大消息數(shù)相同個數(shù)的指針數(shù)組[F6.8(3)]。數(shù)組的起始地址以及數(shù)組中的元素數(shù)作為參數(shù)傳遞給OSQCreate()函數(shù)。事實上,如果內(nèi)存占用了連續(xù)的地址空間,也沒有必要非得使用指針數(shù)組結(jié)構(gòu)。
文件OS_CFG.H中的常數(shù)OS_MAX_QS定義了在μC/OS-II中可以使用的最大消息隊列數(shù),這個值最小應(yīng)為2。μC/OS-II在初始化時建立一個空閑的隊列控制塊鏈表,如圖F6.9所示。
圖F6.8用于消息隊列的數(shù)據(jù)結(jié)構(gòu)——Figure6.8
圖F6.9空閑隊列控制塊鏈表——Figure6.9
隊列控制塊是一個用于維護消息隊列信息的數(shù)據(jù)結(jié)構(gòu),它包含了以下的一些域。這里,仍然在各個變量前加入一個[.]來表示它們是數(shù)據(jù)結(jié)構(gòu)中的一個域。
.OSQPtr在空閑隊列控制塊中鏈接所有的隊列控制塊。一旦建立了消息隊列,該域就不再有用了。
.OSQStart是指向消息隊列的指針數(shù)組的起始地址的指針。用戶應(yīng)用程序在使用消息隊列之前必須先定義該數(shù)組。
.OSQEnd是指向消息隊列結(jié)束單元的下一個地址的指針。該指針使得消息隊列構(gòu)成一個循環(huán)的緩沖區(qū)。
.OSQIn 是指向消息隊列中插入下一條消息的位置的指針。當(dāng).OSQIn和.OSQEnd相等時,.OSQIn被調(diào)整指向消息隊列的起始單元。
.OSQOut 是指向消息隊列中下一個取出消息的位置的指針。當(dāng).OSQOut和.OSQEnd相等時,.OSQOut被調(diào)整指向消息隊列的起始單元。
.OSQSize 是消息隊列中總的單元數(shù)。該值是在建立消息隊列時由用戶應(yīng)用程序決定的。在μC/OS-II中,該值最大可以是65,535。
.OSQEntries 是消息隊列中當(dāng)前的消息數(shù)量。當(dāng)消息隊列是空的時,該值為0。當(dāng)消息隊列滿了以后,該值和.OSQSize值一樣。在消息隊列剛剛建立時,該值為0。
消息隊列最根本的部分是一個循環(huán)緩沖區(qū),如圖F6.10。其中的每個單元包含一個指針。
隊列未滿時,.OSQIn[F6.10(1)]指向下一個存放消息的地址單元。如果隊列已滿(.OSQEntries
與.OSQSize相等),.OSQIn[F6.10(3)]則與.OSQOut指向同一單元。如果在.OSQIn指向的單元
插入新的指向消息的指針,就構(gòu)成 FIFO(First-In-First-Out)隊列。相反,如果在.OSQOut
指向的單元的下一個單元插入新的指針,就構(gòu)成LIFO隊列(Last-In-First-Out)[F6.10(2)]。
當(dāng).OSQEntries和.OSQSize相等時,說明隊列已滿。消息指針總是從.OSQOut[F6.10(4)]指向
的單元取出。指針.OSQStart和.OSQEnd [F6.10(5)]定義了消息指針數(shù)組的頭尾,以便在.OSQIn
和.OSQOut到達隊列的邊緣時,進行邊界檢查和必要的指針調(diào)整,實現(xiàn)循環(huán)功能。
圖F6.10消息隊列是一個由指針組成的循環(huán)緩沖區(qū)——Figure6.10
6.8.1 建立一個消息隊列,OSQCreate()
程序清單L6.21是OSQCreate()函數(shù)的源代碼。該函數(shù)需要一個指針數(shù)組來容納指向各個消息的指針。該指針數(shù)組必須聲名為void類型。
OSQCreate()首先從空閑事件控制塊鏈表中取得一個事件控制塊(見圖F6.3)[L6.21(1)],并對剩下的空閑事件控制塊列表的指針做相應(yīng)的調(diào)整,使它指向下一個空閑事件控制塊[L6.21(2)]。 接著, OSQCreate()函數(shù)從空閑隊列控制塊列表中取出一個隊列控制塊[L6.21(3)]。
如果有空閑隊列控制塊是可以的,就對其進行初始化[L6.21(4)]。然后該函數(shù)將事件控制塊的類型設(shè)置為OS_EVENT_TYPE_Q[L6.21(5)],并使其.OSEventPtr指針指向隊列控制塊[L6.21(6)]。OSQCreate()還要調(diào)用OSEventWaitListInit()函數(shù)對事件控制塊的等待任務(wù)列表初始化[見6.01節(jié),初始化一個事件控制塊,OSEventWaitListInit()][L6.21(7)]。因為此時消息隊列正在初始化,顯然它的等待任務(wù)列表是空的。最后,OSQCreate()向它的調(diào)用函數(shù)返回一個指向事件控制塊的指針[L6.21(9)]。該指針將在調(diào)用OSQPend(),OSQPost(),OSQPostFront(),OSQFlush(),OSQAccept()和OSQQuery()等消息隊列處理函數(shù)時使用。因此,該指針可以被看作是對應(yīng)消息隊列的句柄。值得注意的是,如果此時沒有空閑的事件控制塊,OSQCreate()函數(shù)將返回一個NULL指針。如果沒有隊列控制塊可以使用,為了不浪費事件控制塊資源,OSQCreate()函數(shù)將把剛剛?cè)〉玫氖录刂茐K重新返還給空閑事件控制塊列表[L6.21(8)]。
評論