Index
Mutexes (mutual exclusions) are special types of semaphores used in FreeRTOS for managing access to shared resources between tasks. They ensure that only one task can access a shared resource at a time, helping to prevent data corruption and conflicts in a multitasking environment.
Key Features of Mutexes
- Ownership: Unlike binary semaphores, mutexes have the concept of ownership. Only the task that “takes” the mutex can “give” it back, preventing other tasks from accessing the resource.
- Priority Inheritance: FreeRTOS can temporarily raise the priority of a task holding a mutex if a higher-priority task is waiting for it. This helps prevent priority inversion.
Example: Using Mutexes in FreeRTOS
In this example, we will create two tasks: one that increments a shared counter (the producer) and another that decrements it (the consumer). We will use a mutex to manage access to the shared counter and prevent conflicts.
Step 1: Set Up the Environment
Ensure you have ESP-IDF installed and your environment set up:
. $HOME/esp/esp-idf/export.sh
Step 2: Create a New Project
Create a new folder for your project and initialize it.
Step 3: Write the Code
Here’s a simple FreeRTOS application using a mutex:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#define TASK_COUNT 5
// Declare a mutex handle
SemaphoreHandle_t mutex;
// Shared resource (counter)
volatile int sharedCounter = 0;
// Task that increments the shared counter
void increment_task(void *pvParameter) {
for (int i = 0; i < TASK_COUNT; i++) {
// Wait for the mutex to become available
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) {
// Critical section: Increment the shared counter
sharedCounter++;
printf("Incremented Counter: %d\n", sharedCounter);
// Release the mutex
xSemaphoreGive(mutex);
}
// Simulate work
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
// Task that decrements the shared counter
void decrement_task(void *pvParameter) {
for (int i = 0; i < TASK_COUNT; i++) {
// Wait for the mutex to become available
if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) {
// Critical section: Decrement the shared counter
sharedCounter--;
printf("Decremented Counter: %d\n", sharedCounter);
// Release the mutex
xSemaphoreGive(mutex);
}
// Simulate work
vTaskDelay(150 / portTICK_PERIOD_MS);
}
}
// Main application entry point
void app_main(void) {
// Create the mutex
mutex = xSemaphoreCreateMutex();
// Create the increment and decrement tasks
xTaskCreate(increment_task, "Increment Task", 2048, NULL, 1, NULL);
xTaskCreate(decrement_task, "Decrement Task", 2048, NULL, 1, NULL);
}
Explanation of the Code
- Mutex Declaration:
- A mutex handle
mutex
is declared.
- A mutex handle
- Shared Resource:
- A shared counter variable
sharedCounter
is defined asvolatile
to ensure proper access across multiple tasks.
- A shared counter variable
- Increment Task (
increment_task
):- This task increments the
sharedCounter
. - It waits for the mutex to become available using
xSemaphoreTake()
. If successful, it enters the critical section, increments the counter, and prints the new value. - After finishing the critical section, it releases the mutex with
xSemaphoreGive()
. - A delay simulates some work.
- This task increments the
- Decrement Task (
decrement_task
):- This task decrements the
sharedCounter
. - It waits for the mutex, enters the critical section, decrements the counter, prints the value, and releases the mutex, similar to the increment task.
- A delay simulates some work.
- This task decrements the
- Main Function (
app_main
):- The mutex is created using
xSemaphoreCreateMutex()
. - The increment and decrement tasks are created using
xTaskCreate()
.
- The mutex is created using
Step 4: Build and Flash the Project
- Build the project:
idf.py build
2. Flash the project to the ESP32:
idf.py -p /dev/ttyUSB0 flash
Output Explanation
When you run the project, you will see output similar to this:
Incremented Counter: 1
Decremented Counter: 0
Incremented Counter: 1
Decremented Counter: 0
Incremented Counter: 2
Decremented Counter: 1
...
The output shows that the increment and decrement tasks are operating correctly, updating the sharedCounter
safely using the mutex. The use of xSemaphoreTake()
ensures that only one task can access the shared resource at a time, preventing race conditions.
Summary
In this example, we demonstrated how to use mutexes in FreeRTOS to prevent conflicts when multiple tasks access a shared resource. The mutex ensured that only one task could modify the sharedCounter
at any given time, thus preventing potential data corruption. Understanding how to use mutexes is essential for developing robust and safe multitasking applications in embedded systems.