1. Basic Concepts

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, &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 |

Previous Recommendations
RECOMMEND

[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 ID|aike_eureka
Baijiahao|Master Embedded Systems