4.3.2 Semaphore

Semaphores are used for synchronization and to control access to shared resources between tasks. A semaphore can either be binary or counting and is essentially just a non-negative integer count.

A binary semaphore is initialized to 1 and can be used to guard a resource that can only be handled by one task at a time. When a task takes the resource, the semaphore is decremented to 0. If another task then wants to use the resource and sees that the semaphore is 0, it blocks. When the first task is finished using the resource, the semaphore is incremented and is thus available to other tasks. A binary semaphore can be created with SemaphoreHandle_t semaphoreName = xSemaphoreCreateBinary(void).

A counting semaphore works in the same manner, but for resources that can be used by multiple tasks at once. For example, if you have a parking garage with room for 10 cars, you can allow 10 semaphore access. Every time a car enters, the semaphore will be decremented by 1 until it reaches 0 and no one is allowed entrance before someone leaves. A counting semaphore should be initialized to the number of tasks that can have concurrent access to the resource and is created with SemaphoreHandle_t semaphoreName = xSemaphoreCreateCounting(maxCount, initialCount).

When a task wants a resource protected by a semaphore, it calls the function xSemaphoreTake(semaphoreName, ticksToWait). If the semaphore evaluates to 0, the task will block for the time specified in ticksToWait. When a task is finished using the semaphore, the function xSemaphoreGive(semaphoreName) is called.