Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Event Flag Synchronization Patterns in Real-Time Operating Systems

Tech 1

Event flags (or event groups) provide bitmap-based synchronization primitives enabling complex coordination patterns between multiple producers and consumers. Unlike counting semaphores, setting an already active flag does not queue additional events; the state remains binary until explicitly cleared. This characteristic makes them ideal for signaling asynchronous hardware occurrences—such as interrupts from peripherals—where the occurrence count matters less than the binary state transition.

Core Mechanisms

Each flag group manages a 32-bit bitmask where individual bits represent distinct events. Threads can wait for combinations using logical OR (any flag) or AND (all flags) conditions. When a thread invokes a wait operation, it can specify whether successful matches automatically clear the corresponding bits or leave them intact for manual cleanup.

Key CMSIS-RTOS2 interfaces include:

osEventFlagsId_t osEventFlagsNew(const osEventFlagsAttr_t *attr);

Creates a new flag group. Returns a handle for subsequent operations.

uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags);

Atomically sets one or more bits (e.g., 0x05 sets bits 0 and 2). If threads are waiting on these bits, they become eligible for scheduling according to their wait criteria.

uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags);

Explicitly clears specified bits, typically after processing the associated event.

uint32_t osEventFlagsWait(osEventFlagsId_t ef_id, 
                          uint32_t flags, 
                          uint32_t options, 
                          uint32_t timeout);

Blocks until the specified flags meet the condition defined in options. Common options include osFlagsWaitAny (trigger if any bit matches) and osFlagsWaitAll (trigger only if all bits match). Adding osFlagsNoClear requires manual clearing via osEventFlagsClear, which is essential when multiple processing stages must observe the same event.

Practical Implementation: Interrupt-Driven Button Handling

Consider a scenario where a mechanical button connects to a GPIO line configured for falling-edge interrupts. The ISR signals the event, while a dedicated task handles debouncing and action execution.

Hardware Configuration

Configure the GPIO for interrupt generation on the falling edge:

#define USER_BUTTON_PIN     GPIO_PIN_0
#define USER_BUTTON_PORT    GPIOA
#define BTN_FLAG_PRESSED    0x01U

static osEventFlagsId_t hButtonEvent;

void ConfigureButtonInterrupt(void)
{
    GPIO_InitTypeDef cfg = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    cfg.Pin = USER_BUTTON_PIN;
    cfg.Mode = GPIO_MODE_IT_FALLING;
    cfg.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(USER_BUTTON_PORT, &cfg);
    
    HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

uint8_t IsButtonStillPressed(void)
{
    return (HAL_GPIO_ReadPin(USER_BUTTON_PORT, USER_BUTTON_PIN) == GPIO_PIN_RESET);
}

Interrupt Service Routine

Keep the ISR minimal—defer processing to the thread context:

void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
    if (pin == USER_BUTTON_PIN)
    {
        osEventFlagsSet(hButtonEvent, BTN_FLAG_PRESSED);
    }
}

Processing Task

The task remains blocked indefinitely, consuming no CPU until the ISR signals an event. Upon waking, it validates the button state after a debounce interval:

void vButtonMonitorTask(void *pvParams)
{
    (void)pvParams;
    uint32_t notifiedFlags;
    
    hButtonEvent = osEventFlagsNew(NULL);
    ConfigureButtonInterrupt();
    
    for (;;)
    {
        notifiedFlags = osEventFlagsWait(
            hButtonEvent, 
            BTN_FLAG_PRESSED, 
            osFlagsWaitAny | osFlagsNoClear, 
            osWaitForever
        );
        
        if (notifiedFlags & BTN_FLAG_PRESSED)
        {
            osDelay(20);  // Mechanical debounce
            
            if (IsButtonStillPressed())
            {
                ExecuteButtonCommand();
            }
            
            osEventFlagsClear(hButtonEvent, BTN_FLAG_PRESSED);
        }
    }
}

Using osFlagsNoClear ensures the flag persists during the debounce window. If the task were to clear the flag automatically upon wake, a rapid second interrupt occurring during debounce might be lost. Manual clearing after validation guarantees that only processed events are removed from the pending state.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.