FreeRTOS Tutorial #5 ->Using Queue

This is the Fifth tutorial in the series of FreeRTOS, and today in this tutorial we are going to learn how to use Queue to communicate between the tasks. You can check the other tutorials on FreeRTOS by going HERE. Beloe is the picture of How the Queue works, and it’s self explanatory.

Reference: Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

Queue is the easiest way to send and receive data between the tasks. We are going to use the simple queue at first, where all the elements in the Queue are of same data types, and later we will use a structured Queue, where the data types can be different.

Simple Queue

As I mentioned, in a simple Queue all the elements are of same type. For example a Queue can only hold 5 integers, or 6 characters, or 3 unsigned integers etc.

A Queue is recognized by it’s handler, so first of all we need to create a handler for the Queue

/**************** QUEUE HANDLER ***********************/
xQueueHandle SimpleQueue;

Next, inside the main function, we will create a Queue, which can store 5 integers

 /************************* Create Integer Queue ****************************/
  SimpleQueue = xQueueCreate(5, sizeof (int));
  if (SimpleQueue == 0)  // Queue not created
  {
	  char *str = "Unable to create Integer Queue\n\n";
	  HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);
  }
  else
  {
	  char *str = "Integer Queue Created successfully\n\n";
	  HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);
  }

If there is an error while creating a Queue, like shortage of memory, than the xQueueCreate function return a ‘0’. Otherwise it will return any other value. The above strings will be printed on the console based on if the Queue was created successfully or not.


Inside the sender task function, we can send the data to the Queue using the following

void Sender_HPT_Task (void *argument)
{
	int i=222;
	uint32_t TickDelay = pdMS_TO_TICKS(2000);
	while (1)
	{
		if (xQueueSend(SimpleQueue, &i, portMAX_DELAY) == pdPASS)
		{
			char *str2 = " Successfully sent the number to the queue\nLeaving SENDER_HPT Task\n\n\n";
			HAL_UART_Transmit(&huart2, (uint8_t *)str2, strlen (str2), HAL_MAX_DELAY);
		}
		vTaskDelay(TickDelay);
	}
}

The parameters of xQueueSend are handler to the Queue, the address of the data to send, and the waiting time incase the Queue is full. I have specified the waiting as portMAX_DELAY, that means the task is going to wait forever for the space to become available in the Queue. For this waiting time, this task will be in the suspension.

If the data is sent successfully, the xQueueSend function will return pdPASS, and we can print the string for the confirmation.


On the other hand, RECEIVER TASK will receive this data, and store it in a variable.

void Receiver_Task (void *argument)
{
	int received=0;
	uint32_t TickDelay = pdMS_TO_TICKS(3000);
	while (1)
	{
		if (xQueueReceive(SimpleQueue, &received, portMAX_DELAY) != pdTRUE)
		{
			HAL_UART_Transmit(&huart2, (uint8_t *)"Error in Receiving from Queue\n\n", 31, 1000);
		}
		else
		{
			sprintf(str, " Successfully RECEIVED the number %d to the queue\nLeaving RECEIVER Task\n\n\n",received);
			HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);
		}
		vTaskDelay(TickDelay);
	}
}

The parameters of xQueuReceive are handler to the Queue, the address where the data is to be stored, and the waiting time incase the Queue is Empty. I have specified the waiting as portMAX_DELAY, that means the task is going to wait forever for the data to become available in the Queue. For this waiting time, this task will be in the suspended State.

Again, if the data is received successfully, xQueueReceive will return pdTRUE, and we can display the data on the console.


If we want to send the data to the Queue from an ISR, we have to use the interrupt safe version of these functions. Below is an example of How to send the data to the Queue from an ISR

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Receive_IT(huart, &Rx_data, 1);
	int ToSend = 123456789;
	if (Rx_data == 'r')
	{
		 /* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
		 it will get set to pdTRUE inside the interrupt safe API function if a
		 context switch is required. */
		BaseType_t xHigherPriorityTaskWoken = pdFALSE;

		if (xQueueSendToFrontFromISR(SimpleQueue, &ToSend, &xHigherPriorityTaskWoken) == pdPASS)
		{
			HAL_UART_Transmit(huart, (uint8_t *)"\n\nSent from ISR\n\n", 17, 500);
		}

		/* Pass the xHigherPriorityTaskWoken value into portEND_SWITCHING_ISR(). If
		 xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR()
		 then calling portEND_SWITCHING_ISR() will request a context switch. If
		 xHigherPriorityTaskWoken is still pdFALSE then calling
		 portEND_SWITCHING_ISR() will have no effect */

		portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
	}
}

xQueueSendToFrontFromISR will send the data to the front of the Queue. All the data, which is already available in the queue, will shift back and next time if we read the Queue, we will get this particular data.

Also note that there is no waiting time here. So if the Queue is full, the function will simply timeout, as in the ISR, we can’t afford to wait for the space to become available in the Queue.


Below Shown is the result of this code.

Simple Queue Result
Simple Queue Result

Check out the Video Below






Structured Queue

Like I mentioned in the beginning, if we want to send the different data types, we have to use the Structured Queue.

First of all create the handler for this Queue

/**************** QUEUE HANDLER *****************/
xQueueHandle St_Queue_Handler;

We need to create a structure which can store all the data types that we want to use. I will call it my_struct

/**************** STRUCTURE DEFINITION *****************/

typedef struct {
	char *str;
	int counter;
	uint16_t large_value;
} my_struct;

