Scan to followLearn Embedded Together, learn and grow together

This is a series of introductory articles on FreeRTOS. While organizing my knowledge, I hope to help beginners quickly get started and master the basic principles and usage of FreeRTOS.
Quick Start with FreeRTOS – Initial Exploration of the System
The official Chinese version of the FreeRTOS website is now online!
FreeRTOS Coding Standards and Data Types
Quick Start with FreeRTOS – Task Management
Detailed Explanation of FreeRTOS Message Queues
This article introduces the related content of counting semaphores in FreeRTOS.
A counting semaphore is an important synchronization mechanism in FreeRTOS that allows for event notification and resource management between tasks or between tasks and interrupt service routines (ISRs).
Counting semaphores have the following characteristics:
- It maintains a count value that represents the number of available resources or the number of events that have occurred.
- When a task takes the semaphore, the count value decreases; when the semaphore is given back, the count value increases.
- The count value can be greater than 1, which is different from binary semaphores.
- When the count value is 0, tasks attempting to take the semaphore may be blocked (depending on the calling method).
Working Principle
The core of a counting semaphore is a counter and a task waiting list:
- On creation: specify the initial count value
- Taking the semaphore:
- If the count value > 0, the count value decreases by 1 and returns success immediately.
- If the count value = 0, it may block or return failure immediately depending on the calling method.
- If there are tasks waiting, wake up the highest priority task.
- If no tasks are waiting, increase the count value by 1.
Main Application Scenarios
Counting semaphores have two main uses in embedded systems:
Event Counting
- Record the number of occurrences of events.
- Allow a task to unblock when an event occurs while tracking the number of events that have occurred but have not yet been processed.
- Commonly used for ISRs to send event notifications to tasks.
Resource Management
- Manage a limited number of identical resources.
- The count value represents the number of currently available resources.
- Tasks take the semaphore before using the resource and give it back after use.
Detailed Explanation of FreeRTOS Counting Semaphore API
Creating a Counting Semaphore
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount );
- Parameters:
<span>uxMaxCount</span>: The maximum count value the semaphore can reach.<span>uxInitialCount</span>: The initial count value of the semaphore.- Return Value:
- Success: Semaphore handle.
- Failure: NULL (usually due to insufficient memory).
Example:
// Create a counting semaphore with a maximum count of 10 and an initial value of 0
SemaphoreHandle_t xCountingSemaphore = xSemaphoreCreateCounting(10, 0);
Taking the Semaphore
FreeRTOS provides several ways to take a semaphore:
Blocking Method
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
- Parameters:
<span>xSemaphore</span>: Semaphore handle.<span>xTicksToWait</span>: Maximum blocking time (in ticks), use<span>portMAX_DELAY</span><span> for infinite wait.</span>- Return Value:
- pdTRUE: Successfully took the semaphore.
- pdFALSE: Timeout without taking the semaphore.
Non-blocking Method
BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken );
- Used in interrupt service routines (ISRs).
- Parameters:
<span>pxHigherPriorityTaskWoken</span>indicates whether a higher priority task has been woken up.
Giving the Semaphore
Normal Give
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
- Parameter: Semaphore handle.
- Return Value:
- pdTRUE: Successfully gave the semaphore.
- pdFALSE: The semaphore count has reached the maximum value.
Give in ISR
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken );
- Used in interrupt service routines.
- Parameters:
<span>pxHigherPriorityTaskWoken</span>indicates whether a higher priority task has been woken up.
Example
Event Counting Example
// Semaphore handle declaration
SemaphoreHandle_t xEventSemaphore;
// Task function: Event handling
void vEventHandlerTask(void *pvParameters) {
while(1) {
// Wait for an event to occur
if(xSemaphoreTake(xEventSemaphore, portMAX_DELAY) == pdTRUE) {
// Process the event
printf("Event processed. Remaining events: %u\n",
uxSemaphoreGetCount(xEventSemaphore));
}
}
}
// Interrupt service routine: Generate an event
void vInterruptHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Give the semaphore (notify that an event has occurred)
xSemaphoreGiveFromISR(xEventSemaphore, &xHigherPriorityTaskWoken);
// If needed, perform a context switch
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// Main function
int main(void) {
// Create a counting semaphore, initial value 0, maximum value 10
xEventSemaphore = xSemaphoreCreateCounting(10, 0);
// Create event handling task
xTaskCreate(vEventHandlerTask, "EventHandler", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
// Start the scheduler
vTaskStartScheduler();
while(1);
}
Resource Management Example
// Assume we have 3 identical hardware resources (e.g., UART interfaces)
#define MAX_UART_RESOURCES 3
// Semaphore handle
SemaphoreHandle_t xUARTSemaphore;
// Task function: Using UART resource
void vUARTUserTask(void *pvParameters) {
while(1) {
// Try to take the UART resource
if(xSemaphoreTake(xUARTSemaphore, pdMS_TO_TICKS(100)) == pdTRUE) {
// Successfully took the resource
printf("Task %s got UART resource\n", pcTaskGetName(NULL));
// Simulate using the UART resource
vTaskDelay(pdMS_TO_TICKS(500));
// Give back the resource
xSemaphoreGive(xUARTSemaphore);
printf("Task %s released UART resource\n", pcTaskGetName(NULL));
} else {
// Timeout while trying to get the resource
printf("Task %s failed to get UART resource\n", pcTaskGetName(NULL));
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
// Main function
int main(void) {
// Create a counting semaphore, initial value = maximum value = 3 (indicating 3 available resources)
xUARTSemaphore = xSemaphoreCreateCounting(MAX_UART_RESOURCES, MAX_UART_RESOURCES);
// Create multiple tasks competing for the UART resource
xTaskCreate(vUARTUserTask, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vUARTUserTask, "Task2", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vUARTUserTask, "Task3", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vUARTUserTask, "Task4", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
// Start the scheduler
vTaskStartScheduler();
while(1);
}
Advanced
Getting the Current Count Value
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
- Returns the current count value of the semaphore.
- For a semaphore that has been taken, it returns the number of currently available resources, not the number of waiting tasks.
Semaphore Overflow Handling
When the semaphore count reaches the maximum value, further giving the semaphore will fail. In practical applications, consider:
- Set the maximum count value reasonably.
- Check the return value of
<span>xSemaphoreGive</span>. - If necessary, use a larger data type (e.g., set
<span>configUSE_16_BIT_TICKS</span>to 0 to use 32-bit count values).
Comparison with Binary Semaphores
| Feature | Counting Semaphore | Binary Semaphore |
|---|---|---|
| Count Value Range | 0 to uxMaxCount | 0 or 1 |
| Initial Value | Can be specified | Usually 0 (not available) or 1 (available) |
| Application Scenario | Multi-resource management/event counting | Single resource/event notification |
| Implementation Mechanism | Based on counting | Can be viewed as a counting semaphore with a maximum value of 1 |
Considerations
- Set the maximum count value reasonably: Should be set based on the actual number of resources or expected maximum number of events.
- Avoid priority inversion: High-priority tasks waiting for a long time for low-priority tasks to release the semaphore.
- Usage in ISR:
- Must use the
<span>FromISR</span>version of the API. - Blocking calls cannot be used in ISR.
- Avoid tasks taking multiple semaphores in reverse order.
- Set reasonable timeout values.
- Ensure each
<span>Take</span>has a corresponding<span>Give</span>. - Pay special attention to resource release on error paths.
- Semaphore operations are relatively time-consuming; avoid using them in high-frequency code paths.
- For high-frequency events, consider using direct task notifications instead.
Problems and Solutions
Semaphore Count Value Does Not Match Expectations
Possible Reasons:
- Some paths did not correctly release the semaphore.
- Semaphore released in an interrupt but context switching was not handled correctly.
Solutions:
- Check all code paths to ensure the semaphore is released.
- Use
<span>uxSemaphoreGetCount</span>to debug the current count value. - Check the return value of
<span>xSemaphoreGive</span>.
System Hangs Due to Semaphore Operations
Possible Reasons:
- Multiple tasks waiting for each other to hold the semaphore (deadlock).
- High-priority tasks continuously occupying the CPU, preventing low-priority tasks holding the semaphore from running (priority inversion).
Solutions:
- Use the same order to take multiple semaphores.
- Consider using priority inheritance mutexes (if applicable).
- Use timeout mechanisms to avoid infinite waiting.
Semaphore Operations in ISR Are Ineffective
Possible Reasons:
- Used the non-ISR version of the API.
- Did not handle the
<span>pxHigherPriorityTaskWoken</span>flag correctly.
Solutions:
- Ensure to use the
<span>FromISR</span>version of the API in ISR. - Correctly check and handle the
<span>pxHigherPriorityTaskWoken</span>flag.
Conclusion
By using counting semaphores appropriately, efficient and reliable embedded multitasking systems can be built. Key points include:
- Select the appropriate semaphore type (counting or binary) based on the scenario.
- Correctly initialize and clean up semaphore resources.
- Use the correct API version in tasks and ISRs.
- Pay attention to thread safety and deadlock prevention.
- Set reasonable timeouts and error handling mechanisms.

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