Home > Other >Special Application Circuits > How to implement semaphores and mutexes in FreeRTOS using Arduino

How to implement semaphores and mutexes in FreeRTOS using Arduino

Source: InternetPublisher:三月小雨 Keywords: FreeRTOS Arduino semaphore Updated: 2024/12/24

Semaphores and mutexes (mutex) are kernel objects used for synchronization, resource management, and protecting resources from corruption. In the first half of this tutorial, we will understand the idea behind Semaphore and how and where to use it.

What is a semaphore?

A semaphore is a signaling mechanism where a task in a waiting state is signaled by another task to execute. In other words, when a task1 completes its work, it shows a flag or increments a flag by 1, and then another task (task2) receives this flag indicating that it can now perform its work. When task2 completes its work, the flag is decremented by 1.

So basically, it is a 'give' and 'take' mechanism whereas a semaphore is an integer variable which is used to synchronize the access to a resource.

Semaphore types in FreeRTOS:

There are two types of semaphores.

Binary semaphore

Counting semaphore

1. Binary Semaphore: It has two integer values ​​0 and 1. It is somewhat similar to a Queue with a length of 1. For example, we have two tasks, task1 and task2. Task1 sends data to task2, so task2 keeps checking the queue item, if there is 1, then it can read the data, otherwise it must wait until it becomes 1. After taking the data, task2 decrements the queue to make it 0, that is, task1 can send data to task2 again.

From the above example, it can be said that binary semaphores are used for synchronization between tasks or between tasks and interrupts.

2. Coun ting Semaphore: Its value is greater than 0, and it can be considered as a queue with a length greater than 1. This semaphore is used to count events. In this usage scenario, the event handler will "give" a semaphore (increase the semaphore count value) each time an event occurs, and the handler task will "get" a semaphore (decrease the semaphore count value) each time it processes an event.

Therefore, the count value is the difference between the number of events that have occurred and the number of events that have been processed.

Now, let's see how to use Semaphore in our FreeRTOS code.

How to use semaphores in FreeRTOS?

FreeRTOS supports different APIs for creating semaphores, acquiring semaphores, and providing semaphores .

Now, the same kernel object can have two types of APIs. If we have to provide semaphore from ISR, we cannot use normal semaphore API. You should use interrupt protected API.

In this tutorial, we will use binary semaphore as it is easy to understand and implement. Since interrupt functionality is used here, you need to use interrupt protected API in ISR function. When we say synchronize the task with interrupt, it means put the task in running state immediately after ISR.

Create a semaphore:

To use any kernel object, we must first create it. To create a binary semaphore, use vSemaphoreCreateBinary().

This API does not accept any parameters and returns a variable of type SemaphoreHandle_t. Create a global variable named sema_v to store the semaphore.

SemaphoreHandle_t sema_v;

sema_v = xSemaphoreCreateBinary();

Given a semaphore:

There are two versions of providing semaphores - one for interrupts and another for normal tasks.

xSemaphoreGive(): This API accepts only one parameter which is the variable name of the semaphore, such as sema_v given above while creating the semaphore. It can be called from any normal task you want to synchronize.

xSemaphoreGiveFromISR(): This is the interrupt protected API version of xSemaphoreGive(). When we need to synchronize ISR and normal task, we should use xSemaphoreGiveFromISR() from ISR function.

Get the semaphore:

To take a semaphore, use the API function xSemaphoreTake(). This API has two parameters.

xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToW ait);

xSemaphore: The name of the semaphore to be used, in our case sema_v.

xTicksToWait: This is the maximum time a task waits in a blocked state for the semaphore to become available. In our project, we set xTicksToWait to portMAX_DELAY to make task_1 wait indefinitely in a blocked state until sema_v is available.

Now, let's use these APIs and write code to perform some tasks.

A push button and two LEDs are connected here . The push button will act as an interrupt button connected to pin 2 of Arduino Uno. When this button is pressed, an interrupt will be generated and the LED connected to pin 8 will turn on and when pressed again, it will turn off.

So when the button is pressed, the xSemaphoreGiveFromISR() function is called from the ISR function and the xSemaphoreTake() function is called from the TaskLED function.

To make the system appear to be multitasking, connect an additional LED to pin 7, which will always be blinking.

Semaphore code description

Let's start writing code by opening the Arduino IDE

1. First, include the Arduino_FreeRTOS.h header file. Now, if you use any kernel objects like queues and semaphores, you must also include a header file.

#include

2. Declare a variable of type SemaphoreHandle_t to store the value of the semaphore.

SemaphoreHandle_t interrupt semaphore;

3. In void setup(), create two tasks (TaskLED and TaskBlink) using the xTaskCreate() API, and then create a semaphore using xSemaphoreCreateBinary(). Create a task with the same priority, and then try to use this number. In addition, configure pin 2 as an input and enable the internal pull-up resistor and connect the interrupt pin. Finally, start the scheduler as shown below.

