SoFunction
Updated on 2025-04-14

Basic tutorial on FreeRTOS semaphore API functions

Preface

The semaphores of FreeRTOS include binary semaphores, count semaphores, mutex semaphores (hereinafter referred to as mutex) and recursive mutex semaphores (hereinafter referred to as recursive mutex). We can regard mutexes and recursive mutexes as special semaphores.

Semaphore API functions are actually macros, which use existing queue mechanisms. These macros are defined in the file. If you use semaphores or mutexes, you need to include header files.

The creation API functions of binary semaphores, count semaphores and mutex semaphores are independent, but the acquisition and release API functions are the same; the creation, acquisition and release API functions of recursive mutex semaphores are independent.

1Create a binary semaphore

1.1 Function description

SemaphoreHandle_t xSemaphoreCreateBinary( void );

This function is used to create a binary semaphore. Binary semaphores are either valid or invalid, which is why they are called binary.

The newly created semaphore is in an invalid state, which means that the signal needs to be given before using the API function xSemaphoreTake() to obtain the signal.

Binary semaphores and mutexes are very similar, but there are also subtle differences: mutexes have priority inheritance mechanisms, while binary semaphores do not have this mechanism. This makes binary semaphores more suitable for synchronization (between tasks or between tasks and interrupts), and mutexes are more suitable for interlocking.

Once the binary semaphore is obtained, there is no need to recover, one task or interrupt continuously generates signals, while the other task continuously takes away this signal, and synchronizes in this way.

When a low priority task has a mutex, if another high priority task also attempts to obtain this semaphore, the priority of the low priority task will be temporarily increased to the same priority as the high priority task. This means that the mutex must be released, otherwise the high-priority task will not be able to obtain this mutex, and the low-priority task with the mutex will never be deprived. This is the priority flip in the operating system.

Mutex and binary semaphores are both SemaphoreHandle_t types and can be used in any API function with such parameters.

1.2 Return value

NULL: Failed to create semaphores because the FreeRTOS stack is insufficient. Other values: Semaphore creation was successful. This return value stores the semaphore handle.

1.3 Examples of usage

SemaphoreHandle_t xSemaphore;
void vATask( void * pvParameters )
{
    /* Create semaphore */
   xSemaphore = xSemaphoreCreateBinary();
   if( xSemaphore == NULL )
   {
       /* Due to insufficient stack, semaphore creation failed, failure processing is performed here*/
   }
   else
   {
       /* Semaphores can be used.  The semahore handle is stored in the variable xSemahore.
           If you call the API function xSemahoreTake() here to get the semaphore,
           It must be a failure because the created semaphore is initially invalid (empty).  */
   }
}

2Create count semaphore

2.1 Function description

SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount,
                                 UBaseType_t uxInitialCount )

Create count semaphores, which are usually used in the following two situations:

Event Count: In this application, whenever an event occurs, the event handler will "generate" a semaphore (the semaphore count value will increase), and whenever a task handles an event, a semaphore will be taken away (the semaphore count value will decrease). Therefore, the count value will change after an event occurs or is processed.

Resource Management: In this application scenario, the count value indicates the number of valid resources. In order to obtain resources, the task first needs to obtain a semaphore--decreasing semaphore count value. When the count value is 0, there is no resource available. When the task of occupying the resource is completed, it will release the resource and the corresponding semaphore count value will be increased by one. A count value reaches the initial value (maximum value) means that all resources are available.

2.2 Parameter description

uxMaxCount: The maximum count value, when the signal reaches this value, it no longer increases.

uxInitialCount: The initial value when creating a semaphore.

2.3 Return value

NULL indicates that semaphore creation failed, otherwise the semaphore handle will be returned.

2.4 Examples of usage

