Inter Tasks Communication via Queues

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Queues

Code Link: Tutorial Source Code Github – Keil

In the previous tutorial we introduced Queues in general and freeRTOS queues in specific. We introduced freeRTOS APIs for Queues management. The link to the tutorial is given bellow.

It is highly recommended to go through the above tutorial before moving forward with this tutorial as all freeRTOS Queues APIs are explained well there.

Tutorial Scenario:

In this tutorial we will create three freeRTOS Tasks i.e. Tx1, Tx2, Rx1. Two out of them (Tx1, Tx2) will act as transmitter that pass string messages to the third Receiver Task (Rx1). Instead of passing full string, only string pointers are passed to the Receiver Task via Queue. The Receiver Task Receives queue message and print it to the Serial Debugger window.


1. First of all let’s define what messages will be passed to queue. For the purpose of simplicity we will define two global string literals (one for each transmitter Task) shown in bellow code snippet.

  string literals
const char *t1_Msg = "Task-1 Message...";
const char *t2_Msg = "Task-2 Message...";

2. Next we will create freeRTOS Queue of length `1` and accepting item size of Pointer to char (char *). The length of queue is kept `1` because each transmitter Task will only send message pointer (defined in the previous step) to the queue.

	freeRTOS Queue
qHandle = xQueueCreate(1, sizeof(char *));

qHandle is globally defined variable of type QueueHandle_t.

	freeRTOS Queue Handler
QueueHandle_t qHandle;

3. If queue is created successfully (memory is allocated), Three freeRTOS Tasks i.e. “Tx1”, “Tx2”, and “Rx1” are created and Scheduler is started. Otherwise a failed message is sent to serial window and processor is trapped in while(1).

Note: If you don’t know how to create freeRTOS Tasks, refer to the following Tutorial.

  if (qHandle != NULL) {

    xTaskCreate (vTxTask1, "Tx1", 150, NULL, 1, NULL);

    xTaskCreate (vTxTask2, "Tx2", 150, NULL, 1, NULL);
    xTaskCreate (vRxTask, "Rx1", 200, NULL, 2, NULL);
  }else {
    printf ("Failed to create Queue! :-(\n");
    while (1);

Note: If a high priority Task is in Block state waiting for the data to be available in queue, and meanwhile data is written to the queue by low priority Task, the higher priority Task will be immediately unblock to Ready State to receive queue data.

Note: The polarity of Receiver Task (Rx1) is kept higher than the transmitter tasks. The reason is that Queue be read immediately once data is available in Queue to make room for other tasks to write to the queue.

4. “Inside Rx1 Task Function”: The following is the definition of Receiver Task Function.

void vRxTask(void * pvParams) {
  volatile unsigned int i = 0;
  char * msgPtr;
  int rxStatus = 0;
  for (;;) {
    rxStatus = xQueueReceive(qHandle, &msgPtr, 500); /* 500 is enough to compensate dummy delay in transmitter tasks.*/

    if (0 == rxStatus) {
      printf ("Awaiting Message...\n");
    }else {
      printf ("Rx Msg: %s\n", msgPtr);    

What happens is:

rxStatus = xQueueReceive(qHandle, &msgPtr, 500); /* 500 is enough to compensate dummy delay in transmitter tasks.*/

xQueueReceive is called to check if data in qHandle Queue is available. If data is available (pointer to string in this case), the data (pointer) is copied into msgPtr pointer variable otherwise the Receiver Task “Rx1” is sent to Block State waiting for the data to be available in next 500 Ticks. If data is not sent by any task within next 500 Ticks, the message “Awaiting Message…” is displayed and the Receiver Task (Rx1) goes back to Block State. As soon as the data (pointer to string) becomes available on qHandle Queue, the Receiver task (Rx1) receives the pointer and display string pointed by the received pointer and wait for the next message (pointer) reception.

5. “Inside Tx1/2 Tasks Function”: The two transmitter tasks i.e. Tx1, and Tx2 have almost the exact same definitions. They are defined separately just for the purpose of demonstration. Only Tx1 Task function will be discussed here.

void vTxTask1(void * pvParams) {

  volatile int i = 0;
  int txStatus = 0;
  for (;;) {

    printf("Sending Msg-Task-1\n");

    txStatus = xQueueSendToBack(qHandle, &t1_Msg, 2);

    if (0 == txStatus) {
      printf("Sending failed Task-1!\n");
      This will prevent display printf messages too fast.
      also task yelding not required
    for (i = 0; i < 500000; i++);

What happens is:

printf("Sending Msg-Task-1\n");

First of all an alert message is sent to serial window stating that Tx1/2 is about to send message to Queue.

txStatus = xQueueSendToBack(qHandle, &amp;t1_Msg, 2);

Then it calls xQueueSendToBack to send string message pointer (defined in step-1) to qHandle Queue. If space is not available on Queue, it waits for `2` Ticks. The wait is chosen to be `2` because we have only three task (apart from Idle Task) configured in software. Thus the Tx1 can maximumly delay by `2` Ticks. Now if within these `2` Ticks, the data placement on Queue is not successful then a failed message is sent to serial window. The for (i = 0; i < 500000; i++); is a dummy delay to slow down the messages being sent to serial window.

6. Finally Compile source code, connect STM32F4-Discovery board to PC and enter debug mode. Open printf serial window via View -> Serial Windows -> Debug (printf) Viewer. Run the Program, You will see Task execution messages as shown in bellow Video.

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.


[1] – FreeRTOS Official Website

108 thoughts on “Inter Tasks Communication via Queues”

Leave a Reply

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