FreeRTOS is a popular open-source real-time operating system (RTOS) for embedded systems. It offers a variety of synchronization primitives for managing concurrency, one of which is Event Groups. Event Groups provide a flexible mechanism for tasks to synchronize and communicate by sharing a set of flags, making them an essential tool in building responsive, multitasking systems. This article will explore the concept of Event Groups in FreeRTOS, how they work, and practical use cases for implementing them in embedded applications.
What are Event Groups in FreeRTOS?
An EventGroup in FreeRTOS is a data structure used for synchronization between tasks. It stores a 32-bit value (or word) where each individual bit acts as a flag. Tasks can wait for one or more bits to be set or cleared, set or clear specific bits, and check the current state of the bits. This is an efficient way to manage events in a system where multiple tasks need to coordinate their execution or wait for particular conditions.
EventGroups are particularly useful in situations where you have multiple flags or events that need to be managed at once, as it reduces the overhead of managing multiple semaphores or queues.
How Do Event Groups Work?
An Event Group works on the principle of manipulating individual bits of an unsigned integer. Each bit in the Event Group represents an event or condition. These bits can be set, cleared, or tested by tasks in the system.
The main operations available with Event Groups are:
- Set bits: A task can set one or more bits in an Event Group to indicate that an event has occurred. Setting a bit can either be done individually or using a bitwise OR operation.
- Clear bits: A task can clear one or more bits in the Event Group, signaling that a particular event is no longer active or has been handled.
- Wait for bits: A task can wait for certain bits to be set, meaning it will block until the specified event(s) occur. A task can specify if it wants to wait for any of the bits to be set, all of them, or a specific combination of bits.
- Test bits: A task can check the current state of the bits to determine if certain events or conditions have occurred.
Key Features of EventGroups:
- Bit-wise operations: Each bit in the EventGroup can represent a different event or condition.
- Atomic operations: Setting or clearing bits is done atomically, ensuring that the operation is safe from race conditions.
- Task synchronization: Tasks can wait for one or more bits to be set (or cleared) and proceed when the condition is met.
- Efficient resource usage: Since an EventGroup is essentially a 32-bit word, you can manage up to 32 different events within a single data structure.
Creating and Using Event Groups
To use Event Groups in FreeRTOS, you need to follow these steps:
Create an Event Group:
The first step is to create an Event Group, typically using the xEventGroupCreate() function. This function returns a handle to the newly created Event Group.
EventGroupHandle_t xEventGroup;
xEventGroup = xEventGroupCreate();
The xEventGroup handle is used to refer to the Event Group in subsequent function calls.
Setting and Clearing Bits:
To set or clear bits in the Event Group, use the following functions:
- xEventGroupSetBits(): Sets one or more bits in the Event Group.
- xEventGroupClearBits(): Clears one or more bits in the Event Group.
Waiting for Bits:
To wait for specific bits to be set, use xEventGroupWaitBits(). This function blocks the calling task until the specified bits are set. It can wait for any of the specified bits, all of them, or a combination.
EventBits_t bits = xEventGroupWaitBits(xEventGroup, (1 << 0), pdTRUE, pdFALSE, portMAX_DELAY);
if (bits & (1 << 0)) {
// Bit 0 was set, proceed with the task
}
/*
pdTRUE specifies that bits should be cleared after being retrieved.
pdFALSE specifies that the function should wait for any of the bits to be set (as opposed to all bits).
portMAX_DELAY tells FreeRTOS to wait indefinitely until the condition is met.
*/
Testing Bits:
To check the current state of the bits in the Event Group, you can use xEventGroupGetBits(). This function returns the current bitmask of the Event Group.
EventBits_t bits = xEventGroupGetBits(xEventGroup);
if (bits & (1 << 0)) {
// Bit 0 is set
}
Key Differences Between Event Groups and Task Notifications
Feature | Event Groups | Task Notifications |
Purpose | Synchronization of tasks using multiple event flags (bitmasks). | Simple, lightweight signaling between tasks. |
Number of Participants | Typically used to synchronize multiple tasks based on event flags. | One-to-one communication between a task and another task or interrupt. |
Data Handling | Event Groups work with bitmasks (flags) to represent multiple events | Task Notifications carry simple integer values or are used for signaling (without data payloads). |
Memory Usage | Event Groups require memory for storing the event flags (typically 32-bit or 64-bit). | Task Notifications are very memory-efficient; each task has a small notification value (typically 32-bits). |
Bitmask Support | Supports a set of flags (bits), which allows waiting for combinations of multiple events. | notifications are typically a single integer value or event. |
Waiting Mechanism | Tasks can wait for any/all bits to be set, wait for specific combinations, or wait for timeouts. | Tasks wait for a specific notification value or signal to be sent by another task. |
Blocking Behavior | Tasks can block until specific bits are set, or a timeout is reached. | Tasks can block until a notification arrives or be immediately notified. |
Task-to-Task Communication | Event Groups allow for communication and synchronization between multiple tasks. | Task Notifications are typically used for communication between a task and the task that sends the notification (or from an interrupt). |
Interrupt Capability | Event Groups can be used in interrupt context, though the operations are more complex. | Task Notifications are often used in interrupt contexts due to their simplicity. |
Overhead | Higher overhead than task notifications because of bitmask operations. | Very low overhead, making them ideal for simple signaling. |
Implementing EvenGroups
Let’s consider an example where one task sets an event, and another task waits for that event before proceeding.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_system.h"
// Define bit masks for event group
#define BIT_0 (1 << 0) // Bit 0 (Task 1)
#define BIT_1 (1 << 1) // Bit 1 (Task 2)
// Declare an EventGroup handle
static EventGroupHandle_t xEventGroup;
// Task 1: Sets BIT_0 and BIT_1 in the event group
void task1(void *pvParameters)
{
while (1) {
printf("Task 1: Setting BIT_0 and BIT_1\n");
// Set both BIT_0 and BIT_1 in the event group
xEventGroupSetBits(xEventGroup, BIT_0 | BIT_1);
// Simulate some processing with a delay
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
// Task 2: Waits for BIT_0 and BIT_1 to be set and reacts
void task2(void *pvParameters)
{
EventBits_t uxBits;
while (1) {
// Wait indefinitely for both BIT_0 and BIT_1 to be set
uxBits = xEventGroupWaitBits(xEventGroup,
BIT_0 | BIT_1, // Wait for both BIT_0 and BIT_1
pdTRUE, // Clear the bits when they are set
pdFALSE, // Don't wait for all bits, just BIT_0 and BIT_1
portMAX_DELAY); // Block indefinitely
// Check if BIT_0 and BIT_1 were set
if (uxBits & BIT_0) {
printf("Task 2: BIT_0 was set\n");
}
if (uxBits & BIT_1) {
printf("Task 2: BIT_1 was set\n");
}
}
}
// Main function: Creates tasks and initializes event group
void app_main(void)
{
// Create an event group
xEventGroup = xEventGroupCreate();
if (xEventGroup == NULL) {
printf("Error creating event group\n");
return;
}
// Create two tasks
xTaskCreate(task1, "Task 1", 2048, NULL, 1, NULL);
xTaskCreate(task2, "Task 2", 2048, NULL, 1, NULL);
}
Conclusion
Event Groups are a powerful synchronization tool in FreeRTOS, allowing efficient inter-task communication and coordination in real-time embedded systems. With simple operations like setting, clearing, and waiting for bits, Event Groups make it easy to synchronize tasks based on multiple events or conditions. Understanding how to leverage Event Groups can improve both the responsiveness and efficiency of your FreeRTOS-based applications.