Understanding Semaphore Mechanisms in FreeRTOS

1. Basic Concepts

Understanding Semaphore Mechanisms in FreeRTOS

A semaphore is a classic synchronization mechanism for processes/tasks, used to coordinate multiple execution units (such as tasks and threads) in accessing shared resources, preventing race conditions. Thus, it can be generally understood as:

A semaphore is an integer counter used to control access to shared resources.

Operation Control
  • P (Proberen) Operation: Attempt to acquire a resource (in FreeRTOS, this is <span>xSemaphoreTake()</span>)
  • V (Verhogen) Operation: Release a resource (in FreeRTOS, this is <span>xSemaphoreGive()</span>)
Working Principle
  • Initialize a semaphore with a value of N
    • This indicates that there are up to N resources available.
  • P Operation (Take)
    • If the semaphore value > 0: allow access, decrement the semaphore value by 1.
    • If the semaphore value == 0: block and wait for the resource to be released.
  • V Operation (Give)
    • Increment the semaphore value by 1, waking up blocked tasks (if any).
Main Functions
  • Mutual Exclusion: Ensures that only one task accesses a shared resource at a time (like a mutex)
  • Synchronization: Controls the execution order between multiple tasks or interrupts
  • Event Notification: A task/interrupt notifies other tasks that something has occurred via a semaphore
  • Resource Management: Controls concurrent access to a fixed number of resources (like connection pools, buffers)

2. Classification

Semaphores in FreeRTOS are implemented through a queue mechanism. Depending on their use, semaphores can be classified into the following three types:

  • Counting Semaphore
  • Binary Semaphore
  • Mutex Semaphore

2.1. Counting Semaphore

Counting Semaphore
  • Characteristics: The semaphore value can be greater than 1.
  • Function:
    • Can be used to implement event counting (e.g., number of interrupts).
    • Control the number of resources (e.g., control N identical resources, allowing only N tasks to access).
  • Interface:
SemaphoreHandle_t xSemaphore = xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
  • <span>uxMaxCount</span>: Maximum count value
    • <span>uxInitialCount</span>: Initial count value
Note
  • In FreeRTOS, counting semaphores do not support priority inheritance.

2.2. Binary Semaphore

Binary Semaphore
  • Characteristics: The semaphore value can only be 0 or 1.
  • Function:
    • Commonly used for task synchronization (e.g., interrupt service routines waking tasks).
    • Does not have the concept of resource ownership (unlike mutexes).
  • Interface:
SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();
  • After creation, the initial state is empty (needs to be manually <span>xSemaphoreGive()</span> to be usable).
Note
  • In FreeRTOS, binary semaphores do not support priority inheritance.

2.3. Mutex Semaphore

Mutex Semaphore
  • Characteristics:
    • A special type of binary semaphore
    • Supports priority inheritance mechanisms to prevent priority inversion
  • Function:
    • Used for mutual access to shared resources (like serial ports, I2C buses) between tasks.
  • Interface:
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

3. Interface Examples

Common Functions Function Description
<span>xSemaphoreTake(xSemaphore,xTicksToWait)</span> Acquire (“P operation”), if failed can block waiting for <span>xTicksToWait</span> ticks
<span>xSemaphoreGive(xSemaphore)</span> Release (“V operation”), increment the semaphore value by 1
<span>xSemaphoreGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken)</span> Release semaphore in an interrupt
<span>xSemaphoreTakeFromISR(xSemaphore,pxHigherPriorityTaskWoken)</span> Attempt to acquire semaphore in an interrupt (not commonly used)
<span>vSemaphoreDelete(xSemaphore)</span> Delete semaphore (release memory)

3.1. Task and Interrupt Synchronization

Binary Semaphore
// Interrupt Service Routine
void vISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xBinarySemaphore, &amp;xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// Task
void vTask(void *pvParameters)
{
    for (;;)
    {
        if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE)
        {
            // Woken by interrupt, perform response handling
        }
    }
}

3.2. Multi-task Access to Shared Resources

Mutex Semaphore
void vTaskA(void *pvParameters)
{
    if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
    {
        // Critical section
        printf("Task A accessing resource\n");
        xSemaphoreGive(xMutex);
    }
}

3.3. Counting Semaphore for Resource Pool Control

Counting Semaphore
SemaphoreHandle_t xCountingSemaphore;

void init()
{
    xCountingSemaphore = xSemaphoreCreateCounting(3, 3);
}

void vTask(void *pvParameters)
{
    if (xSemaphoreTake(xCountingSemaphore, portMAX_DELAY) == pdTRUE)
    {
        // Use one resource
        // ...
        xSemaphoreGive(xCountingSemaphore); // Return resource
    }
}

4. Application Scenarios

Semaphore Type Applicable Scenarios Characteristics
Binary Semaphore Task and interrupt synchronization

Event notification (a task has completed an action, notifying other tasks) | The semaphore value can only be 0 or 1 (not an accumulating counter)

Does not record the number of times the semaphore is “Given”, multiple Gives only retain one

Does not support priority inheritance.

Default is empty after creation (take will fail, must give first) | Counting Semaphore | <1>. Event counting (e.g., number of interrupts or packets)

  • Each time an interrupt occurs, give, and each time a task processes, take.

<2>. Resource pool management (concurrent control of multiple resources)

  • For example, 3 camera tasks, limiting a maximum of 2 to run simultaneously. | The semaphore value can be greater than 1, can accumulate the number of Gives.

Suitable for situations requiring statistics/control of multiple events or resources.

Does not support priority inheritance. | Mutex Semaphore | Protection of access to shared resources between tasks

Such as shared printers, LCDs, serial ports, I2C buses, etc. | <1>. Essentially a binary semaphore that supports priority inheritance.

<2>. Tasks must call Take and Give in pairs (similar to locking and unlocking).

<3>. Supports recursive calls: a task can acquire the same Mutex multiple times.

<4>. Supports priority inheritance:

  • If a high-priority task is blocked by a resource held by a low-priority task, the low-priority task’s priority will be temporarily raised to avoid priority inversion.
Summary
Semaphore Type Supports Accumulation Used for Synchronization Used for Mutual Exclusion Supports Priority Inheritance Application Example
Binary Semaphore ❌ (maximum 1) Interrupt wakes task
Counting Semaphore ✅ (resource pool) Control multiple resources, count multiple events
Mutex Semaphore ❌ (only 0/1) ❌ (not recommended) Control access to serial/LCD/shared buffer

Understanding Semaphore Mechanisms in FreeRTOS

Previous Recommendations

RECOMMEND

Understanding Semaphore Mechanisms in FreeRTOS

[1]. FreeRTOS Development: Queue Development Code Explanation

[2]. FreeRTOS Development: Core Content of Task Scheduling

[3]. FreeRTOS Development: In-depth Understanding of Memory Management Schemes

[4]. FreeRTOS Development: Core Mechanisms and Important Concepts

I am Aike, an embedded software engineer.

Follow me for more embedded insights.

Remember to like, share, and click to view,

Your encouragement is my greatest motivation to continue sharing!

See you next time.

Looking forward to your

sharing

likes

views

NEWS

WeChat IDaike_eureka

Baijiahao|Master Embedded Systems

Leave a Comment