事件标志組
當任務需要與多個事件的發生同步時,可以使用事件标志組。等待多個事件時,任何一個事件發生,任務才被同步,這種同步機制稱為“或”同步,當所有的事件都發生時,任務才能被同步,這種同步機制被稱為“與”同步。
這兩種同步機制如下圖所示:
1 UCOSIII中的事件标志組就是OS_FLAG_GRP類型中的核心對象,由一串事件标志組組成。
2 任務和ISR都可以釋出事件标志。
3 任務可以等待事件标志組中的任意個事件标志。
4 任務可以設定使用或同步機制還是與同步機制。
事件标志組API函數
如何建立事件标志組
在使用事件标志組之前,需要調用函數OSFlagCreate()建立一個事件标志組
void OSFlagCreate (OS_FLAG_GRP *p_grp, //指向事件标志組
CPU_CHAR *p_name, //事件标志組的名字
OS_FLAGS flags, //定義事件标志組的初始值
OS_ERR *p_err)
{
CPU_SR_ALLOC();
OS_CRITICAL_ENTER();
p_grp->Type = OS_OBJ_TYPE_FLAG; /* Set to event flag group type */
p_grp->NamePtr = p_name;
p_grp->Flags = flags; /* Set to desired initial value */
p_grp->TS = (CPU_TS)0;
OS_PendListInit(&p_grp->PendList);
OSFlagQty++;
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
}
事件标志組的結構體OS_FLAG_GRP:
struct os_flag_grp { /* Event Flag Group */
/* ------------------ GENERIC MEMBERS ------------------ */
OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_FLAG */
CPU_CHAR *NamePtr; /* 事件标志組的名稱 */
OS_PEND_LIST PendList; /* 等待事件标志組的任務組 */
#if OS_CFG_DBG_EN > 0u
OS_FLAG_GRP *DbgPrevPtr;
OS_FLAG_GRP *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
/* ------------------ SPECIFIC MEMBERS ------------------ */
OS_FLAGS Flags; /* 8, 16 or 32 bit flags */
CPU_TS TS; /* Timestamp of when last post occurred */
};
Flags是32位,它的每一位都代表着一個任務的狀态,每個任務有1和0兩種狀态。也就是說,我們可以同時最多完成一個任務和32個任務的任務同步!我們可以設定:Falgs的第0、1兩個任務為1的時候,完成任務同步,也就是說,Flags變成0x03的時候,完成同步。當然,OSFlagCreate()函數中的flags參數隻是确定一個初始值。
等待事件标志組
等待一個事件标志組需要調用函數OSFlagPend()
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp, //指向事件标志組
OS_FLAGS flags, //bit序列
OS_TICK timeout, //指定等待事件标志組的逾時時間(時鐘節拍數)
OS_OPT opt, //決定任務等待的條件
CPU_TS *p_ts, //指向一個時間戳
OS_ERR *p_err)
{
CPU_BOOLEAN consume;
OS_FLAGS flags_rdy;
OS_OPT mode;
OS_PEND_DATA pend_data;
CPU_SR_ALLOC();
if ((opt & OS_OPT_PEND_FLAG_CONSUME) != (OS_OPT)0) { /* See if we need to consume the flags */
consume = DEF_TRUE;
} else {
consume = DEF_FALSE;
}
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS)0; /* Initialize the returned timestamp */
}
mode = opt & OS_OPT_PEND_FLAG_MASK;
CPU_CRITICAL_ENTER();
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL: /* See if all required flags are set */
flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); /* Extract only the bits we want */
if (flags_rdy == flags) { /* Must match ALL the bits that we want */
if (consume == DEF_TRUE) { /* See if we need to consume the flags */
p_grp->Flags &= ~flags_rdy; /* Clear ONLY the flags that we wanted */
}
OSTCBCurPtr->FlagsRdy = flags_rdy; /* Save flags that were ready */
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); /* Yes, condition met, return to caller */
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { /* Block task until events occur or timeout */
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK; /* Specified non-blocking so task would block */
return ((OS_FLAGS)0);
} else { /* Specified blocking so check is scheduler is locked */
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* See if called with scheduler locked ... */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED; /* ... can't PEND when locked */
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); /* Lock the scheduler/re-enable interrupts */
OS_FlagBlock(&pend_data,
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
}
break;
case OS_OPT_PEND_FLAG_SET_ANY:
flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); /* Extract only the bits we want */
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag set */
if (consume == DEF_TRUE) { /* See if we need to consume the flags */
p_grp->Flags &= ~flags_rdy; /* Clear ONLY the flags that we got */
}
OSTCBCurPtr->FlagsRdy = flags_rdy; /* Save flags that were ready */
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); /* Yes, condition met, return to caller */
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { /* Block task until events occur or timeout */
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK; /* Specified non-blocking so task would block */
return ((OS_FLAGS)0);
} else { /* Specified blocking so check is scheduler is locked */
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* See if called with scheduler locked ... */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED; /* ... can't PEND when locked */
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); /* Lock the scheduler/re-enable interrupts */
OS_FlagBlock(&pend_data,
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
}
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
OSSched(); /* Find next HPT ready to run */
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: /* We got the vent flags */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: /* Indicate that we aborted */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_ABORT;
return ((OS_FLAGS)0);
case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn't get semaphore within timeout */
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TIMEOUT;
return ((OS_FLAGS)0);
case OS_STATUS_PEND_DEL: /* Indicate that object pended on has been deleted */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OBJ_DEL;
return ((OS_FLAGS)0);
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATUS_INVALID;
return ((OS_FLAGS)0);
}
flags_rdy = OSTCBCurPtr->FlagsRdy;
if (consume == DEF_TRUE) { /* See if we need to consume the flags */
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY: /* Clear ONLY the flags we got */
p_grp->Flags &= ~flags_rdy;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
}
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE; /* Event(s) must have occurred */
return (flags_rdy);
}
flags:bit序列,任務需要等待事件标志組的哪個位就把這個序列對應的位置1
opt:決定任務等待的條件是所有标志置位
向事件标志組釋出标志
調用函數OSFlagPost()可以對事件标志組進行置位或清零
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, //指向事件标志組
OS_FLAGS flags, //決定對哪些位清零和置位
OS_OPT opt, //決定對标志位的操作
OS_ERR *p_err)
{
OS_FLAGS flags_cur;
CPU_TS ts;
ts = OS_TS_GET(); /* Get timestamp */
flags_cur = OS_FlagPost(p_grp,
flags,
opt,
ts,
p_err);
return (flags_cur);
}
flags:決定對哪些位清零和置位
opt:決定對flags標明的标志位的操作
小結
ISR和任務都可以通過三種方式向一個或者多個任務發信号:信号量,任務信号量,事件标志組。
當任務需要與一個或者多個事件的發生同步時,可以使用事件标志組。但是事件标志不支援信用記錄功能,因為在事件标志組中的每個事件用一個資料位表示。