μC/OS-II的任務(wù)之間的通訊與同步
程序清單L6.26清空消息隊列
INT8UOSQFlush(OS_EVENT*pevent)
{
OS_Q*pq;
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_Q){(1)
OS_EXIT_CRITICAL();
return(OS_ERR_EVENT_TYPE);
}
pq=pevent->OSEventPtr;
pq->OSQIn=pq->OSQStart;(2)
pq->OSQOut=pq->OSQStart;
pq->OSQEntries=0;
OS_EXIT_CRITICAL();
return(OS_NO_ERR);
}
6.8.7 查詢一個消息隊列的狀態(tài),OSQQuery()
OSQQuery()函數(shù)使用戶可以查詢一個消息隊列的當前狀態(tài)。 程序清單L6.27是該函數(shù)的源代碼。OSQQuery()需要兩個參數(shù):一個是指向消息隊列的指針pevent。它是在建立一個消息隊列時,由OSQCreate()函數(shù)返回的;另一個是指向OS_Q_DATA(見uCOS_II.H)數(shù)據(jù)結(jié)構(gòu)的指針pdata。該結(jié)構(gòu)包含了有關(guān)消息隊列的信息。在調(diào)用OSQQuery()函數(shù)之前,必須先定義該數(shù)據(jù)結(jié)構(gòu)變量。OS_Q_DATA結(jié)構(gòu)包含下面的幾個域:
.OSMsg 如果消息隊列中有消息,它包含指針.OSQOut所指向的隊列單元中的內(nèi)容。如果隊列是空的,.OSMsg包含一個NULL指針。
.OSNMsgs是消息隊列中的消息數(shù)(.OSQEntries的拷貝)。
.OSQSize 是消息隊列的總的容量
.OSEventTbl[]和.OSEventGrp是消息隊列的等待任務(wù)列表。通過它們,OSQQuery()的調(diào)用函數(shù)可以得到等待該消息隊列中的消息的任務(wù)總數(shù)。
OSQQuery()函數(shù)首先檢查pevent指針指向的事件控制塊是一個消息隊列[L6.27(1)],然后復(fù)制等待任務(wù)列表[L6.27(2)]。如果消息隊列中有消息[L6.27(3)],.OSQOut指向的隊列單元中的內(nèi)容被復(fù)制到OS_Q_DATA結(jié)構(gòu)中[L6.27(4)], 否則的話, 就復(fù)制一個NULL指針[L6.27(5)]。
最后,復(fù)制消息隊列中的消息數(shù)和消息隊列的容量大小[L6.27(6)]。
程序清單L6.27程序消息隊列的狀態(tài)
INT8UOSQQuery(OS_EVENT*pevent,OS_Q_DATA*pdata)
{
OS_Q*pq;
INT8Ui;
INT8U*psrc;
INT8U*pdest;
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_Q){(1)
OS_EXIT_CRITICAL();
return(OS_ERR_EVENT_TYPE);
}
pdata->OSEventGrp=pevent->OSEventGrp;(2)
psrc=pevent->OSEventTbl[0];
pdest=pdata->OSEventTbl[0];
for(i=0;i
*pdest++=*psrc++;
}
pq=(OS_Q*)pevent->OSEventPtr;
if(pq->OSQEntries>0){(3)
pdata->OSMsg=pq->OSQOut;(4)
}else{
pdata->OSMsg=(void*)0;(5)
}
pdata->OSNMsgs=pq->OSQEntries;(6)
pdata->OSQSize=pq->OSQSize;
OS_EXIT_CRITICAL();
return(OS_NO_ERR);
}
6.8.8 使用消息隊列讀取模擬量的值
在控制系統(tǒng)中,經(jīng)常要頻繁地讀取模擬量的值。這時,可以先建立一個定時任務(wù)OSTimeDly()[見5.00節(jié),延時一個任務(wù),OSTimeDly()],并且給出希望的抽樣周期。然后,如圖F6.11所示,讓A/D采樣的任務(wù)從一個消息隊列中等待消息。該程序最長的等待時間就是抽樣周期。當沒有其它任務(wù)向該消息隊列中發(fā)送消息時,A/D采樣任務(wù)因為等待超時而退出等待狀態(tài)并進行執(zhí)行。這就模仿了OSTimeDly()函數(shù)的功能。
也許,讀者會提出疑問,既然OSTimeDly()函數(shù)能完成這項工作,為什么還要使用消息隊列呢?這是因為,借助消息隊列,我們可以讓其它的任務(wù)向消息隊列發(fā)送消息來終止A/D采樣任務(wù)等待消息,使其馬上執(zhí)行一次A/D采樣。此外,我們還可以通過消息隊列來通知A/D采樣程序具體對哪個通道進行采樣,告訴它增加采樣頻率等等,從而使得我們的應(yīng)用更智能化。換句話說,我們可以告訴A/D采樣程序,“現(xiàn)在馬上讀取通道3的輸入值!”之后,該采樣任務(wù)將重新開始在消息隊列中等待消息,準備開始一次新的掃描過程。

圖F6.11讀模擬量輸入——Figure6.11
6.8.9 使用一個消息隊列作為計數(shù)信號量
在消息隊列初始化時,可以將消息隊列中的多個指針設(shè)為非NULL值(如void*1),來實現(xiàn)計數(shù)信號量的功能。這里,初始化為非NULL值的指針數(shù)就是可用的資源數(shù)。系統(tǒng)中的任務(wù)可以通過OSQPend()來請求“信號量”,然后通過調(diào)用OSQPost()來釋放“信號量”,如程序清單L6.28。如果系統(tǒng)中只使用了計數(shù)信號量和消息隊列,使用這種方法可以有效地節(jié)省代碼空間。
這時將OS_SEM_EN設(shè)為0,就可以不使用信號量,而只使用消息隊列。值得注意的是,這種方法
為共享資源引入了大量的指針變量。也就是說,為了節(jié)省代碼空間,犧牲了RAM空間。另外,
對消息隊列的操作要比對信號量的操作慢,因此,當用計數(shù)信號量同步的信號量很多時,這種
方法的效率是非常低的。
程序清單L6.28使用消息隊列作為一個計數(shù)信號量
OS_EVENT*QSem;
void*QMsgTbl[N_RESOURCES]
voidmain(void)
{
OSInit();
.
QSem=OSQCreate(QMsgTbl[0],N_RESOURCES);
for(i=0;i
OSQPost(Qsem,(void*)1);
}
.
.
OSTaskCreate(Task1,..,..,..);
.
.
OSStart();
}
voidTask1(void*pdata)
{
INT8Uerr;
for(;;){
OSQPend(QSem,0,err);/*得到對資源的訪問權(quán)*/
.
./*任務(wù)獲得信號量,對資源進行訪問*/
.
OSMQPost(QSem,(void*)1);/*釋放對資源的訪問權(quán)*/
}
}
評論