...
µC/OS-II event flags consist of two elements: a series of bits (either 8, 16 or 32) used to hold the current state of the events in the group, and a list of tasks waiting for a combination of these bits to either be set (1) or cleared (0). µC/OS-II provides six services to access semaphores: OSFlagAccept()
, OSFlagCreate()
, OSFlagDel()
, OSFlagPend()
, OSFlagPost()
and OSFlagQuery()
.
To enable µC/OS-II event flags services, you must set the configuration constants in OS_CFG.H
. Specifically, table 9.1 shows which services are compiled based on the value of configuration constants found in OS_CFG.H
. You should note that NONE of the event flag services are enabled when OS_FLAG_EN
is set to 0. To enable the feature (i.e. service), simply set the configuration constant to 1. You will notice that OSFlagCreate()
, OSFlagPend()
and OSFlagPost()
cannot be individually disabled like the other services because they are always needed when you enable µC/OS-II event flag management.
Anchor |
---|
OS_FLAGS
|
OSFlagPend()
call.As you can see from Figure 9.1, a task or an ISR can call OSFlagAccept()
, OSFlagPost()
or OSFlagQuery()
. However, only tasks are allowed to call OSFlagCreate()
, OSFlagDel()
or OSFlagPend()
.
Event Flag Internals
A µC/OS-II's event flag group consist of three elements as shown in the OS_FLAG_GRP
structure below.
You should note that the wait list for event flags is different than the other wait lists in µC/OS-II. With event flags, the wait list is accomplished through a doubly linked list as shown in figure 9.2. Three data structures are involved. OS_FLAG_GRP
(mentioned above), OS_TCB
which is the task control block and OS_FLAG_NODE
which is used to keep track of which bits the task is waiting for and what type of wait (AND or OR). As you can see, there are a lot of pointers involved.
An OS_FLAG_NODE
is created when a task desires to wait on bits of an event flag group and the node is ‘destroyed’ when the event(s) occur. In other words, a node is created by OSFlagPend()
as we will see shortly. Before we discuss this, let’s look at the OS_FLAG_NODE
data structure.
You should note that AND and ALL means the same thing and either one can be used. I prefer to use OS_FLAG_WAIT_???_ALL
because it’s more obvious but you are certainly welcomed to use OS_FLAG_WAIT_???_AND
. Similarly, OR or ANY means the same thing and either one can be used. Again, I prefer to use OS_FLAG_WAIT_???_ANY
because it’s more obvious but again, you can use OS_FLAG_WAIT_???_OR
. The other thing to notice is that you can wait for either bits to be SET or CLEARED.
Creating an Event Flag Group, OSFlagCreate()
The code to create an event flag group is shown in listing 9.3.
Deleting an Event Flag Group, OSFlagDel()
The code to delete an event flag group is shown in listing 9.4.
This is a function you should use with caution because multiple tasks could attempt to access a deleted event flag group. You should always use this function with great care. Generally speaking, before you would delete an event flag group, you would first delete all the tasks that access the event flag group.
Waiting for event(s) of an Event Flag Group, OSFlagPend()
The code to wait for event(s) of an event flag group is shown in listing 9.5.
As mentioned above, if the desired bits and conditions of a PEND call are not satisfied the calling task is suspended until either the event or a timeout occurs. The task is suspended by OS_FlagBlock()
(see Listing 9.6) which adds the calling task to the wait list of the event flag group. The process is shown in Figure 9.4.
Setting or Clearing event(s) in an Event Flag Group, OSFlagPost()
The code to either setting or clearing bits in an event flag group is done by calling OSFlagPost()
and the code for this function is shown in listing 9.7.
As previously mentioned, the code in listing 9.8 is executed to make a task ready-to-run.
The unlinking of the OS_FLAG_NODE
is performed by the function OS_FlagUnlink()
as shown in listing 9.9. Figure 9.5 shows the four possible locations of an OS_FLAG_NODE
which needs to be removed from the event flag wait list. This is a classical doubly linked list removal problem except that there are also other pointers to adjust.
Figures 9.6 through 9.9 shows the before and after for each case mentioned. The number in parenthesis corresponds to the number in parenthesis of listing 9.9. You will notice that OS_FlagUnlink()
updates at most three pointers. Because the node being removed exist on the stack of the task that is being readied (it was allocated by OSFlagPend()
), that node will automatically disappear! As far as the task that pended on the event flag is concerned, it doesn’t even know about the OS_FLAG_NODE
.
Looking for event(s) of an Event Flag Group, OSFlagAccept()
The code to look for desired event(s) from an event flag group without waiting is shown in listing 9.10. This function is quite similar to OSFlagPend()
except that the caller will not be suspended (i.e. blocked) should the event(s) not be present. The only two things that are different are:
OSFlagAccept()
can be called from an ISR unlike some of the other calls.- If the conditions are NOT met, the call does not block and simply returns an error code that the caller should check.
Querying an Event Flag Group, OSFlagQuery()
OSFlagQuery()
allows your code to get the current value of the event flag group. The code for this function is shown in listing 9.11
|
Panel | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||
|
Figure 9.1 shows a flow diagram to illustrate the relationship between tasks, ISRs, and a event flags. Note that the symbology used to represent an event flag group is a series of 8 bits even though the event flag group can contain 8, 16 or 32 bits (see OS_FLAGS
in OS_CFG.H
). The hourglass represents a timeout that can be specified with the OSFlagPend()
call.
As you can see from Figure 9.1, a task or an ISR can call OSFlagAccept()
, OSFlagPost()
or OSFlagQuery()
. However, only tasks are allowed to call OSFlagCreate()
, OSFlagDel()
or OSFlagPend()
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Event Flag Internals
A µC/OS-II's event flag group consist of three elements as shown in the OS_FLAG_GRP
structure below.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
typedef struct {
INT8U OSFlagType; (1)
void *OSFlagWaitList; (2)
OS_FLAGS OSFlagFlags; (3)
} OS_FLAG_GRP; |
Panel | ||
---|---|---|
| ||
(1) (2) (3) |
You should note that the wait list for event flags is different than the other wait lists in µC/OS-II. With event flags, the wait list is accomplished through a doubly linked list as shown in figure 9.2. Three data structures are involved. OS_FLAG_GRP
(mentioned above), OS_TCB
which is the task control block and OS_FLAG_NODE
which is used to keep track of which bits the task is waiting for and what type of wait (AND or OR). As you can see, there are a lot of pointers involved.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
An OS_FLAG_NODE
is created when a task desires to wait on bits of an event flag group and the node is ‘destroyed’ when the event(s) occur. In other words, a node is created by OSFlagPend()
as we will see shortly. Before we discuss this, let’s look at the OS_FLAG_NODE
data structure.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
typedef struct {
void *OSFlagNodeNext; (1)
void *OSFlagNodePrev;
void *OSFlagNodeTCB; (2)
void *OSFlagNodeFlagGrp; (3)
OS_FLAGS OSFlagNodeFlags; (4)
INT8U OSFlagNodeWaitType; (5)
} OS_FLAG_NODE; |
Panel | ||
---|---|---|
| ||
(1) The (2) (3) (4) The (5) The last member of the OS_FLAG_WAIT_CLR_ALL
OS_FLAG_WAIT_CLR_AND
OS_FLAG_WAIT_CLR_ANY
OS_FLAG_WAIT_CLR_OR
OS_FLAG_WAIT_SET_ALL
OS_FLAG_WAIT_SET_AND
OS_FLAG_WAIT_SET_ANY
OS_FLAG_WAIT_SET_OR |
You should note that AND and ALL means the same thing and either one can be used. I prefer to use OS_FLAG_WAIT_???_ALL
because it’s more obvious but you are certainly welcomed to use OS_FLAG_WAIT_???_AND
. Similarly, OR or ANY means the same thing and either one can be used. Again, I prefer to use OS_FLAG_WAIT_???_ANY
because it’s more obvious but again, you can use OS_FLAG_WAIT_???_OR
. The other thing to notice is that you can wait for either bits to be SET or CLEARED.
Creating an Event Flag Group, OSFlagCreate()
The code to create an event flag group is shown in listing 9.3.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAG_GRP *OSFlagCreate (OS_FLAGS flags, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_FLAG_GRP *pgrp;
if (OSIntNesting > 0) { (1)
*err = OS_ERR_CREATE_ISR;
return ((OS_FLAG_GRP *)0);
}
OS_ENTER_CRITICAL();
pgrp = OSFlagFreeList; (2)
if (pgrp != (OS_FLAG_GRP *)0) { (3)
(4)
OSFlagFreeList = (OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;
pgrp->OSFlagType = OS_EVENT_TYPE_FLAG; (5)
pgrp->OSFlagFlags = flags; (6)
pgrp->OSFlagWaitList = (void *)0; (7)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else {
OS_EXIT_CRITICAL();
*err = OS_FLAG_GRP_DEPLETED;
}
return (pgrp); (8)
} |
Panel | |||||||
---|---|---|---|---|---|---|---|
| |||||||
(1) (2) (3) An non-NULL pointer indicates that an event flag group is available. (4) Once a group is allocated, the free list pointer is adjusted. Note that the number of Event Flag Groups that you can create is determined by the #define constant (5) (6) (7) Because we are creating the group, there are no tasks waiting on the group and thus, the wait list pointer is initialized to NULL. (8) The pointer to the created event flag group is returned. If there were no more groups available,
|
Deleting an Event Flag Group, OSFlagDel()
The code to delete an event flag group is shown in listing 9.4.
This is a function you should use with caution because multiple tasks could attempt to access a deleted event flag group. You should always use this function with great care. Generally speaking, before you would delete an event flag group, you would first delete all the tasks that access the event flag group.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAG_GRP *OSFlagDel (OS_FLAG_GRP *pgrp, INT8U opt, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
BOOLEAN tasks_waiting;
OS_FLAG_NODE *pnode;
if (OSIntNesting > 0) { (1)
*err = OS_ERR_DEL_ISR;
return (pgrp);
}
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { (2)
*err = OS_FLAG_INVALID_PGRP;
return (pgrp);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { (3)
*err = OS_ERR_EVENT_TYPE;
return (pgrp);
}
#endif
OS_ENTER_CRITICAL();
if (pgrp->OSFlagWaitList != (void *)0) { (4)
tasks_waiting = TRUE;
} else {
tasks_waiting = FALSE;
}
switch (opt) {
case OS_DEL_NO_PEND: (5)
if (tasks_waiting == FALSE) {
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList; (6)
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0); (7)
} else {
OS_EXIT_CRITICAL();
*err = OS_ERR_TASK_WAITING;
return (pgrp);
}
case OS_DEL_ALWAYS: (8)
pnode = pgrp->OSFlagWaitList;
while (pnode != (OS_FLAG_NODE *)0) { (9)
OS_FlagTaskRdy(pnode, (OS_FLAGS)0);
pnode = pnode->OSFlagNodeNext;
}
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList; (10)
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
if (tasks_waiting == TRUE) { (11)
OS_Sched();
}
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0); (12)
default:
OS_EXIT_CRITICAL();
*err = OS_ERR_INVALID_OPT;
return (pgrp);
}
} |
Panel | ||
---|---|---|
| ||
(1) (2) & (3) We then validate the arguments passed to (4) Based on the option (i.e. opt) passed in the call, (5) & (6) When opt is set to (7) You will note that (8) & (9) When opt is set to (10) Once all pending tasks are readied, (11) The scheduler is called only if there were tasks waiting on the event flag group. (12) You will note that |
Waiting for event(s) of an Event Flag Group, OSFlagPend()
The code to wait for event(s) of an event flag group is shown in listing 9.5.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_FLAG_NODE node;
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
BOOLEAN consume;
if (OSIntNesting > 0) { (1)
*err = OS_ERR_PEND_ISR;
return ((OS_FLAGS)0);
}
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { (2)
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { (3)
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
if (wait_type & OS_FLAG_CONSUME) { (4)
wait_type &= ~OS_FLAG_CONSUME;
consume = TRUE;
} else {
consume = FALSE;
}
OS_ENTER_CRITICAL();
switch (wait_type) { (5)
case OS_FLAG_WAIT_SET_ALL:
flags_rdy = pgrp->OSFlagFlags & flags; (6)
if (flags_rdy == flags) { (7)
if (consume == TRUE) { (8)
pgrp->OSFlagFlags &= ~flags_rdy; (9)
}
flags_cur = pgrp->OSFlagFlags; (10)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur); (11)
} else { (12)
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
case OS_FLAG_WAIT_SET_ANY:
flags_rdy = pgrp->OSFlagFlags & flags; (13)
if (flags_rdy != (OS_FLAGS)0) { (14)
if (consume == TRUE) { (15)
pgrp->OSFlagFlags &= ~flags_rdy; (16)
}
flags_cur = pgrp->OSFlagFlags; (17)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur); (18)
} else { (19)
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0
case OS_FLAG_WAIT_CLR_ALL:
flags_rdy = ~pgrp->OSFlagFlags & flags;
if (flags_rdy == flags) {
if (consume == TRUE) {
pgrp->OSFlagFlags |= flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur);
} else {
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
case OS_FLAG_WAIT_CLR_ANY:
flags_rdy = ~pgrp->OSFlagFlags & flags;
if (flags_rdy != (OS_FLAGS)0) {
if (consume == TRUE) {
pgrp->OSFlagFlags |= flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur);
} else {
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#endif
default:
OS_EXIT_CRITICAL();
flags_cur = (OS_FLAGS)0;
*err = OS_FLAG_ERR_WAIT_TYPE;
return (flags_cur);
}
OS_Sched(); (20)
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStat & OS_STAT_FLAG) { (21)
OS_FlagUnlink(&node); (22)
OSTCBCur->OSTCBStat = OS_STAT_RDY;
OS_EXIT_CRITICAL();
flags_cur = (OS_FLAGS)0;
*err = OS_TIMEOUT;
} else {
if (consume == TRUE) { (23)
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
case OS_FLAG_WAIT_SET_ANY: (24)
pgrp->OSFlagFlags &= ~OSTCBCur->OSTCBFlagsRdy;
break;
case OS_FLAG_WAIT_CLR_ALL:
case OS_FLAG_WAIT_CLR_ANY:
pgrp->OSFlagFlags |= OSTCBCur->OSTCBFlagsRdy;
break;
}
}
flags_cur = pgrp->OSFlagFlags; (25)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
return (flags_cur);
} |
Panel | ||
---|---|---|
| ||
(1) Like all µC/OS-II pend calls, (2) & (3) Assuming that the configuration constant
OSFlagPend(OSFlagMyGrp,
(OS_FLAGS)0x01,
FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
0,
&err); (4) Because the ‘consumption’ of the flag(s) is done later in the code, (5)
The last two choices are identical to the first two choices except that Wait for ALL of the specified bits to be SET: (6) When wait_type is set to either (7) If all the bits extracted matches the bits that you specified in the flags argument then, the event flags that the task wants are all set and thus, the PEND call would return to the caller. (8) & (9) Before we return, we need to determine whether we need to ‘consume’ the flags and if so, we will CLEAR all the flags that satisfied the condition. (10) & (11) The new value of the event flag group is obtained and returned to the caller. (12) If ALL the desired bits in the event flag group were not SET then the calling task will block (i.e. suspend) until ALL the bits are either SET or a timeout occurs. Instead of repeating code for all four types of wait, I created a function ( Wait for ANY of the specified bits to be SET: (13) When wait_type is set to either (14) If any of the bits extracted matches the bits that you specified in the flags argument then the PEND call will return to the caller. (15) & (16) Before we return, we need to determine whether we need to ‘consume’ the flag(s) and if so, we need to CLEAR all the flag(s) that satisfied the condition. (17) & (18) The new value of the event flag group is obtained and returned to the caller. (19) If NONE of the desired bits in the event flag group were not SET then the calling task will block (i.e. suspend) until ANY of the bits is either SET or a timeout occurs. |
As mentioned above, if the desired bits and conditions of a PEND call are not satisfied the calling task is suspended until either the event or a timeout occurs. The task is suspended by OS_FlagBlock()
(see Listing 9.6) which adds the calling task to the wait list of the event flag group. The process is shown in Figure 9.4.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
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; (1)
OSTCBCur->OSTCBDly = timeout;
#if OS_TASK_DEL_EN > 0
OSTCBCur->OSTCBFlagNode = pnode; (2)
#endif
pnode->OSFlagNodeFlags = flags; (3)
pnode->OSFlagNodeWaitType = wait_type;
pnode->OSFlagNodeTCB = (void *)OSTCBCur; (4)
pnode->OSFlagNodeNext = pgrp->OSFlagWaitList; (5)
pnode->OSFlagNodePrev = (void *)0; (6)
pnode->OSFlagNodeFlagGrp = (void *)pgrp; (7)
pnode_next = pgrp->OSFlagWaitList;
if (pnode_next != (void *)0) {
pnode_next->OSFlagNodePrev = pnode; (8)
}
pgrp->OSFlagWaitList = (void *)pnode; (9)
(10)
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
} |
Panel | ||
---|---|---|
| ||
The notes below apply both and simultaneously to Listing 9.6 and Figure 9.4. When reading each numbered note, refer to both the listing and the figure. (1) (2) We then link the (3) Next, (4) We then link the TCB to the (5) The (6) You should note that the (7) We then link the event flag group to the (8) (9) & (10) Finally, the pointer of the beginning of the wait list is updated to point to the new You should note that interrupts are disabled during the process of blocking the calling task. (20) When (21) When µC/OS-II resumes the calling task, (22) In this case, the (23) & (24) If the calling task is NOT resumed because of a timeout then, it MUST have been resumed because the event flags that it was waiting for have been either set or cleared. In this case, we determine whether the calling task wanted to consume the event flags. If this is the case, the appropriate flags are either set or cleared based on the wait type. (25) Finally, |
Setting or Clearing event(s) in an Event Flag Group, OSFlagPost()
The code to either setting or clearing bits in an event flag group is done by calling OSFlagPost()
and the code for this function is shown in listing 9.7.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAGS OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_FLAG_NODE *pnode;
BOOLEAN sched;
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { (1)
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { (2)
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
OS_ENTER_CRITICAL();
switch (opt) { (3)
case OS_FLAG_CLR:
pgrp->OSFlagFlags &= ~flags; (4)
break;
case OS_FLAG_SET:
pgrp->OSFlagFlags |= flags; (5)
break;
default:
OS_EXIT_CRITICAL();
*err = OS_FLAG_INVALID_OPT;
return ((OS_FLAGS)0);
}
sched = FALSE; (6)
pnode = pgrp->OSFlagWaitList;
while (pnode != (OS_FLAG_NODE *)0) { (7)
switch (pnode->OSFlagNodeWaitType) {
case OS_FLAG_WAIT_SET_ALL: (8)
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy == pnode->OSFlagNodeFlags) { (9)
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) { (10)
sched = TRUE; (11)
}
}
break;
case OS_FLAG_WAIT_SET_ANY:
flags_rdy = pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy != (OS_FLAGS)0) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) {
sched = TRUE;
}
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0
case OS_FLAG_WAIT_CLR_ALL:
flags_rdy = ~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy == pnode->OSFlagNodeFlags) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) {
sched = TRUE;
}
}
break;
case OS_FLAG_WAIT_CLR_ANY:
flags_rdy = ~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
if (flags_rdy != (OS_FLAGS)0) {
if (OS_FlagTaskRdy(pnode, flags_rdy) == TRUE) {
sched = TRUE;
}
}
break;
#endif
}
pnode = pnode->OSFlagNodeNext; (12)
}
OS_EXIT_CRITICAL();
if (sched == TRUE) { (13)
OS_Sched(); (14)
}
OS_ENTER_CRITICAL();
flags_cur = pgrp->OSFlagFlags; (15)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags_cur); (16)
} |
Panel | ||
---|---|---|
| ||
(1) & (2) Assuming that the configuration constant (3) & (4) & (5) Depending on the option you specified in the opt argument of (6) We next start by assuming that POSTing doesn’t make a higher priority task ready-to-run and thus, we set the BOOLEAN variable sched to FALSE. If this assumption is not verified because we will make a higher-priority-task ready-to-run then sched will simply be set to TRUE. (7) We then go through the wait list to see if any task is waiting on one or more events. (15) & (16) If the wait list is empty, we simply get the current state of the event flag bits and return this information to the caller. (8) If there is one or more tasks waiting on the event flag group, we go through the list of
(9) & (10) Note that the last two condition can be ‘compiled-out’ by setting (11) Because a task is made ready-to-run, the scheduler will have to be called. However, we will only do this after going through all waiting tasks because, there is no need to call the scheduler every time a task is made ready-to-run. (12) We proceed to the next node by following the linked list. You should note that interrupts are disabled while we are going through the wait list. The implication is that (13) & (14) When we have gone through the whole waiting list, we examine the sched flag to see if we need to run the scheduler and thus possibly perform a context switch to a higher priority task that just received the event flag(s) it was waiting for. (15) & (16) |
As previously mentioned, the code in listing 9.8 is executed to make a task ready-to-run.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
static BOOLEAN OS_FlagTaskRdy (OS_FLAG_NODE *pnode, OS_FLAGS flags_rdy)
{
OS_TCB *ptcb;
BOOLEAN sched;
ptcb = (OS_TCB *)pnode->OSFlagNodeTCB;
ptcb->OSTCBDly = 0;
ptcb->OSTCBFlagsRdy = flags_rdy;
ptcb->OSTCBStat &= ~OS_STAT_FLAG;
if (ptcb->OSTCBStat == OS_STAT_RDY) { (1)
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
sched = TRUE; (2)
} else {
sched = FALSE; (3)
}
OS_FlagUnlink(pnode); (4)
return (sched);
} |
Panel | ||
---|---|---|
| ||
(1) & (2) & (3) Note that even though this function ‘removes’ the waiting task from the event flag group wait list, the task could still be suspended and may not be ready-to-run. This is why the BOOLEAN variable sched is used and returned to the caller. (4) This is a standard procedure in µC/OS-II (see section 6.02, Making a Task Ready) except for the fact that the |
The unlinking of the OS_FLAG_NODE
is performed by the function OS_FlagUnlink()
as shown in listing 9.9. Figure 9.5 shows the four possible locations of an OS_FLAG_NODE
which needs to be removed from the event flag wait list. This is a classical doubly linked list removal problem except that there are also other pointers to adjust.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
void OS_FlagUnlink (OS_FLAG_NODE *pnode)
{
#if OS_TASK_DEL_EN > 0
OS_TCB *ptcb;
#endif
OS_FLAG_GRP *pgrp;
OS_FLAG_NODE *pnode_prev;
OS_FLAG_NODE *pnode_next;
pnode_prev = pnode->OSFlagNodePrev; (1)
pnode_next = pnode->OSFlagNodeNext; (2)
if (pnode_prev == (OS_FLAG_NODE *)0) { (3)
pgrp = pnode->OSFlagNodeFlagGrp; (4)
pgrp->OSFlagWaitList = (void *)pnode_next; (5)
if (pnode_next != (OS_FLAG_NODE *)0) { (6)
pnode_next->OSFlagNodePrev = (OS_FLAG_NODE *)0; (7)
}
} else {
pnode_prev->OSFlagNodeNext = pnode_next; (8)
if (pnode_next != (OS_FLAG_NODE *)0) { (9)
pnode_next->OSFlagNodePrev = pnode_prev; (10)
}
}
#if OS_TASK_DEL_EN > 0
ptcb = (OS_TCB *)pnode->OSFlagNodeTCB; (11)
ptcb->OSTCBFlagNode = (void *)0; (12)
#endif
} |
Panel | ||
---|---|---|
| ||
(1) & (2) (3) & F9.5(A, B) The previous pointer is examined to see if we have the first two cases of figure 9.6 (an (4) & (5) If the (6) & (7) F9.5(B) If there is an (8) & F9.5(C, D) Because the node to delete is not the first node in the wait list, the node to the left of the node to delete must now point to the node to the right of the node to delete. (9) & (10) If there is a node to the right of the node to delete, the previous pointer of that node must now point to the previous node of the node to delete. (11) & (12) In all cases, the |
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Figures 9.6 through 9.9 shows the before and after for each case mentioned. The number in parenthesis corresponds to the number in parenthesis of listing 9.9. You will notice that OS_FlagUnlink()
updates at most three pointers. Because the node being removed exist on the stack of the task that is being readied (it was allocated by OSFlagPend()
), that node will automatically disappear! As far as the task that pended on the event flag is concerned, it doesn’t even know about the OS_FLAG_NODE
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Anchor Figure - Figure 9.7, Removing an OS_FLAG_NODE from the wait list, Case B Figure - Figure 9.7, Removing an OS_FLAG_NODE from the wait list, Case B
Panel | ||||
---|---|---|---|---|
| ||||
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Looking for event(s) of an Event Flag Group, OSFlagAccept()
The code to look for desired event(s) from an event flag group without waiting is shown in listing 9.10. This function is quite similar to OSFlagPend()
except that the caller will not be suspended (i.e. blocked) should the event(s) not be present. The only two things that are different are:
OSFlagAccept()
can be called from an ISR unlike some of the other calls.- If the conditions are NOT met, the call does not block and simply returns an error code that the caller should check.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAGS OSFlagAccept (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
BOOLEAN consume;
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) {
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) {
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
if (wait_type & OS_FLAG_CONSUME) {
wait_type &= ~OS_FLAG_CONSUME;
consume = TRUE;
} else {
consume = FALSE;
}
OS_ENTER_CRITICAL();
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
flags_rdy = pgrp->OSFlagFlags & flags;
if (flags_rdy == flags) {
if (consume == TRUE) {
pgrp->OSFlagFlags &= ~flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else {
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_FLAG_ERR_NOT_RDY;
}
break;
case OS_FLAG_WAIT_SET_ANY:
flags_rdy = pgrp->OSFlagFlags & flags;
if (flags_rdy != (OS_FLAGS)0) {
if (consume == TRUE) {
pgrp->OSFlagFlags &= ~flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else {
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_FLAG_ERR_NOT_RDY;
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0
case OS_FLAG_WAIT_CLR_ALL:
flags_rdy = ~pgrp->OSFlagFlags & flags;
if (flags_rdy == flags) {
if (consume == TRUE) {
pgrp->OSFlagFlags |= flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else {
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_FLAG_ERR_NOT_RDY;
}
break;
case OS_FLAG_WAIT_CLR_ANY:
flags_rdy = ~pgrp->OSFlagFlags & flags;
if (flags_rdy != (OS_FLAGS)0) {
if (consume == TRUE) {
pgrp->OSFlagFlags |= flags_rdy;
}
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else {
flags_cur = pgrp->OSFlagFlags;
OS_EXIT_CRITICAL();
*err = OS_FLAG_ERR_NOT_RDY;
}
break;
#endif
default:
OS_EXIT_CRITICAL();
flags_cur = (OS_FLAGS)0;
*err = OS_FLAG_ERR_WAIT_TYPE;
break;
}
return (flags_cur);
} |
Querying an Event Flag Group, OSFlagQuery()
OSFlagQuery()
allows your code to get the current value of the event flag group. The code for this function is shown in listing 9.11.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
OS_FLAGS OSFlagQuery (OS_FLAG_GRP *pgrp, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
OS_FLAGS flags;
#if OS_ARG_CHK_EN > 0
if (pgrp == (OS_FLAG_GRP *)0) { (1)
*err = OS_FLAG_INVALID_PGRP;
return ((OS_FLAGS)0);
}
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG) { (2)
*err = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
#endif
OS_ENTER_CRITICAL();
flags = pgrp->OSFlagFlags; (3)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return (flags); (4)
} |
Panel | ||
---|---|---|
| ||
(1) & (2) As with all µC/OS-II calls, (3) & (4) If there are no errors, |
OSFlagQuery()
is passed two arguments: pgrp contains a pointer to the event flag group which was returned by OSFlagCreate()
when the event flag group is created and, err which is a pointer to an error code that will let the caller know whether the call was successful or not.
...