void setup() { 
  pinMode(2,INPUT_PULLUP);
  xTaskCrea

4. Now, implement the ISR functionality. Create a function and name it the same as the second parameter of the attachInterrupt() function. For the interrupt to work properly, you need to use millis or micros functionality and eliminate the debounce issue of the button by adjusting the debounce time. Call the interruptHandler() function from this function as shown below.

Long Debounce Time = 150; 
volatile unsigned long last_micros; 
void debounceInterrupt() { 
if((long)(micros() - last_micros) >= debounce_time * 1000) { 
interruptHandler(); 
last_micros = micros(); 
} 
}

In the interruptHandler() function, call the xSemaphoreGiveFromISR() API.

void interruptHandler() { 
xSemaphoreGiveFromISR(interruptSemaphore, NULL); 
}

This function will give TaskLed a semaphore to turn on the LED.

5. Create a TaskLed function and call the xSemaphoreTake() API in a while loop and check if the semaphore is taken successfully. If it is equal to pdPASS (i.e. 1), then make the LED toggle as shown below.

void TaskLed(void *pvPa

6. Also, create a function to blink the other LED connected to pin 7.

void TaskLed1(void *pvParameters) 
{ 
(void) pvParameters; 
pinMode(7, OUTPUT);
And (1) {
digitalWrite(7,HIGH);
vTaskDelay(200 / portT

7. The void loop function will remain empty. Don't forget it.

void loop(){}

That's it, the complete code can be found at the end of this tutorial. Now, upload this code and connect the LED and button with Arduino UNO according to the circuit diagram.

Circuit Schematic

poYBAGL7SCGAG-2xAAQwgqHUXRE555.png

After uploading the code, you will see one LED blinking after 200 milliseconds and when the button is pressed, the second LED will light up instantly as shown in the video given at the end.

pYYBAGL7SB2AZJtlAAZ3UKhATq8320.png

In this way, semaphores can be used in FreeRTOS with Arduino, which needs to pass data from one task to another without any loss.

Now, let's see what is Mutex and how to use it in FreeRTOS.

What is a mutex?

As mentioned above, Semaphore is a signaling mechanism, similarly Mutex is a locking mechanism, unlike Semaphore, Semaphore has separate increment and decrement functions, but in Mutex, the function itself accepts and gives. This is a technique to avoid corruption of shared resources.

In order to protect a shared resource, a token card (mutex) needs to be assigned to the resource. The person who has this card can access other resources. Others should wait until the card is returned. In this way, only one resource can access the task, and the others wait for their chance.

Let us understand Mutex in FreeRTOS through an example.

Here we have three tasks, one for printing data on LCD, second for sending LDR data to LCD task and last task for sending temperature data on LCD. So here two tasks are sharing the same resource i.e. LCD. If LDR task and temperature task send data at the same time then one of the data may get corrupted or lost.

Therefore, to prevent data loss, we need to lock the LCD resource of task1 until it completes the display task. Then the LCD task will unlock, and task2 can then perform its work.

You can observe the working of mutex and semaphore in the following figure.

pYYBAGL7SBiAaPDtAAFXiAZad48919.png

How to use mutex in FreeRTOS?

Mutexes are used in the same way as semaphores. First, create it, then use the respective APIs to provide and obtain it.

Create a mutex:

To create a mutex, use the xSemaphoreCreateMutex() API. As the name suggests, a mutex is a type of binary semaphore. They are used in different contexts and purposes. Binary semaphores are used to synchronize tasks, whereas Mutex is used to protect shared resources.

This API does not accept any parameters and returns a variable of type SemaphoreHandle_t. If the mutex cannot be created, xSemaphoreCreateMutex() returns NULL.

SemaphoreHandle_t mutex_v;

mutex_v = xSemaphoreCreateMutex();

Take a mutex lock:

When a task wants to access a resource, it acquires the Mutex using the xSemaphoreTake() API. It is the same as a binary semaphore. It also takes two parameters.

xSemaphore: The name of the Mutex used in our example is mutex_v.

xTicksToWait: This is the maximum time that a task waits in the Blocked state for Mutex to become available. In our project, we set xTicksToWait to portMAX_DELAY to make task_1 wait indefinitely in the Blocked state until mutex_v becomes available.

Provides a mutex lock:

After accessing the shared resource, the task should return the Mutex so that other tasks can access it. xSemaphoreGive() API is used to return the Mutex.

The xSemaphoreGive() function accepts only one argument which is the Mutex given in our case mutex_v.

Using the above API, let us implement Mutex in FreeRTOS code using Arduino IDE.

Mutually exclusive code description

The goal of this part is to use the serial monitor as a shared resource and two different tasks to access the serial monitor to print some messages.

1. The header file will remain the same as for semaphores.

#include

2. Declare a variable of type SemaphoreHandle_t to store the value of Mutex.

SemaphoreHandle_t mutex_v;

3. In void setup(), initialize the serial monitor at 9600 baud rate and create two tasks (Task1 and Task2) using xTaskCreate() API. Then create a Mutex using xSemaphoreCreateMutex(). Create a task with the same priority and then try to use this number.

void setup() {
Serial.Start(9600);
mutex_v = xSemaphoreCreateMutex(); 
if (mutex_v == NULL) { 
Serial.println("Unable to create mutex"); 
} 
xTaskCreate(Task1, "Task 1", 128, NULL, 1, NULL); 
xTaskCreate(Task2, "Task 2", 128, NULL, 1, NULL); 
}

4. Now, make task functions for Task1 and Task2. In the while loop of the task function, before printing the message on the serial monitor, we have to get the Mutex using xSemaphoreTake(), then print the message, and then return the Mutex using xSemaphoreGive(). Then delay for some time.

void Task1(void *pvParameters) { 
while(1) { 
xSemaphoreTake(mutex_v, portMAX_DELAY); 
Serial.println("Hello from Task1"); 
xSemaphoreGive(mutex_v); 
vTaskDelay(pdMS_TO_TICKS(1000)); 
} 
}

Similarly, implement the Task2 function with a delay of 500ms.

5. void loop() will remain empty.

Now, upload this code to Arduino UNO and open the serial monitor.

You will see messages being printed from both task1 and task2.

poYBAGL7SBCAUrmqAAElUa0KgQI681.png

To test the working of Mutex, just comment xSemaphoreGive(mutex_v); from any task. You can see that the program hangs on the last print message.

poYBAGL7SAuAFdn1AAF9_V6mP5g682.png

This is how semaphores and mutexes are implemented in FreeRTOS using Arduino.

Semaphore code:

#include

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号