📜 ⬆️ ⬇️

The whole truth about the RTOS. Article # 17. Event flag groups: introduction and basic services



Groups of event flags have already been mentioned earlier in a previous article (# 5). In Nucleus SE, they look like signals, but are more flexible. They provide a low-cost and flexible way to transfer simple messages between tasks.


Previous articles in the series:
Article # 16. Signals
Article # 15. Memory sections: services and data structures
Article # 14. Memory sections: introduction and basic services
Article # 13. Task data structures and unsupported API calls
Article # 12. Task Services
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: additional features and context preservation
Article # 9. Scheduler: implementation
Article # 8. Nucleus SE: Inside and Deployment
Article # 7. Nucleus SE: introduction
Article # 6. Other RTOS services
Article # 5. Interaction between tasks and synchronization
Article # 4. Tasks, context switching and interrupts
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.

')

Using event flags


In Nucleus SE, event flags are defined at the build stage. The maximum number of event flag groups in the application is 16. If the event flag groups are not defined, the code related to the data structures and service calls of the event flag groups will not be included in the application.

A group of event flags is a set of eight bit flags, access to which is regulated in such a way that several tasks can be safely used by one flag. One task can set or clear any combination of event flags. Another task can read a group of flags at any time, and can also wait for a specific sequence of flags (by polling or with suspension).

Setting groups of event flags


Number of event flag groups


As with most Nucleus SE objects, the setting of groups of event flags is specified by the #define directives in nuse_config.h . The main parameter is NUSE_EVENT_GROUP_NUMBER , which determines how many groups of event flags will be defined in the application. By default, this parameter is set to 0 (that is, groups of event flags are not used) and can have any value up to 16. An incorrect value will result in a compilation error, which will be generated by checking in nuse_config_check.h (it is enabled by nuse_config.c , which means it is compiled together with this module), as a result, the #error directive will work. The choice of a non-zero value serves as the main activator of the event flag groups. This parameter is used when defining data structures and their size depends on its value (for more details, see the following articles). In addition, a non-zero value activates the API settings.

Activate API calls


Each API function (service call) in the Nucleus SE is activated by the #define directive in nuse_config.h . For groups of event flags, these include:
NUSE_EVENT_GROUP_SET
NUSE_EVENT_GROUP_RETRIEVE
NUSE_EVENT_GROUP_INFORMATION
NUSE_EVENT_GROUP_COUNT

By default, they are assigned the value FALSE , thus disabling each service call and blocking the inclusion of the code implementing them. To configure groups of event flags, select the necessary API calls and set the corresponding directives to TRUE .

The following is an excerpt from the default nuse_config.h file.

 #define NUSE_EVENT_GROUP_NUMBER 0 /* Number of event groups in the system - 0-16 */ #define NUSE_EVENT_GROUP_SET FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_RETRIEVE FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_INFORMATION FALSE /* Service call enabler */ #define NUSE_EVENT_GROUP_COUNT FALSE /* Service call enabler */ 

An activated API function if there are no event flag groups in the application will result in a compilation error (except NUSE_Event_Group_Count () , which is always allowed). If your code uses an API call that has not been activated, a build error will occur because the implementation code was not included in the application.

Service Calls for Event Flags


Nucleus RTOS supports seven service calls that provide the following functionality:


The implementation of each of these service calls is detailed below.

It is worth noting that there is no reset function in either the Nucleus RTOS or the Nucleus SE. This is intentional. The reset function implies the predominance of a special state of flags. For groups of event flags, the only “special” state is the zeroing of all flags, which can be performed using NUSE_Event_Group_Set () .

Service calls for setting and reading event flag groups


The fundamental operations that can be performed on a group of event flags are setting the value of one or more flags, as well as reading the current values ​​of the flags. Nucleus RTOS and Nucleus SE provide four basic API calls for these operations.

Since event flags are bits, they are best rendered as binary numbers. Since standard C does not historically support the representation of binary constants (octal and hexadecimal only), Nucleus SE has a useful header file nuse_binary.h , which contains #define characters of the form b01010101 for all 256 8-bit values.

Setting event flags


The Nucleus RTOS API service call for setting flags is very flexible and allows you to set and clear flag values ​​using AND and OR operations. Nucleus SE provides similar functionality, but task suspension is optional.

Call to set flags in the Nucleus RTOS
Service Call Prototype:

STATUS NU_Set_Events (NU_EVENT_GROUP * group, UNSIGNED event_flags, OPTION operation);

Options:

group — pointer to the user-provided control unit for the group of event flags;
event_flags - the bitmask value of the group of flags;
operation - the operation to be performed, NU_OR (to set flags) or NU_AND (to clear flags).

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_GROUP - incorrect pointer to the group of event flags;
NU_INVALID_OPERATION - the specified operation is different from NU_OR and NU_AND .

Call to set flags in the Nucleus SE
This API call supports the core Nucleus RTOS API.

Service Call Prototype:

STATUS NUSE_Event_Group_Set (NUSE_EVENT_GROUP group, U8 event_flags, OPTION operation);

Options:

group - the index (ID) of the group of events whose flags are set / cleared;
event_flags - bit maxi flag group value;
operation - the operation to be performed, NUSE_OR (for setting flags) or NUSE_AND (for cleaning flags).

Return value:

NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_GROUP - incorrect index of the group of event flags;
NUSE_INVALID_OPERATION - the specified operation is different from NUSE_OR and NUSE_AND .

Implementing the setting of event flags in the Nucleus SE
The initial code of the NUSE_Event_Group_Set () API function is common (after checking parameters), regardless of whether support for blocking calls (task suspension) is activated or not. The logic is pretty simple:

 NUSE_CS_Enter(); if (operation == NUSE_OR) { NUSE_Event_Group_Data[group] |= event_flags; } else /* NUSE_AND */ { NUSE_Event_Group_Data[group] &= event_flags; } 

The event_flags bitmask is superimposed (using an AND or OR operation) on the value of the selected group of event flags.

The remaining code is enabled only when the task lock is activated:

 #if NUSE_BLOCKING_ENABLE while (NUSE_Event_Group_Blocking_Count[group] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this event group */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_EVENT_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == group)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Event_Group_Blocking_Count[group]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

If any tasks are suspended (for reading) from this group of flags, they are resumed. When they are given the opportunity to continue execution (it depends on the scheduler), they can determine whether the conditions for their resumption are satisfied or not (see reading the event flags).

Reading event flags


Nucleus RTOS API read calls are very flexible and allow you to suspend tasks indefinitely or with a specific timeout if the operation cannot be performed immediately (for example, if you try to read a certain sequence of event flags that do not represent the current state). Nucleus SE provides the same functions, only task suspension is optional, and timeout is not implemented.

Call to read flags in the Nucleus RTOS
Service Call Prototype:

STATUS NU_Retrieve_Events (NU_EVENT_GROUP * group, UNSIGNED requested_events, OPTION operation, UNSIGNED * retrieved_events, UNSIGNED suspend);

Options:

group — pointer to the user-provided control unit for the group of event flags;
requested_events - bitmask defining readable flags;
operation - four operations are available: NU_AND , NU_AND_CONSUME , NU_OR and NU_OR_CONSUME . The NU_AND and NU_AND_CONSUME operations indicate that all requested flags are required. The NU_OR and NU_OR_CONSUME operations indicate that one or more of the requested flags are sufficient. The CONSUME parameter automatically clears existing flags after a successful request;
retrieved_events - pointer to the repository for the values ​​of readable event flags;
suspend - specification for suspending tasks; can be NU_NO_SUSPEND or NU_SUSPEND , or the timeout value in system timer ticks (from 1 to 4,294,967,293).

Return value:

NU_SUCCESS - the call was successfully completed;
NU_NOT_PRESENT - the specified operation did not return an event (not a single event in the case of NU_OR and not all events in the case of NU_AND);
NU_INVALID_GROUP - incorrect pointer to the group of event flags;
NU_INVALID_OPERATION - the specified operation was incorrect;
NU_INVALID_POINTER — null pointer to the event flags storage (NULL);
NU_INVALID_SUSPEND - an attempt to suspend a non-task-related stream;
NU_TIMEOUT - the required combination of event flags was not set even after the specified timeout;
NU_GROUP_DELETED - the group of event flags was deleted, while the task was suspended.

A call to read flags in the Nucleus SE
This API call supports the core Nucleus RTOS API.

Service Call Prototype:

STATUS NUSE_Event_Group_Retrieve (NUSE_EVENT_GROUP group, U8 requested_events, OPTION operation, U8 * retrieved_events, U8 suspend);

Options:

group - index of the readout group of event flags;
requested_events - bitmask defining readable flags;
operation - specification indicating the number of flags required: NUSE OR (some flags) or NUSE AND (all flags);
retrieved_events - pointer to the repository for the actual values ​​of the read event flags (for the NUSE_AND operation , this will be the same as that passed in the requested_events parameter);
suspend - specification for pausing the task, can be NUSE_NO_SUSPEND or NUSE_SUSPEND .

Return value:

NUSE_SUCCESS - the call was successfully completed;
NUSE_NOT_PRESENT - the specified operation did not return an event (not a single event in the case of NUSE_OR and not all events in the case of NUSE_AND );
NUSE_INVALID_GROUP - incorrect index of the group of event flags;
NUSE_INVALID_OPERATION - the specified operation is different from NUSE_OR or NUSE_AND ;
NUSE_INVALID_POINTER - null pointer to the storage of read event flags ( NULL );
NUSE_INVALID_SUSPEND - an attempt to pause from a thread that is not associated with a task or when support for blocking API calls is disabled.

Implementing the reading of event flags in the Nucleus SE
The code variant of the NUSE_Event_Group_Retrieve () API function (after checking the parameters) is selected during conditional compilation depending on whether support for call blocking (suspension) of API calls is activated or not. Consider these two options separately.

If the lock is disabled, the full code for this API call will look like this:

 temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } 

