FreeRTOS Tutorial #7 -> Using MUTEX

There is no tutorial #6. 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

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 Function

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, &MPT_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 Function 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, &MPT_Handler);
  xTaskCreate(LPT_Task, "LPT", 128, NULL, 1, &LPT_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.

HPT even being the Highest priority Task, have to wait for the MPT to finish. 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 is called 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 the priority of LPT is HIGHER than MPT and the execution goes as planned.

Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

5 Comments. Leave new

  • Thanks!

    Reply
  • Gerhard van Rensburg
    June 5, 2021 2:26 AM

    In FreeRTOS Tutorial 7.1 || MUTEX vs Bin Semaphore || Priority inversion || Priority InheritanceHow is the Priority Inherited?
    The only code changes was to change from Semaphore to Mutex.
    So will Mutex always have Priority inheritance?
    And Semaphore will cause Priority inversion?

    Any help appreciated

    Reply
  • Gerhard van Rensburg
    June 4, 2021 6:18 PM

    Dear CT,
    Why do you use HPT_Hndler x3 times
     /// 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);

    Is this a mistake, since you need to use HPT_Handler, MPT_Handler, LPT_Handler??

    Any help appreciated

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×