Next, inside the main function, create the Queue

  /***** create QUEUE *****/
  St_Queue_Handler = xQueueCreate(2, sizeof (my_struct));

  if (St_Queue_Handler == 0) // if there is some error while creating queue
  {
 	  char *str = "Unable to create STRUCTURE Queue\n\n";
 	  HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);
   }
  else
   {
 	  char *str = "STRUCTURE Queue Created successfully\n\n";
 	  HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);
   }

Here I have created a Queue which can store 2 elements of my_struct data type. Again if there is some error while creating Queue, the xQueueCreate will return 0, or else it will return any other value.


We will now send the data to this Queue from a sender Task

void Sender1_Task (void *argument)
{
	my_struct *ptrtostruct;

	uint32_t TickDelay = pdMS_TO_TICKS(2000);
	while (1)
	{
		char *str = "Entered SENDER1_Task\n about to SEND to the queue\n\n";
		HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);

		/****** ALOOCATE MEMORY TO THE PTR ********/
		ptrtostruct = pvPortMalloc(sizeof (my_struct));

		/********** LOAD THE DATA ***********/
		ptrtostruct->counter = 1+indx1;
		ptrtostruct->large_value = 1000 + indx1*100;
		ptrtostruct->str = "HELLO FROM SENDER 1 ";

		/***** send to the queue ****/
		if (xQueueSend(St_Queue_Handler, &ptrtostruct, portMAX_DELAY) == pdPASS)
		{
			char *str2 = " Successfully sent the to the queue\nLeaving SENDER1_Task\n\n\n";
			HAL_UART_Transmit(&huart2, (uint8_t *)str2, strlen (str2), HAL_MAX_DELAY);
		}

		indx1 = indx1+1;

		vTaskDelay(TickDelay);
	}
}

Before sending the data to the Queue, we must allocate the memory to the structure. And to do that, we use the function pvPortMALLOC.

After allocating the memory, Load the data into the pointer to this struct.


Next, we will send this data to the Queue by passing it’s address in the parameter.

void Receiver_Task (void *argument)
{
	my_struct *Rptrtostruct;
	uint32_t TickDelay = pdMS_TO_TICKS(3000);
	char *ptr;

	while (1)
	{
		char *str = "Entered RECEIVER Task\n about to RECEIVE FROM the queue\n\n";
		HAL_UART_Transmit(&huart2, (uint8_t *)str, strlen (str), HAL_MAX_DELAY);

		/**** RECEIVE FROM QUEUE *****/
		if (xQueueReceive(St_Queue_Handler, &Rptrtostruct, portMAX_DELAY) == pdPASS)
		{
			ptr = pvPortMalloc(100 * sizeof (char)); // allocate memory for the string

			sprintf (ptr, "Received from QUEUE:\n COUNTER = %d\n LARGE VALUE = %u\n STRING = %s\n\n\n",Rptrtostruct->counter,Rptrtostruct->large_value, Rptrtostruct->str);
			HAL_UART_Transmit(&huart2, (uint8_t *)ptr, strlen(ptr), HAL_MAX_DELAY);

			vPortFree(ptr);  // free the string memory
		}

		vPortFree(Rptrtostruct);  // free the structure memory

		vTaskDelay(TickDelay);
	}
}

We will create another pointer to struct in the receiver function to store the received data.
After receiving the data, we will free the memory allocated by the sender Task using vPortFREE function.



The result for the above code is shown below

structured Queue result
structured Queue result

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.

8 Comments. Leave new

  • Hi, your tutorials have been very helpful, Thank you..

    I’m confused about about the structured queue example though,

    The queue was created to hold an element size of a copy of the struct.

    xQueueCreate(2, sizeof (my_struct));

    But you enqueue just a pointer in the sender.

    When you dequeue the pointer in the receive, won’t you be copying additional data and overwriting ram?

    Thanks.

    Reply
    • That’s why I used malloc. It will allocate only the amount of memory required by my_struct. And in the sender, we send the address of this memory location.
      After receiving from the queue, this memory location will be freed.
      you can check the video for proper demonstration of this scenario

      Reply
      • I understand that you are sending the pointer to the actual struct through the queue.

        I guess my specific question is why the queue is initially created with an item size of the structure and not the size of a pointer to the structure?

        Am I misunderstanding xQueueCreate?

        Thank you.

        Reply
        • oh… Well it’s because we don’t want to create a queue, whose size is same as pointer, rather we want to create for the size of my_struct.

          Pointers have fixed size. It doesn’t vary based on where it is pointing to.

          Reply
  • Another grat tutorial 🙂

    Reply
  • Hi,
    I have been following your tutorials for a long time ago . They are very instructive.
    I’m using freeRTOS for STM32 under cubeMX IDE with cmsis_os2.
    They are some differencies between your tutos and cmsis_os2 but that allows me to see differents way to do the same thing.
    About ISR, I’m facing a wierd(?) way to implement them because cmcsis_os2 doesn’t allow us to use directly ” xQueueSendToFrontFromISR” and I looked for what kind of function I could use instead… But there is none.
    I found that “osMessageQueuePut” that I used to use already implement the case if we are inside an ISR or not.
    Am I right? so, If I well understood, cmsis_os2 do the job for me.
    The thing I didn’t understand is that function put the value at the end of the queue whenever we are in ISR or not.
    Maybe, can I have your opinion?
    Thank you again,
    Laurent

    Reply
    • yes, CMSIS don’t have separate API for ISR. They have implemented it as a condition inside the regular functions itself
      regarding the position of the message, the third parameter of osMessageQueuePut, i.e. msg_prio is used to sort message according their priority (higher numbers indicate a higher priority) on insertion.

      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.

×