void vATask( void * pvParameters )
 {
     xSemaphoreHandle xSemaphore;
     // You must create a semaphore before you can use it     // The maximum value that the semaphore can count is 10, and the initial value of the count is 0.     xSemaphore = xSemaphoreCreateCounting( 10, 0 );
     if( xSemaphore != NULL )
     {
         // Semaphore creation is successful         // Now you can use semaphores.     }
 }

3Create mutexes

3.1 Function description

SemaphoreHandle_t xSemaphoreCreateMutex( void )

Create mutexes.

Mutexes can be accessed using the API functions xSemaphoreTake() and xSemaphoreGive(), but they must not be accessed using xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive().

Binary semaphores and mutexes are very similar, but there are also subtle differences: mutexes have priority inheritance mechanisms, while binary semaphores do not have this mechanism. This makes binary semaphores more suitable for synchronization (between tasks or between tasks and interrupts), and mutexes are more suitable for interlocking.

Once the binary semaphore is obtained, there is no need to recover, one task or interrupt continuously generates signals, while the other task continuously takes away this signal, and synchronizes in this way.

When a low priority task has a mutex, if another high priority task also attempts to obtain this semaphore, the priority of the low priority task will be temporarily increased to the same priority as the high priority task. This means that the mutex must be released, otherwise the high-priority task will not be able to obtain this mutex, and the low-priority task with the mutex will never be deprived. This is the priority flip in the operating system.

Mutex and binary semaphores are both SemaphoreHandle_t types and can be used in any API function with such parameters.

3.2 Return value

NULL indicates that semaphore creation failed, otherwise the semaphore handle will be returned.

3.3 Examples of usage

xSemaphoreHandle xSemaphore;
voidvATask( void * pvParameters )
{
    // Mutex is not available before being created    xSemaphore = xSemaphoreCreateMutex();
    if( xSemaphore != NULL )
    {
        // Created successfully        // This mutex can be used here    }
}

4 Create recursive mutexes

4.1 Function description

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

Used to create recursive mutexes. The created mutex can be used by the API functions xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive(), but cannot be used by the API functions xSemaphoreTake() and xSemaphoreGive().

Mutexes of recursive types can be repeatedly retrieved by the owner. Tasks that have mutexes must call the API function xSemaphoreGiveRecursive() to release all the recursive mutexes they have before the semaphore is truly released. For example, if a task successfully obtains the same mutex 5 times, then this task must release the mutex 5 times before other tasks can obtain it.

Recursive mutexes have priority inheritance mechanism, so a task must perform a release operation after using it after obtaining a signal.

Mutex type signals cannot be used in interrupt service routines.

4.2 Return value

NULL indicates that the mutex creation failed, otherwise the mutex handle will be returned.

4.3 Examples of usage

xSemaphoreHandle xMutex;
void vATask( void * pvParameters )
{
    // Mutex cannot be used before it is created    xMutex = xSemaphoreCreateRecursiveMutex();
    if( xMutex != NULL )
    {
        // Created successfully        // Create mutex here    }
}

5 Delete semaphore

5.1 Function description

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

Delete the semaphore. If a task is blocking this semaphore, do not delete this semaphore.

5.2 Parameter Description

xSemaphore: Semaphore handle

6 Obtain semaphore

6.1 Function description

xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)

Get the semaphore. Semaphores must be pre-created through the API functions xSemaphoreCreateBinary(), xSemaphoreCreateCounting() and xSemaphoreCreateMutex(). Note that recursive mutex type semaphores cannot use this function, and do not use this function in interrupt service programs.

6.2 Parameter Description

xSemaphore: Semaphore handle

xTickToWait: When the semaphore is invalid, the maximum waiting time for the task is the number of system beat cycles. Use the macro portTICK_PERIOD_MS to help convert the number of beats of the system into actual time (in milliseconds). If set to 0, it means that the waiting time is not set. If INCLUDE_vTaskSuspend is set to 1 and the parameter xTickToWait is portMAX_DELAY, you can wait infinitely.

6.3 Return value

The semaphore is successfully obtained and returns pdTRUE, otherwise it returns pdFALSE.

6.4 Examples of usage

SemaphoreHandle_t xSemaphore = NULL;
/*This task creates a semaphore */
void vATask( void * pvParameters )
{
    /* Create mutex semaphores to protect shared resources.  */
    xSemaphore = xSemaphoreCreateMutex();
}
/* This task uses semaphore */
void vAnotherTask( void * pvParameters )
{
    /* ... Do other things. */
    if( xSemaphore != NULL )
    {
        /*If the semaphore is invalid, wait for up to 10 system beat cycles.  */
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
        {
            /*We have obtained the semaphore here and can now access the shared resources*/
            /* ... */
            /* After accessing shared resources, the semaphore must be released*/
            xSemaphoreGive( xSemaphore );
        }
        else
        {
            /* No semaphore was obtained, exceptions are handled here.  */
        }
    }
}

7 Obtain semaphore (with interrupt protection)

7.1 Function description

xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
signedBaseType_t *pxHigherPriorityTaskWoken)

Another version of the API function xSemaphoreTake(), used to interrupt service programs.

7.2 Parameter Description

xSemaphore: Semaphore handle

pxHigherPriorityTaskWoken: If *pxHigherPriorityTaskWoken is pdTRUE, you need to manually perform a context switch before the interrupt exits. Starting with FreeRTOS V7.3.0, this parameter is an optional parameter and can be set to NULL.

7.3 Return value

The semaphore is successfully obtained and returns pdTRUE, otherwise it returns pdFALSE.

8 Get recursive mutex

8.1 Function description

xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );

