FreeRTOS binary semaphores and counting semaphores

1. Binary semaphore and counting semaphore

binary semaphore

concept

  • Queue of length 1

  • There is no need to care about the storage content, just whether it is empty or not.

Application scenarios

  • Synchronization of tasks and tasks, tasks and interrupts

block

  • When the task obtains the semaphore, if the semaphore is empty, it enters the blocking state (immediate, scheduled, permanent)

  • When the task releases the semaphore, if the semaphore is full, an error will be returned.

counting semaphore

concept

  • Queue with length greater than 1

  • There is no need to care about the storage content, just whether it is empty or not.

Application scenarios

  • Event counting: In this case, every time an event occurs, the semaphore is released in the event processing function (the count value of the semaphore is increased), and other tasks will obtain the semaphore (the count value of the semaphore is decremented by one) to process the event. The initial count value of the counting semaphore created in this situation is 0

  • Resource management: In this case, the semaphore value represents the current available number of resources, such as the current number of parking spaces remaining in the parking lot. If a task wants to obtain the right to use resources, it must first obtain the semaphore. After the semaphore is successfully obtained, the semaphore value will be reduced by one. When the semaphore value is 0, it means there are no resources. When a task finishes using resources, it must release the semaphore. After releasing the semaphore, the semaphore value will be increased by one. The initial value of the counting semaphore created in this occasion should be the number of resources. For example, if there are 100 parking spaces in the parking lot, then the semaphore value should be initialized to 100 when the semaphore is created.

block

  • When the task obtains the semaphore, if the semaphore is empty, it enters the blocking state (immediate, scheduled, permanent)

  • When the task releases the semaphore, if the semaphore is full, an error will be returned.

2. Related API functions

2.1, Create

SemaphoreHandle_t xSemaphoreCreateBinary(void)

  • Create a binary semaphore

Parameters

Description

void

null

Return value

Description

NULL

Failed to create binary semaphore

other values

Create binary semaphore successfully

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount)

  • Create a counting semaphore

Parameters

Description

uxMaxCount

The maximum value of the counting signal. When the semaphore value is equal to this value, releasing the semaphore will fail.

uxInitialCount

Counting semaphore initial value

Return value

Description

NULL

Counting semaphore creation failed

other values

The counting semaphore is created successfully and the counting semaphore handle is returned.

2.2, Release

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)

  • Release binary/counting semaphore

Parameters

Description

xSemaphore

The semaphore is to be released. This parameter is a variable of type SemaphoreHandle_t and must be created before use.

Return value

Description

pdPASS

Semaphore released successfully

pdFAIL

The semaphore release fails. If the semaphore is not acquired immediately, it will fail when releasing the semaphore again.

BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHighPriorityWoken)

  • Release binary/counting semaphore in interrupt

Parameters

Description

xSemaphore

The semaphore is to be released. This parameter is a variable of type SemaphoreHandle_t and must be created before use.

pxHighPriorityWoken

If there are multiple tasks waiting to obtain the semaphore and enter the blocking state, calling this function can unblock the tasks. If there is a task in the queue with a higher priority than the unblocked task, or the two have the same priority, then the value of pxHighPriorityWoken will be set to pdTRUE. If xpHighPriorityWoken is pdTRUE, a task switch must be performed before exiting the interrupt service function

Return value

Description

pdTRUE

Semaphore released successfully

pdFAIL

The semaphore release failed because the semaphore has been released but has not been acquired.

2.3, Obtain

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)

  • Get binary/counting semaphore

Parameters

Description

xSemaphore

The semaphore to be obtained

xBlockTime

blocking time

Return value

Description

pdTRUE

Obtaining semaphore successfully

pdFAIL

Retrieval timed out or failed

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, BaseType_t *pxHighPriorityWoken)

  • Get binary/counting semaphore in interrupt

Parameters

Description

xSemaphore

The semaphore to be obtained

pxHighPriorityWoken

Mark whether task switching is required before exiting the interrupt service function. The value is set by the function, the user only provides the stored variable. If the value of pxHighPriorityWoken is pdTRUE, a task switch must be performed before exiting the interrupt function.

Return value

Description

pdPASS

Obtaining semaphore successfully

