從零開始學習UCOSII操作系統9--事件標誌組
1、事件標誌組管理
(1)UCOSII的事件標誌組由2部分組成:
一是用來保存當前事件組中各事件狀態的一些標誌位。
二是等待這些標誌位置位或者清除的任務列表。
(2)UCOSII提供了6個函數,完成事件標誌組的各種功能:
OSFlagAccept(),OSFlagCreate(),OSFlagDel(),
OSFlagPend(),OSFlagPost()以及OSFlagQuery();
2、中斷和任務之間的通過事件標誌組的關係
(1)任務或者中斷都可以調用函數OSFlagAccept(),OSFlagPost()或者OSFlagQuery();但是隻有任務纔可以調用函數OSFlagCreate(),OSFlagDel()以及OSFlagPend();
3、事件標誌組的結構
(1)參數1:OSFlagType變量用來檢測指針的類型是否是指向事件標誌組的指針,這個變量是事件標誌組數據結構的第一個成員。
注意:事件控制塊ECB的第一個字節也是一個標誌該事件控制塊的類型:
信號量,互斥型信號量,郵箱或者消息隊列的變量
(2)參數2:OSFlagWaitList包含了一個等待事件標誌組的任務列表。
(3)OSFlagFlags包含了一系列表明當前事件標誌狀態的位,這些位的數目在編譯時由OS_CFG.h中的OS_FLAGS常量決定。
typedef struct
{
INT8U OSFlagType; //定義一個類型,用來放置到事件標誌組中
void *OSFlagWaitList; //定義一個事件標誌鏈表
OS_FLAGS OSFlagFlags;
}OS_FLAG_GRP;
這裏使用的等待事件標誌組的任務列表和UCOSII裏面用到的其他的等待任務列表不同。這裏的任務列表是一個雙向鏈表,使用了3個數據結構。
當一個任務開始等待某些事件標誌位時,就建立一個OS_FLAG_NODE數據結構,當這些等待的事件標誌位發生後,這個數據結構被刪除,就是換句話說,當調用OSFlagPend()時,建立OS_FLAG_NODE.
(4)OS_FLAG_NODE的結構定義
OSFlagNodeNext和OSFlagNodePrev用來構建雙向OS_FLAG_NODE數據結構鏈表、這種雙向鏈表可以很方便地向鏈表中插入或者從鏈表中刪除一個OS_FLAG_NODE的結構。
OSFlagNodeTCB指針指向某個等待事件標誌組中的事件標誌的任務控制塊。
換句話說,通過這個指針,可以知道哪一個任務在等待事件標誌組中的事件。
typedef struct
{
void *OSFlagNodeNext;
void *OSFlagNodePrev;
void *OSFlagNodeTCB;
void *OSFlagNodeFlagGrp;
OS_FLAGS OSFlagNodeFlags;
INT8U OSFlagNodeWaitType;
}OS_FLAG_NODE;
實現事件標誌組的鏈表:
根據圖中可以詳細的知道一個事件控制組的列表是由三個部分組成的:
(1)首先是定義事件控制塊的類型:OSFlagType
其實是定義事件標誌組的標誌位,用來判斷是否執行這些請求了這些標誌位的任務。
最後是一個鏈表的結構:這個鏈表的一個單元是OS_FLAG_NODE
(2)這個鏈表結點的成員包括:
連接這些結點的兩個鏈表指針:OSFlagNodeNext,OSFlagNodePrev;
(3)有一個OSFlagNodeTCB,指針指向某個等待事件標誌組中的事件標誌的任務控制塊,換句話說,通過這個指針可以知道哪一個任務在等待事件標誌組中的事件。
(4)OS_FlagNodeFlagGrp是一個反向指向事件標誌組的指針,在刪除一個OS_FLAG_NODE數據結構或者通過OSTaskDel()刪除一個仍然在等待事件標誌的任務時候,將會用到這個指針。
(5)OSFlagNodeFlags用來指明任務等待事件標誌組中的哪些事件標誌。
任務可能調用OSFlagPend(),並且指明等待事件標誌組中的事件標誌。
在這種情況下面,OSFlagNodeFlags的值爲0xD1,根據由OS_FLAGS指定的事件標誌組的位數。OS_FLAGS可能是8位或者16位或者32位。
可以很方便的根據實際產品的需要更改事件標誌組的位數。使用8位的事件標誌組,可以減少程序佔用的RAM和ROM空間。但是爲了程序的可移植性,我們一般定義爲32位。
(6)最後一個變量是OSFlagNodeWaitType(),這個變量指明任務是等待事件標誌組中的所有事件標誌都發生(與型),還是任何的一個事件標誌發生或型。
4、使用事件標誌組的API函數
(1)建立一個事件標誌組OSFlagCreate()
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags,INT8U *err)
{
OS_FLAG_GRP *pgrp;
OS_ENTER_CRITICAL();
pgrp = OSFlagFreeList;
if(pgrp != (OS_FLAG_GRP *)0)
{
OSFlagFreeList = (OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;
pgrp->OSFlagType = OS_EVENT_TYPE_FLAG;
pgrp->OSFlagFlags = flags;
pgrp->OSFlagWaitList = (void *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
else
{
OS_EXIT_CRITICAL();
*err = OS_FLAG_GRP_DEPLETED;
}
return (pgrp);
}
其實就是要在OS_FLAG_GRP填入相應的值:
填入flags參數值。
(2)刪除事件標誌組:
OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP * pgrp, INT8U opt,INT8U *err)
{
/*異常處理*/
OS_ENTER_CRITICAL();
if(pgrp->OSFlagWaitList != (void *)0)
{
tasks_waiting = TRUE;
}
else
{
tasks_waiting = FALSE;
}
switch(opt)
{
case OS_DEL_NO_PEND:
if(tasking_waiting == FALSE)
{
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0);
}
else
{
OS_EXIT_CRITICAL();
*err = OS_ERR_TASK_WAITING;
return (pgrp);
}
case OS_DEL_ALWAYS:
pnode = pgrp->OSFlagWaitList;
while(pnode != (OS_FLAG_NODE *)0)
{
OS_FlagTaskRdy(pnode, (OS_FLAGS)0);
pnode = pnode->OSFlagNodeNext;
}
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagwaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
if(tasks_waiting == TRUE)
{
OS_sched();
}
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0);
default:
OS_EXIT_CRITICAL();
*err = OS_ERR_INVALID_OPT;
return (pgrp);
}
}
PS:使用這個函數需要注意的是,多任務系統中可能出現一個任務試圖,訪問已經被刪除的事件標誌組,一般來說,在刪除一個事件標誌組之前,必須先刪除所有的可能用到這個事件標誌組的任務。
當調用選項:OS_DEL_ALWAYS時,所有等待這個事件標誌組的任務都會被標誌爲就緒態,認爲任務等待的事件都發生了,將在討論OSFlagPOST()函數時候,進一步的瞭解這裏用到的OS_FlagTaskRdy()函數。
(3)等待事件標誌組的事件標誌位OSFlagPend()
參數1:pgrp是事件標誌組
參數2:事件標誌位,比如哪些可以將其恢復到就緒狀態中
參數3:等待的類型,與或者或關係
參數4:超時時間
參數5:返回的輸出型參數:錯誤類型
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT32U timeout,
INT8U *perr)
{
OS_FLAG_NODE node;
OS_FLAGS flags_rdy;
INT8U result;
INT8U pend_stat;
BOOLEAN consume;
/*異常處理*/
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG)
{
/* 錯誤的事件標誌組類型 */
*perr = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
result = (INT8U)(wait_type & OS_FLAG_CONSUME);
if (result != (INT8U)0)
{
/* 我們判斷等待的類型 */
wait_type &= (INT8U)~(INT8U)OS_FLAG_CONSUME;
consume = OS_TRUE;
}
else
{
consume = OS_FALSE;
}
/*$PAGE*/
OS_ENTER_CRITICAL();
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
/* See if all required flags are set */
flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);
/* Extract only the bits we want */
if (flags_rdy == flags)
{
/* Must match ALL the bits that we want */
if (consume == OS_TRUE)
{
/* See if we need to consume the flags */
pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;
/* Clear ONLY the flags we wanted */
}
OSTCBCur->OSTCBFlagsRdy = flags_rdy;
/* Save flags that were ready */
OS_EXIT_CRITICAL();
/* Yes, condition met, return to caller */
*perr = OS_ERR_NONE;
return (flags_rdy);
} else {
/* Block task until events occur or timeout */
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
case OS_FLAG_WAIT_SET_ANY:
flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags); /* Extract only the bits we want */
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag set */
if (consume == OS_TRUE) { /* See if we need to consume the flags */
pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy; /* Clear ONLY the flags that we got */
}
OSTCBCur->OSTCBFlagsRdy = flags_rdy; /* Save flags that were ready */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*perr = OS_ERR_NONE;
return (flags_rdy);
} else { /* Block task until events occur or timeout */
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0u
case OS_FLAG_WAIT_CLR_ALL: /* See if all required flags are cleared */
flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
if (flags_rdy == flags) { /* Must match ALL the bits that we want */
if (consume == OS_TRUE) { /* See if we need to consume the flags */
pgrp->OSFlagFlags |= flags_rdy; /* Set ONLY the flags that we wanted */
}
OSTCBCur->OSTCBFlagsRdy = flags_rdy; /* Save flags that were ready */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*perr = OS_ERR_NONE;
return (flags_rdy);
} else { /* Block task until events occur or timeout */
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
case OS_FLAG_WAIT_CLR_ANY:
flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag cleared */
if (consume == OS_TRUE) { /* See if we need to consume the flags */
pgrp->OSFlagFlags |= flags_rdy; /* Set ONLY the flags that we got */
}
OSTCBCur->OSTCBFlagsRdy = flags_rdy; /* Save flags that were ready */
OS_EXIT_CRITICAL(); /* Yes, condition met, return to caller */
*perr = OS_ERR_NONE;
return (flags_rdy);
} else { /* Block task until events occur or timeout */
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#endif
default:
OS_EXIT_CRITICAL();
flags_rdy = (OS_FLAGS)0;
*perr = OS_ERR_FLAG_WAIT_TYPE;
return (flags_rdy);
}
/*$PAGE*/
OS_Sched(); /* Find next HPT ready to run */
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) { /* Have we timed-out or aborted? */
pend_stat = OSTCBCur->OSTCBStatPend;
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OS_FlagUnlink(&node);
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Yes, make task ready-to-run */
OS_EXIT_CRITICAL();
flags_rdy = (OS_FLAGS)0;
switch (pend_stat) {
case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted waiting */
break;
case OS_STAT_PEND_TO:
default:
*perr = OS_ERR_TIMEOUT; /* Indicate that we timed-out waiting */
break;
}
return (flags_rdy);
}
flags_rdy = OSTCBCur->OSTCBFlagsRdy;
if (consume == OS_TRUE) { /* See if we need to consume the flags */
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
case OS_FLAG_WAIT_SET_ANY: /* Clear ONLY the flags we got */
pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;
break;
#if OS_FLAG_WAIT_CLR_EN > 0u
case OS_FLAG_WAIT_CLR_ALL:
case OS_FLAG_WAIT_CLR_ANY: /* Set ONLY the flags we got */
pgrp->OSFlagFlags |= flags_rdy;
break;
#endif
default:
OS_EXIT_CRITICAL();
*perr = OS_ERR_FLAG_WAIT_TYPE;
return ((OS_FLAGS)0);
}
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; /* Event(s) must have occurred */
return (flags_rdy);
}
中間函數:添加一個任務到事件標誌組等待任務鏈表中
static void OS_FlagBlock(
OS_FLAG_GRP *pgrp,
OS_FLAG_NODE *pnode,
OS_FLAGS flags,
INT8U wait_type,
INT16U timeout
)
{
OS_FLAG_NODE *pnode_next;
OSTCBCur->OSTCBStat |= OS_STAT_FLAG;
OSTCBCur->OSTCBDly = timeout;
pnode->OSFlagNodeFlags = flags;
pnode->OSFlagNodeWaitType = wait_type;
pnode->OSFlagNodeTCB = (void *)OSTCBCur;
pnode->OSFlagNodeNext = pgrp->OSFlagWaitList;
pnode->OSFlagNodePrev = (void *)0;
pnode_next = pgrp->OSFlagWaitList;
if(pnode_next != (void *)0)
{
pnode_next->OSFlagNodePrev = pnode;
}
pgrp->OSFlagWaitList = (void *)pnode;
...
}