Event Control Blocks
- 1 Use of Event Control Blocks
- 2 Placing a Task in the ECB Wait List
- 3 Removing a Task from an ECB Wait List
- 4 Finding the Highest Priority Task Waiting on an ECB
- 5 List of Free ECBs
- 6 Initializing an ECB, OS_EventWaitListInit()
- 7 Making a Task Ready, OS_EventTaskRdy()
- 8 Making a Task Wait for an Event, OS_EventTaskWait()
Use of Event Control Blocks
Figure 6.1 shows how tasks and Interrupt Service Routines (ISRs) can interact with each other. A task or an ISR signals a task through a kernel object called an Event Control Block (ECB). The signal is considered to be an event, which explains my choice of this name.
(A1) An ISR or a task can signal an ECB.
(A2) Only a task can wait for another task or an ISR to signal the object. An ISR is not allowed to wait on an ECB.
(A3) An optional timeout can be specified by the waiting task in case the object is not signaled within a specified time period.
(B) Multiple tasks can wait for a task or an ISR to signal an ECB. When the ECB is signaled, only the highest priority task waiting on the ECB will be “signaled” and made ready to run. An ECB can be either a semaphore, a message mailbox, or a message queue, as discussed later.
(C4) When an ECB is used as a semaphore, tasks can both wait on and signal the ECB.
An ECB is used as a building block to implement services such as Semaphores (chapter 7), Mutual Exclusion Semaphores (chapter 8), Message Mailboxes (chapter 10) and Message Queues (chapter 11).
µC/OS-II maintains the state of an ECB in a data structure called OS_EVENT (see uCOS_II.H). The state of an event consists of the event itself (a counter for a semaphore, a bit for a mutex, a pointer for a message mailbox, or an array of pointers for a queue) and a list of tasks waiting for the event to occur. Each semaphore, mutual exclusion semaphore, message mailbox, and message queue is assigned an ECB. The data structure for an ECB is shown in Listing 6.1 and also graphically in Figure 6.2.
Listing - Listing 6.1 Event control block data structure
typedef struct {
INT8U OSEventType; /* Event type */
void *OSEventPtr; /* Ptr to message or queue structure */
INT16U OSEventCnt; /* Count (when event is a semaphore) */
OS_PRIO OSEventGrp; /* Group for wait list */
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* Wait list for event to occur */
#if OS_EVENT_NAME_EN > 0u
INT8U *OSEventName;
#endif
} OS_EVENT;.OSEventType
contains the type associated with the ECB and can have the following values: OS_EVENT_TYPE_SEM, OS_EVENT_TYPE_MUTEX, OS_EVENT_TYPE_MBOX, or OS_EVENT_TYPE_Q. This field is used to make sure you are accessing the proper object when you perform operations on these objects through µC/OS-II’s service calls. .OSEventType is the first field (and first byte) of the data structure. This allows run-time checking to determine whether the pointer points to an ECB or an event flag (see Chapter 9).
.OSEventPtr
is only used when the ECB is assigned to a message mailbox or a message queue. It points to the message when used for a mailbox or to a data structure when used for a queue (see Chapter 10, Message Mailboxes, and Chapter 11, Message Queues).
.OSEventCnt
is used to hold the semaphore count when the ECB is used for a semaphore (see Chapter 7, Semaphores) or the mutex and PIP when the ECB is used for a mutex (see Chapter 8, Mutual Exclusion Semaphores).
.OSEventTbl[] and .OSEventGrp
are similar to OSRdyTbl[] and OSRdyGrp, respectively, except that they contain a list of tasks waiting on the event instead of a list of tasks ready to run (see section 3.??, Ready List).
Each task that needs to wait for the event to occur is placed in the wait list consisting of the two variables, .OSEventGrp and .OSEventTbl[]. Note that I used a dot (.) in front of the variable name to indicate that the variable is part of a data structure. Task priorities are grouped (eight tasks per group) in .OSEventGrp. Each bit in.OSEventGrp is used to indicate when any task in a group is waiting for the event to occur. When a task is waiting, its corresponding bit is set in the wait table,.OSEventTbl[]. The size (in bytes) of .OSEventTbl[] depends on OS_LOWEST_PRIO (see uCOS_II.H). This allows µC/OS-II to reduce the amount of RAM (i.e., data space) when your application requires just a few task priorities.
The task that is resumed when the event occurs is the highest priority task waiting for the event and corresponds to the lowest priority number that has a bit set inOSEventTbl[]. The relationship between .OSEventGrp and .OSEventTbl[] is shown in Figure 6.3 and is given by the following rules.
Bit 0 in .OSEventGrp is 1 when any bit in .OSEventTbl[0] is 1.
Bit 1 in .OSEventGrp is 1 when any bit in .OSEventTbl[1] is 1.
Bit 2 in .OSEventGrp is 1 when any bit in .OSEventTbl[2] is 1.
Bit 3 in .OSEventGrp is 1 when any bit in .OSEventTbl[3] is 1.
Bit 4 in .OSEventGrp is 1 when any bit in .OSEventTbl[4] is 1.
Bit 5 in .OSEventGrp is 1 when any bit in .OSEventTbl[5] is 1.
Bit 6 in .OSEventGrp is 1 when any bit in .OSEventTbl[6] is 1.
Bit 7 in .OSEventGrp is 1 when any bit in .OSEventTbl[7] is 1.
Etc.
Placing a Task in the ECB Wait List
The following code places a task in the wait list:
Listing - Listing 6.2 Making a task wait for an event
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;You should realize from Listing 6.2 that the time required to insert a task in the wait list is constant and does not depend on how many tasks are in your system. Also, from Figure 6.3, the lower 3 bits of the task’s priority are used to determine the bit position in .OSEventTbl[] , and the next three most significant bits are used to determine the index into OSEventTbl[] . Note that OSMapTbl[] (see OS_CORE.C ) is a table in ROM, used to equate an index from 0 to 7 to a bit mask as shown in the Table 6.1.
Index | Bit Mask (Binary) |
|---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
Removing a Task from an ECB Wait List
A task is removed from the wait list by reversing the process (Listing 6.3).
Listing - Listing 6.3 Removing a task from a wait list
INT8U y;
y = ptcb->OSTCBY;
pevent->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
if (pevent->OSEventTbl[y] == 0u) {
pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}This code clears the bit corresponding to the task in .OSEventTbl[] and clears the bit in .OSEventGrp only if all tasks in a group are not waiting.
Finding the Highest Priority Task Waiting on an ECB
The code to find the highest priority task waiting for an event to occur is shown in Listing 6.4. Table lookups are again used for performance reasons because we don’t want to scan the .OSEventTbl[] one bit at a time to locate the highest priority task waiting on the event.