pdFALSE

Failed to obtain semaphore

3. Experiment

3.1. Experimental design

  • Create a start task. In the start task, create the binary semaphore, task 1 and task 2, and then delete the start task;

  • After task 1 runs 10 times, a semaphore is released;

  • Task 2 keeps getting the semaphore, and after getting it, prints the information;

3.2. Experimental procedures

k_task_handle_t task1_handle; //task 1 handle
#define TSK1_PRIO 3 //task 1 priority
#define TASK1_STK_SIZE (1*1024) //The stack size allocated by task 1

k_task_handle_t task2_handle; //task 2 handle
#define TSK2_PRIO 2 //task 2 priority
#define TASK2_STK_SIZE (1*1024) //The stack size allocated by task 2

k_task_handle_t start_task_handle; //start task handle
#define START_TSK_PRIO 1 //start task priority
#define START_TSK_STK_SIZE (1*1024) //Start task allocated stack size

#define TEST_TIME_QUANTA 100

k_sem_handle_t g_usSem; //Semaphore

void task1(void)
{
        uint8_t count = 0;
        while(1)
        {
                count + + ;
                my_printf("task 1 run %d times!!!\r\\
", count);
                
                if(count == 10)
                {
                        if(0 != csi_kernel_sem_post(g_usSem))
                        {
                                my_printf("---task 1 give sem fail!!!\r\\
");
                        }
                        else
                        {
                                my_printf("---task 1 give sem sucess!!!\r\\
");
                        }
                        
                        count = 0;
                }
                
                csi_kernel_delay_ms(1000);
        }
}

void task2(void)
{
        while(1)
        {
                if(0 != csi_kernel_sem_wait(g_usSem, -1))
                {
                        my_printf("task 2 take sem fail!!!\r\\
");
                }
                else
                {
                        my_printf("task 2 take sem sucess,task 2 is working now!!!\r\\
");
                }
        }
}

void start_task(void)
{
    //Enter the critical section
    taskENTER_CRITICAL();
    
    //Create a binary semaphore
    g_usSem = csi_kernel_sem_new(1, 0);
    if (g_usSem == NULL)
    {
        printf("fail to create semaphore.\\
");
    }

    //Create task 1
    csi_kernel_task_new((k_task_entry_t)task1,
                                     "task1",
                                       NULL,
                                  TSK1_PRIO,
                           TEST_TIME_QUANTA,
                                       NULL,
                             TASK1_STK_SIZE,
                                &task1_handle);

    if (task1_handle == NULL)
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 1!\r\\
");
    }

    //Create task 2
    csi_kernel_task_new((k_task_entry_t)task2,
                                     "task2",
                                         NULL,
                                    TSK2_PRIO,
                             TEST_TIME_QUANTA,
                                         NULL,
                               TASK2_STK_SIZE,
                                &task2_handle);

    if (task2_handle == NULL)
    {
        csi_kernel_sched_resume(0);
        my_printf("Fail to create task 2!\r\\
");
    }

        //Delete the start task
    if(0 != csi_kernel_task_del(csi_kernel_task_get_cur()))
    {
         my_printf("Fail to delete start_task!\r\\
");
    }

     //Exit critical section
     taskEXIT_CRITICAL();
}


void freertos_demo(void)
{

    my_printf("\r\\
-->this is freertos task test demo!!!\r\\
");
    //system initialization
    csi_kernel_init();

    //Create a start task
    csi_kernel_task_new((k_task_entry_t)start_task,
                                      "start_task",
                                                 0,
                                    START_TSK_PRIO,
                                                 0,
                                                 0,
                                START_TSK_STK_SIZE,
                                &start_task_handle);

    //Task scheduling starts
    csi_kernel_start();
}

3.3. Experimental phenomena

  • After the start task is created, task 1 and task 2 will be deleted immediately after it is created.

  • In the initial stage, task 1 is running, and task 2 is in a blocked state because the semaphore obtained at this time has not been released yet.

  • After task 1 runs 10 times, a semaphore is released. After task 2 obtained the semaphore, it executed the printing action

  • At this time, the operation of task 2 is controlled by the semaphore. It is blocked before acquiring the semaphore, fully releasing the CPU usage rights.