從零開始學習UCOSII操作系統11--消息隊列
1、什麼是消息隊列?
(1)傳遞多個郵箱的一種任務之間進行通信的方式
(2)消息隊列時UCOSII中的另一種的通信機制,他允許一個任務或者中斷服務子程序向另一個任務發送以指針方式定義的變量或者其他的任務。因爲具體的應用不同,每個指針指向的包含了消息的數據結構的變量類型也有所不同。
(3)UCOSII提供了9個對消息隊列進行操作的函數:OSQCreate(),OSQDel(),OSQPend(),OSQPost(),OSQPostFront()等等。
(4)至少要調用發送消息隊列中的一個函數OSQPost(),OSQPostFront()以及OSQPostOpt()功能函數中的一個函數。
(5)其中消息隊列看做是多個郵箱組成的數組,只是他們共用一個等待任務列表,每個指針所指向的數據結構類型是由具體的應用程序決定的。
2、怎麼使用消息隊列?
(1)是實現消息隊列所需要的各種數據結構:
OS_EVENT_TYPE_Q == OSEVENTType;
OSEventCnt == 0x00;
OSEventPtr == OS_Q ==指向一個數組的數組頭函數
用於消息隊列的數據結構
(2)消息其實就是存儲在一段內存中的一些數據,UCOS中的消息傳遞就是將這段內存的首地址傳遞出去,PEND請求到的消息就是這段內存的首地址,然後從這個地址開始讀取消息就可以了,一般情況下面這段內存通過定義一個數組來實現,數組名就是內存的首地址。
(3)這裏也需要事件控制塊ECB記錄等待任務列表,而且事件控制塊可以使多個消息隊列的 操作與信號量函數、互斥性信號量以及郵箱使用相同的代碼。
(4)在建立一個消息隊列之前,必須先定義一個含有與消息隊列最大消息數相同的指針數組,換句話說,這個數組的單元數與隊列中的單元數是一致的
3、一個簡單的實例:說明怎麼使用消息隊列:
消息隊列其實就是多個消息郵箱的應用。
//首先定義一個消息隊列的存儲地址,最大支持256個消息:
void * MsgGrp[256];
//對列消息顯示任務
void qmsgshow_task(void pdata)
{
u8 p;u8 err;
while(1)
{
p = OSQpend(q_msg,0,&err); //請求消息隊列
LCD_ShowString(5,170,240,16,16,p);//顯示消息
myfree(SRAMIN,p);
}
}
//然後是任意的一個函數的任務:
void tmr3_callback()
{
OSQPost(q_msg,p); //發送隊列
}
void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
void *msg;
OS_Q *pq;
pq = pevent->OSEventPtr; /* Point at queue control block */
//然後看隊列中是否有消息
if (pq->OSQEntries != 0) {
/* 不斷的查看消息對列中是否有信息 */
msg = *pq->OSQOut++;
/* Yes, extract oldest message from the queue */
pq->OSQEntries--;
/* Update the number of entries in the queue */
if (pq->OSQOut == pq->OSQEnd)
{
/* Wrap OUT pointer if we are at the end of the queue */
pq->OSQOut = pq->OSQStart;//如果到頭則重新進行初始化
}
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else if (OSIntNesting > 0) {
/* See if called from ISR ... */
OS_EXIT_CRITICAL(); /* ... can't PEND from an ISR */
*err = OS_ERR_PEND_ISR;
} else {
//若沒有消息,則置爲等待消息的狀態
OSTCBCur->OSTCBStat |= OS_STAT_Q; /* Task will have to pend for a message to be posted */
//並決定是否需要等待
OSTCBCur->OSTCBDly = timeout; /* Load timeout into TCB */
//把此任務加入到等待消息的列表中
OSEventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
//然後切換任務
OSSched(); /* Find next highest priority task ready to run */
OS_ENTER_CRITICAL();
//待任務回來,判斷是否已經得到消息
if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) {/* Did we get a message? */
OSTCBCur->OSTCBMsg = (void *)0; /* Extract message from TCB (Put there by QPost) */
//若等到消息,則更改爲直接就緒狀態
OSTCBCur->OSTCBStat = OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event */
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
//若沒有收到消息,則說明任務超時
//表明一定是時間超時造成
else if (OSTCBCur->OSTCBStat & OS_STAT_Q)
{ /* Timed out if status indicates pending on Q */
OSEventTO(pevent);//若消息依然在等待消息,則強制任務從等待列表中返回
OS_EXIT_CRITICAL();
msg = (void *)0; /* No message received */
*err = OS_TIMEOUT; /* Indicate a timeout occured */
} else {//這裏的else很疑惑:如果已經超時,且等待狀態標誌也被清零,則說明已經得到了消息(從OSQPend()中得到 )
//那麼爲何還要從隊列中取消息呢
msg = *pq->OSQOut++; /* Extract message from queue */
pq->OSQEntries--; /* Update the number of entries in the queue */
if (pq->OSQOut == pq->OSQEnd) { /* Wrap OUT pointer if we are at the end of Q */
pq->OSQOut = pq->OSQStart;
}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
}
return (msg); /* Return message received (or NULL) */
}