The required event flags are selected from the specified group of event flags. The value is compared with the required events, taking into account the AND / OR operation, as well as the return result and the immediate values ​​of the requested flags.

If task lock is activated, the code becomes more complex:

 do { temp_events = NUSE_Event_Group_Data[group] & requested_events; if (operation == NUSE_OR) { if (temp_events != 0) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } else /* operation == NUSE_AND */ { if (temp_events == requested_events) { return_value = NUSE_SUCCESS; } else { return_value = NUSE_NOT_PRESENT; } } if (return_value == NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } else { if (suspend == NUSE_SUSPEND) /* block task */ { NUSE_Event_Group_Blocking_Count[group]++; NUSE_Suspend_Task(NUSE_Task_Active, (group << 4) | NUSE_EVENT_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

The code is placed in a do ... while loop , which works while the suspend parameter is NUSE_SUSPEND .

The requested event flags are read the same way as when calling without blocking. If the read is not successful and the suspend parameter is NUSE_NO_SUSPEND , the API call is assigned the value NUSE_NOT_PRESENT . If the suspend parameter was set to NUSE_SUSPEND , the task is suspended. On return (when the task is resumed), if the return value is NUSE_SUCCESS , indicating that the task has been resumed because the event flags in this group have been set or cleared, the cycle starts from the beginning, the flags are read and checked. Since there is no API function for resetting groups of event flags, this is the only reason for resuming the task, but the NUSE_Task_Blocking_Return [] checkout process was left on the system for lock control compatibility with other types of objects.

The next article will describe additional API calls related to groups of event flags, as well as their data structures.

About the author: Colin Walls has been working in the electronics industry for more than thirty years, spending a significant amount of time on embedded software. He is now an embedded software engineer in Mentor Embedded (a division of Mentor Graphics). Colin Walls often speaks at conferences and seminars, author of numerous technical articles and two books on embedded software. Lives in the UK. Colin's professional blog , e-mail: colin_walls@mentor.com.

Source: https://habr.com/ru/post/428131/


All Articles