Scan to FollowLearn Embedded Together, learn and grow together

This is a series of introductory articles on FreeRTOS. While organizing my own knowledge, I hope to help beginners quickly get started and master the basic principles and usage of FreeRTOS.
Quick Start with FreeRTOS – Exploring the System
FreeRTOS Official Chinese Website is Now Live
FreeRTOS Coding Standards and Data Types
Quick Start with FreeRTOS – Task Management
Learning FreeRTOS: Detailed Explanation of Message Queues
Learning FreeRTOS: Counting Semaphores
Learning FreeRTOS: Mutex Semaphores
This article introduces the related content of FreeRTOS’s Event Groups.
Event Groups are a powerful inter-task communication mechanism in FreeRTOS, allowing tasks to wait for any one or all of multiple events to occur.
Event Groups are particularly suitable for scenarios where multiple conditions need to be met or any one condition needs to be met.
Features
- Multi-event Management: Can manage up to 24 different event flags simultaneously (depending on configuration)
- Bit Operations: Each event flag is represented by a bit, similar to a bitmask
- Efficient Synchronization: Multiple tasks can wait for the same event group
- Broadcast Mechanism: Setting an event can wake up all waiting tasks
Comparison with Other Synchronization Mechanisms
| Feature | Event Groups | Semaphores | Message Queues |
|---|---|---|---|
| Synchronization Type | Multi-event Synchronization | Single-event Synchronization | Data Transfer + Synchronization |
| Waiting Conditions | Multiple Condition Combinations | Single Condition | Single Condition |
| Data Transfer | No | No | Yes |
| Applicable Scenarios | Complex Condition Waiting | Simple Synchronization | Data Transfer + Synchronization |
Implementation Principles
Data Structure
The event groups in FreeRTOS are represented by the <span>EventGroupHandle_t</span> type, which is actually a pointer to a <span>EventGroupDef_t</span> structure:
typedef struct EventGroupDef_t
{
EventBits_t uxEventBits; // Variable to store event flag bits
List_t xTasksWaitingForBits; // List of tasks waiting for event flags
} EventGroupDef_t;
Event Flag Bits
<span>uxEventBits</span> is an unsigned integer (usually 32 bits), with each bit representing an event flag:
bit31 -------------------------------- bit0
| | | |...| | | | | | |
FreeRTOS uses the lowest 24 bits as event flag bits by default, with the high bits reserved for internal use.
API Details
Creating Event Groups
EventGroupHandle_t xEventGroupCreate(void);
Function: Creates a new event group
Return Value: Returns the event group handle on success, NULL on failure
Example:
EventGroupHandle_t xEventGroup;
void vInitFunction(void)
{
xEventGroup = xEventGroupCreate();
if(xEventGroup == NULL)
{
// Handle creation failure
}
}
Setting Event Flags
Set Bits (Do Not Wake Tasks)
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet);
Function: Sets the specified event flag bits without waking waiting tasks
Parameters:
<span>xEventGroup</span>: Event group handle<span>uxBitsToSet</span>: Bitmask to set (e.g., 0x01 sets bit0, 0x05 sets bit0 and bit2)
Return Value: The value of the event group after setting
Note: Typically used in interrupt service routines (ISRs) with its FromISR version
Set Bits and Wake Tasks
EventBits_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
BaseType_t *pxHigherPriorityTaskWoken);
Function: Sets event flag bits in an ISR and may wake waiting tasks
Parameters:
<span>pxHigherPriorityTaskWoken</span>: Indicates if a higher priority task was woken
Example:
// Set event flags in a task
xEventGroupSetBits(xEventGroup, BIT_0 | BIT_2);
// Set event flags in an ISR
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xEventGroupSetBitsFromISR(xEventGroup, BIT_1, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
Clearing Event Flags
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear);
EventBits_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear);
Function: Clears the specified event flag bits
Parameters:
<span>uxBitsToClear</span>: Bitmask to clear
Return Value: The value of the event group before clearing
Example:
// Clear bit0 and bit2
xEventGroupClearBits(xEventGroup, BIT_0 | BIT_2);
Getting Event Flag Status
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup);
Function: Gets the current value of the event group
Note: Does not change the state of the event group
Example:
EventBits_t uxBits = xEventGroupGetBits(xEventGroup);
if((uxBits & BIT_0) != 0) {
// bit0 is set
}
Waiting for Event Flags
EventBits_t xEventGroupWaitBits(const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait);
Function: Waits for the specified event flag bits to be set
Parameters:
<span>uxBitsToWaitFor</span>: Bitmask to wait for<span>xClearOnExit</span>: pdTRUE indicates to clear the waited bits before exiting<span>xWaitForAllBits</span>: pdTRUE indicates to wait for all bits to be set, pdFALSE indicates to wait for any bit to be set<span>xTicksToWait</span>: Maximum wait time (ticks), portMAX_DELAY indicates infinite wait
Return Value: The event flag bits that met the conditions, or the value on timeout
Example:
// Wait for either bit0 or bit1 to be set, do not auto-clear, wait for 100ms
EventBits_t uxBits = xEventGroupWaitBits(xEventGroup, BIT_0 | BIT_1,
pdFALSE, pdFALSE, pdMS_TO_TICKS(100));
// Wait for both bit0 and bit1 to be set, auto-clear these bits before exiting, wait indefinitely
uxBits = xEventGroupWaitBits(xEventGroup, BIT_0 | BIT_1,
pdTRUE, pdTRUE, portMAX_DELAY);
Synchronizing Event Flags
EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait);
Function: Sets specified bits and waits for other bits to be set (multi-task synchronization)
Parameters:
<span>uxBitsToSet</span>: Bitmask to set<span>uxBitsToWaitFor</span>: Bitmask to wait for<span>xTicksToWait</span>: Wait timeout
Return Value: The event flag bits that met the conditions
Typical Application: Synchronizing multiple tasks to the same point
Example:
// Set bit0 and wait for bit1 and bit2 to be set
EventBits_t uxBits = xEventGroupSync(xEventGroup, BIT_0, BIT_1 | BIT_2, 100);
Usage Patterns
Single Task Waiting for Multiple Events
void vTaskFunction(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
// Wait for both bit0 and bit1 to be set, do not auto-clear
EventBits_t uxBits = xEventGroupWaitBits(
xEventGroup,
BIT_0 | BIT_1,
pdFALSE,
pdTRUE,
portMAX_DELAY);
if((uxBits & (BIT_0 | BIT_1)) == (BIT_0 | BIT_1))
{
// Both events occurred
// Handle events...
}
}
}
Multiple Tasks Waiting for the Same Event Group
// Task 1 waits for bit0
void vTask1(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
xEventGroupWaitBits(xEventGroup, BIT_0, pdTRUE, pdTRUE, portMAX_DELAY);
// bit0 is set and auto-cleared
// Handle bit0 event...
}
}
// Task 2 waits for bit1
void vTask2(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
xEventGroupWaitBits(xEventGroup, BIT_1, pdTRUE, pdTRUE, portMAX_DELAY);
// bit1 is set and auto-cleared
// Handle bit1 event...
}
}
Task Synchronization
// Task A
void vTaskA(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
// Perform some work...
// Set bit0 and wait for bit1 and bit2
xEventGroupSync(xEventGroup, BIT_0, BIT_1 | BIT_2, portMAX_DELAY);
// All tasks reach the synchronization point
// Continue execution...
}
// Task B
void vTaskB(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
// Perform some work...
// Set bit1 and wait for bit0 and bit2
xEventGroupSync(xEventGroup, BIT_1, BIT_0 | BIT_2, portMAX_DELAY);
// All tasks reach the synchronization point
// Continue execution...
}
// Task C
void vTaskC(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
// Perform some work...
// Set bit2 and wait for bit0 and bit1
xEventGroupSync(xEventGroup, BIT_2, BIT_0 | BIT_1, portMAX_DELAY);
// All tasks reach the synchronization point
// Continue execution...
}
Practical Applications
Event Flag Definitions
It is recommended to use macros or enums to clearly define the meaning of event flags:
#define TASK_READY_BIT (1 << 0) // bit0: Task is ready
#define DATA_READY_BIT (1 << 1) // bit1: Data is ready
#define NETWORK_UP_BIT (1 << 2) // bit2: Network connection established
#define SENSOR_DATA_BIT (1 << 3) // bit3: Sensor data available
Error Handling
- Always check the return value of
<span>xEventGroupCreate()</span> - Consider timeout situations when waiting for events
- Use the FromISR version of the API in ISRs
Performance Considerations
- Event group operations are atomic, but holding an event group for a long time may cause priority inversion
- For high-frequency events, consider using direct task notifications as a lightweight alternative
- Avoid using too many bits in event groups, as this complicates logic
Debugging Tips
- Use
<span>uxEventGroupGetNumber()</span>to assign a unique number to the event group - View the value of
<span>uxEventBits</span>in the debugger - Use FreeRTOS tracing features to monitor event group operations
Application Cases
Multi-Sensor Data Acquisition System
// Define event flags
#define TEMP_READY_BIT (1 << 0)
#define HUMID_READY_BIT (1 << 1)
#define PRESS_READY_BIT (1 << 2)
#define ALL_SENSORS_BITS (TEMP_READY_BIT | HUMID_READY_BIT | PRESS_READY_BIT)
// Sensor task
void vTempSensorTask(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
// Read temperature...
xEventGroupSetBits(xEventGroup, TEMP_READY_BIT);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// Data processing task
void vDataProcessorTask(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
// Wait for all sensor data to be ready
EventBits_t uxBits = xEventGroupWaitBits(
xEventGroup,
ALL_SENSORS_BITS,
pdTRUE, // Auto-clear flags
pdTRUE, // Wait for all bits
portMAX_DELAY);
// Process all sensor data...
}
}
Network Connection Status Management
// Define event flags
#define WIFI_CONNECTED_BIT (1 << 0)
#define ETH_CONNECTED_BIT (1 << 1)
#define ANY_NETWORK_BIT (WIFI_CONNECTED_BIT | ETH_CONNECTED_BIT)
// Network status monitoring task
void vNetworkMonitorTask(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
if(xWiFiIsConnected())
{
xEventGroupSetBits(xEventGroup, WIFI_CONNECTED_BIT);
xEventGroupClearBits(xEventGroup, ETH_CONNECTED_BIT);
}
elseif(xEthernetIsConnected())
{
xEventGroupSetBits(xEventGroup, ETH_CONNECTED_BIT);
xEventGroupClearBits(xEventGroup, WIFI_CONNECTED_BIT);
}
else
{
xEventGroupClearBits(xEventGroup, ANY_NETWORK_BIT);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
}
// Task requiring network
void vNetworkUserTask(void *pvParameters)
{
EventGroupHandle_t xEventGroup = (EventGroupHandle_t)pvParameters;
for(;;)
{
// Wait for any network connection
xEventGroupWaitBits(xEventGroup, ANY_NETWORK_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
// Perform network-required operations...
}
}
Limitations and Alternatives
Limitations
- Limited Bits: Usually only 24 bits are available
- No History: Cannot know when an event was set
- Broadcast Mechanism: Setting an event wakes all waiting tasks, which may cause the “thundering herd effect”
Alternatives
- Direct Task Notifications:
- More lightweight (each task has its own notification value)
- But can only wait for a single event
- Suitable for simple synchronization needs
- Cannot express complex conditions
- Can carry data
- Weaker synchronization capability
Conclusion
By properly using event groups, an efficient and clear inter-task communication architecture can be built. Key points include:
- Clearly define the meaning of event flags
- Select the appropriate waiting mode (any/all) based on needs
- Use the FromISR version of the API in ISRs
- Consider using synchronization points for multi-task coordination
- For simple scenarios, consider more lightweight alternatives
By mastering the use of event groups, the structural clarity and operational efficiency of FreeRTOS applications can be significantly improved.

Follow 【Learn Embedded Together】 to become better together.
If you find this article useful, click “Share”, “Like”, “Recommend”!