FreeRTOS Tutorial #7 -> Using MUTEX

Description

There is no tutorial #6, and after no #5 we have #7 directly.

Mutex, which is short for Mutual Exclusion, does what’s it name indicates. It prevents several tasks from accessing a resource mutually. It ensures that at one time, only one task have access to the resource.
In this tutorial, we will see how to use mutex. And i will also explain the difference between a mutex and a binary semaphore. Also we will learn about priority inversion and priority inheritance.

Simple Mutex Operation

First of all let’s see how the mutex is used.

SemaphoreHandle_t SimpleMutex;

TaskHandle_t HPT_Handler;
TaskHandle_t MPT_Handler;

void HPT_Task (void *argument);
void MPT_Task (void *argument);



void Send_Uart (uint8_t *str)
{
	xSemaphoreTake(SimpleMutex, portMAX_DELAY);
	HAL_Delay(2000);
	HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);
	xSemaphoreGive(SimpleMutex);
}

In the code above, I have created a mutex handler (SimpleMutex), two task handlers, and defined the task functions.

Along with that, there is a function (Send_Uart), which will acquire the mutex first, waits for 2 seconds, sends the data to the UART, and releases the mutex.

Now, let’s write the Task functions

void HPT_Task (void *argument)
{
	uint8_t *strtosend = "IN HPT===========================\n";
	while (1)
	{
		char *str = "Entered HPT and About to take MUTEX\n";
		HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);

		Send_Uart(strtosend);

		char *str2 = "Leaving HPT\n\n";
		HAL_UART_Transmit(&huart2, str2, strlen (str2), HAL_MAX_DELAY);

		vTaskDelay(2000);
	}
}

void MPT_Task (void *argument)
{
	uint8_t *strtosend = "IN MPT...........................\n";
	while (1)
	{
		char *str = "Entered MPT and About to take MUTEX\n";
		HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);

		Send_Uart(strtosend);

		char *str2 = "Leaving MPT\n\n";
		HAL_UART_Transmit(&huart2, str2, strlen (str2), HAL_MAX_DELAY);

		vTaskDelay(1000);
	}
}

Above are the functions for two tasks. Both of them will make a call to the function, Send_Uart. Now we have to see, can the High Priority Task (HPT) preempt the Medium Priority Task (MPT) while the MPT holds the MUTEX.

We will write the following into the main function

  SimpleMutex = xSemaphoreCreateMutex();

  if (SimpleMutex != NULL)
  {
	  HAL_UART_Transmit(&huart2, "Mutex Created\n\n", 15, 1000);
  }

  /// create tasks

  xTaskCreate(HPT_Task, "HPT", 128, NULL, 3, &HPT_Handler);
  xTaskCreate(MPT_Task, "MPT", 128, NULL, 2, &HPT_Handler);

  vTaskStartScheduler();

We have created the MUTEX, along with two tasks with different priority. And at last, the scheduler will start.

Let’s see the output of the above program.

mutex output
mutex output

This was a very simple operation of mutex. And this is exactly what a binary semaphore does too. That’s why we will go a little more deep, and see what’s the difference between the two.

Priority Inversion in Semaphore

For this purpose, I will make some changes in the code again.

SemaphoreHandle_t SimpleMutex;
SemaphoreHandle_t BinSemaphore;


TaskHandle_t HPT_Handler;
TaskHandle_t MPT_Handler;
TaskHandle_t LPT_Handler;


void HPT_Task (void *argument);
void MPT_Task (void *argument);
void LPT_Task (void *argument);


void Send_Uart (uint8_t *str)
{
	xSemaphoreTake(BinSemaphore, portMAX_DELAY);
	HAL_Delay(5000);
	HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);
	xSemaphoreGive(BinSemaphore);
}

This time I have defined a handler for the binary semaphore also (BinSemaphore). Also all the three tasks are being used now.

let’s write the task functions now

void HPT_Task (void *argument)
{
	uint8_t *strtosend = "IN HPT===========================\n";
	while (1)
	{
		char *str = "Entered HPT and About to take Semaphore\n";
		HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);

		Send_Uart(strtosend);

		char *str2 = "Leaving HPT\n\n";
		HAL_UART_Transmit(&huart2, str2, strlen (str2), HAL_MAX_DELAY);

		vTaskDelay(750);
	}
}


void MPT_Task (void *argument)
{
	while (1)
	{
		char *str = "IN MPT****************************\n\n";
		HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);
		vTaskDelay(2000);
	}
}

void LPT_Task (void *argument)
{
	uint8_t *strtosend = "IN LPT...........................\n";
	while (1)
	{
		char *str = "Entered LPT and About to take Semaphore\n";
		HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);

		Send_Uart(strtosend);

		char *str2 = "Leaving LPT\n\n";
		HAL_UART_Transmit(&huart2, str2, strlen (str2), HAL_MAX_DELAY);

		vTaskDelay(1000);
	}
}

As you can see above, the HPT, and the LPT will call the function Send_Uart, so they will require the semaphore. But MPT will run independently without any need for the semaphore.

Let’s write the main function now

  SimpleMutex = xSemaphoreCreateMutex();

  if (SimpleMutex != NULL)
  {
	  HAL_UART_Transmit(&huart2, "Mutex Created\n\n", 15, 1000);
  }

  BinSemaphore = xSemaphoreCreateBinary();
  if (BinSemaphore != NULL)
  {
	  HAL_UART_Transmit(&huart2, "Semaphore Created\n\n", 19, 1000);
  }

  xSemaphoreGive(BinSemaphore);

  /// create tasks

  xTaskCreate(HPT_Task, "HPT", 128, NULL, 3, &HPT_Handler);
  xTaskCreate(MPT_Task, "MPT", 128, NULL, 2, &HPT_Handler);
  xTaskCreate(LPT_Task, "LPT", 128, NULL, 1, &HPT_Handler);

  vTaskStartScheduler();

Both the mutex and semaphore were created along with three tasks of different priorities. And at last, the scheduler will start.

Let’s see the output of the above

priority inversion output
priority inversion output
priority inversion

You can see in the above pictures, the MPT can preempt the LPT, and therefore it delays the execution of the HPT also. This scenario is termed as PRIORITY INVERSION

Now let’s see how can we use mutex to avoid it.

Priority Inheritance using Mutex

I will just make a very small change in the code

void Send_Uart (uint8_t *str)
{
	xSemaphoreTake(SimpleMutex, portMAX_DELAY);
	HAL_Delay(5000);
	HAL_UART_Transmit(&huart2, str, strlen (str), HAL_MAX_DELAY);
	xSemaphoreGive(SimpleMutex);
}

In the Send_Uart function, instead of taking the semaphore, now we will take the mutex. The rest of the code will remain exactly as it is.

Let’s see the output now

priority inheritance output
priority inheritance output
priority inheritance
priority inheritance

As shown above, when the LPT have the Mutex and HPT tries to preempt it, the priority of LPT rises to that of the HPT. This scenario is termed as PRIORITY INHERITANCE as LPT inherits the priority of the highest priority task, that is waiting for the mutex. And in this case that is HPT.

Now the MPT can not preempt LPT, because it’s priority is HIGHER than MPT and the execution goes as planned.

100%
100%
Check out the VIDEO Below

100%
100%

DOWNLOAD

You can buy me a coffee sensor ūüôā

download the CODE below

100%
100%
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
Menu
0
Would love your thoughts, please comment.x
()
x