Gets recursive mutex semaphores. The mutex must be a type created by the API function xSemaphoreCreateRecursiveMutex().

The macro configUSE_RECURSIVE_MUTEXES in the file must be set to 1 for this function to be valid.

Tasks that have obtained recursive mutexes can repeatedly obtain the recursive mutex.

Use the xSemaphoreTakeRecursive() function to successfully obtain several recursive mutexes. You need to use the xSemaphoreGiveRecursive() function to return several times. Before that, the recursive mutexes are in an invalid state. For example, if a task successfully obtains 5 recursive mutexes, then this mutex is invalid for other tasks before it returns 5 recursive mutexes.

8.2 Parameter Description

xMutex: Mutex handle, must be returned using the API function xSemaphoreCreateRecursiveMutex().

xTickToWait: When the mutex is invalid, the maximum waiting time for the task is the number of system beat cycles. Use the macro portTICK_PERIOD_MS to help convert the number of beats of the system into actual time (in milliseconds). If set to 0, it means that the waiting time is not set. If the task already has a semaphore, xSemaphoreTakeRecursive() returns immediately, regardless of the value of xTickToWait.

8.3 Return value

Recursive mutex is successfully obtained and returns pdTURE, otherwise, returns pdFALSE.

8.4 Examples of usage

SemaphoreHandle_t xMutex = NULL;
// This task creates mutexvoid vATask( void *pvParameters )
{
    // This mutex is used to protect shared resources    xMutex =xSemaphoreCreateRecursiveMutex();
}
//This task uses mutexvoid vAnotherTask( void *pvParameters )
{
    // ... Do other things.    if( xMutex != NULL )
    {
        // If the mutex is invalid, wait for up to 10 system clock beat cycles.        if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
            // At this point we have successfully obtained mutexes and can access shared resources            // ...
            // For some reason, some code requires multiple calls to API functions in a task            // xSemaphoreTakeRecursive().  Of course it won't be like this in this case            //Call the actual code will have a more complex structure            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            // We get a mutex three times, so we need to release this mutex three times            //It will become effective.  Once again, the actual code may be more complicated.            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            // At this point, this shared resource can be used by other tasks.        }
        else
        {
            // Handle exceptions        }
    }
}

9 Release semaphore

9.1 Function Description

xSemaphoreGive(SemaphoreHandle_t xSemaphore )

Used to release a semaphore. The semaphore must be created by the API functions xSemaphoreCreateBinary(), xSemaphoreCreateCounting(), or xSemaphoreCreateMutex(). This semaphore must be obtained using the API function xSemaphoreTake().

This function must never be used in interrupt service routines. The same function can be achieved using the API function xSemaphoreGiveFromISR() with interrupt protection version.

This function cannot be used for recursive mutexes created using the API function xSemaphoreCreateRecursiveMutex().

9.2 Parameter Description

xSemaphore: Semaphore handle.

9.3 Return value

The semaphore release is successful and returns pdTRUE, otherwise it returns pdFALSE.

9.4 Examples of usage

SemaphoreHandle_t xSemaphore = NULL;
voidvATask( void * pvParameters )
{
   // Create a mutex to protect shared resources   xSemaphore = xSemaphoreCreateMutex();
 
    if( xSemaphore != NULL )
    {
         if( xSemaphoreGive( xSemaphore ) != pdTRUE )
         {
              //We hope that this function call fails because we need to get the mutex first         }
         // Get the semaphore without waiting         if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
         {
              // Now we have mutexes and can access shared resources safely              if( xSemaphoreGive( xSemaphore ) != pdTRUE )
              {
                   //We do not want this function call to fail because we must                   //To release the acquired mutex              }
         }
     }
}

10 Release semaphore (with interrupt protection)

10.1 Function description

xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken )

Release semaphore. Is another version of the API function xSemaphoreGive(), used to interrupt service programs. Semaphores must be created by the API function xSemaphoreCreateBinary() or xSemaphoreCreateCounting(). There is no mutex here because mutex cannot be used in interrupt service programs.

10.2 Parameter Description

xSemaphore: Semaphore handle

pxHigherPriorityTaskWoken: If *pxHigherPriorityTaskWoken is pdTRUE, a context switch is required before the interrupt is terminated. Starting with FreeRTOS V7.3.0, this parameter is an optional parameter and can be set to NULL.

10.3 Return value

The semaphore is successfully released and returns pdTURE, otherwise it returns errQUEUE_FULL.

10.4 Examples of usage

#define LONG_TIME 0xffff
#define TICKS_TO_WAIT    10
SemaphoreHandle_t xSemaphore = NULL;
/* Repetitive task. */
void vATask( void * pvParameters )
{
    /* We use semaphore synchronization, so we create a binary semaphore first. We must ensure
        The interrupt will not access this binary semaphore until it is created.  */
    xSemaphore = xSemaphoreCreateBinary();
 
    for( ;; )
    {
        /* We want the task to run once for every 10 timer interrupts generated.  */
        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
        {
            /* Successfully obtained the semaphore*/
            ...
           /* We successfully executed it once, because this is a dead loop, so the task will still
                Blocking on waiting for semaphores.  Semaphore is released by the ISR.  */
        }
    }
}
/* Timer ISR */
void vTimerISR( void * pvParameters )
{
    static unsigned char ucLocalTickCount = 0;
    static signed BaseType_txHigherPriorityTaskWoken;
    /*Timer interrupt occurs */
      ...Execute other codes
    /*Does vATask() need to be run? */
    xHigherPriorityTaskWoken = pdFALSE;
    ucLocalTickCount++;
    if( ucLocalTickCount >= TICKS_TO_WAIT )
    {
        /* Release semaphore and release vATask task blocking state */
        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
        /* Reset counter */
        ucLocalTickCount = 0;
    }
    /* If the xHigherPriorityTaskWoken expression is true, a context switch needs to be performed*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

11 Release recursive mutex

11.1 Function Description

xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )

Free a recursive mutex. Mutex must be created using the API function xSemaphoreCreateRecursiveMutex(). The macro configUSE_RECURSIVE_MUTEXES in the file must be set to 1 to be valid.

11.2 Parameter Description

xMutex: Mutex handle. Must be the value returned by the function xSemaphoreCreateRecursiveMutex().

11.3 Return value

If the recursive mutex is released successfully, pdTRUE is returned.

11.4 Examples of usage

See “8 Getting Recursive Mutex”.

12 Get the handle to the mutex hold task

12.1 Function description

TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );

Returns the handle of the mutex holding task (if any), which is specified by the parameter xMutex.

If the task calling this function holds a mutex, then the task handle can be reliably returned, but if another task holds a mutex, it is not always reliable.

The macro configUSE_MUTEXES in the file must be set to 1 to be valid.

12.2 Parameter Description

xMutex: Mutex handle

12.3 Return value

Returns the handle to the mutex hold task. If the parameter xMutex is not a mutex type semaphore or although the mutex is valid, this mutex is not held by any task, then NULL will be returned.

This is the last blog post in the basics of FreeRTOS. At this point, we can already transplant and use FreeRTOS proficiently. But if you want to know the operating mechanism behind FreeRTOS, these are far from enough, and the road to go will be long. Whether to understand the operating mechanism behind FreeRTOS depends entirely on your interests. After all, even if we don’t know the details of the car’s structure, we can drive well as long as we master driving skills. Using RTOS is also similar. As long as we master the basic knowledge, we can already use FreeRTOS well. Exploring the mechanism behind FreeRTOS can allow us to use RTOS more elegantly, make fewer mistakes, and make more slight weight.

The Advanced FreeRTOS article has been written. For more information about FreeRTOS semaphore API functions, please pay attention to my other related articles!