Check the operation of STM32 FreeRTOS mail queue

In this article, I will use the FreeRTOS mail queue.

Here is the development environment at the time of submission.

PC: Windows 10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
Board: STM32Nucleo-F401RE

Mail queues are one of the items used in multi-threaded programs for data communication between threads (also called inter-task communication).

I wrote an article on message queues a while ago.
The message queue is light and convenient for sending small amounts of data, but if you want to handle large amounts of data, use the mail queue.

Create an IDE project

Select File – New -STM32 Project, select NUCLEO-F401RE from the Boart Selector tab and press the Next button.

The Project Name should be RtosMailQueue.
The language should be C, so press Finish.

When it asks “Initialize all peripherals with their default Mode?”, press Yes.
This kinod of project is associated with the STM32CubeMx perspectiove.
Press Yes when it asks “Do you want to open this perspective now?

Add FreeRTOS

Check Middleware – FREERTOS in Pinout & Configuration – Categories.
Select CMSIS_V1 from Mode , Interface under FREERTOS Mode and Configuration on the right.
This is the version of the OS.
Either one is fine, but we’ll use the stable V1 one.

Modify the SysTick

Next, go to Pinout & Configuration – Categories, System Core, SYS – Timebase Source and select TIM5.

This is because FreeRTOS recommends that you use a Timebase Source other than SysTick.

Add a task

Under Mode, in Configuration, select Tasks & Queues.

Click on the Add button above the one for Tasks.

Change only the following three items in the list as shown below, and press OK.

Task Name : Receiver
Priority : osPriorityNormal
Entry Function : ReceiveFunc

Next, double-click on defaultTask, which has been in the list since the beginning, and edit the two items as follows

Task Name : Sender
Entry Function : SendFunc

It looks like this.

You can think of task here as meaning the same as thread.
The term “thread” will be used consistently from here on.

Implementation of the mail queue

For more information about the mail queue, please refer to here.

The green thread on the left allocates a memory area and sends (puts), while the light blue thread on the right releases the memory area after receiving.
This is the flow.

The green thread on the right allocates memory and sends (put), and the light blue thread on the right receives and releases the memory area. Unlike the message queue, memory needs to be allocated and released for handling large data.

Make sure you don’t forget to release the memory.

Coding it

The mail queue code is not generated by the configurator, so you need to prepare it by yourself.

The code is not very meaningful, but let’s check that it can send and receive data exceeding 32 bits, such as structures.

In the vicinity of main(), code as follows

/* USER CODE BEGIN 0 */

#define COUNTER_MAX 256
typedef struct
{
	uint8_t buffer[COUNTER_MAX];
	uint32_t counter;
} strData_t;

osMailQId strMailHandle;

/* USER CODE END 0 */ /* USER CODE END 0

/**
  * @brief The application entry point.
  * @retval int
  */ int main(void)
int main(void)
{
  /* USER CODE BEGIN 1 */ strMail(strMail, 8, strData_t)

	osMailQDef(strMail, 8, strData_t);
	strMailHandle = osMailCreate(osMailQ(strMail), NULL);

  /* USER CODE END 1 */

Code the two threads at the bottom as follows.

void SendFunc(void const * argument)
{
  /* USER CODE BEGIN 5 */

  strData_t data;
  for (int i = 0; i < COUNTER_MAX; i++)
  {
	  if (i & 1)
	  {
		  data.buffer[i] = 1;
	  }
  }
  /* Infinite loop */
  for(;;)
  {
	  strData_t *p = osMailAlloc(strMailHandle, osWaitForever);
	  p = &data;
	  osMailPut(strMailHandle, p);
	  osDelay(100);
  }
  /* USER CODE END 5 */ } 
}

void ReceiveFunc(void const * argument)
{
  /* USER CODE BEGIN ReceiveFunc */
  static int i = 0;
  /* Infinite loop */ for(;;)
  for(;;)
  {
    osEvent event = osMailGet(strMailHandle, osWaitForever);
    if (event.status == osEventMail)
    {
      strData_t *p = (strData_t*)event.value.p;
      if ((p->buffer[i] & 0x01) == 0)
      {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
      }
      } else
      {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
      }
	  osMailFree(strMailHandle, p);
	  if (++i >= COUNTER_MAX)
	  {
		  i = 0;
	  }
    }
  }
  /* USER CODE END ReceiveFunc */
}

Build it

Build it and make sure there are no errors.
If a query window comes up, press Yes.

Now let’s Run – Resume to run the program.

Outline of the code

You have to generate the mail queue by yourself.

Definition and mail queue generation

typedef struct
{
	uint8_t buffer[COUNTER_MAX];
	uint32_t counter;
} strData_t;
osMailQId strMailHandle;

There is a counter as a member of the structure, but we are not using it.
(I made it a member to make sure it can send data from the structure.
Then we define a variable of type osMailQId.

osMailQDef(strMail, 8, strData_t);
strMailHandle = osMailCreate(osMailQ(strMail), NULL);

In osMailDef() we associate the type to be used for the mail queue with its size and name.
First argument: specifies the name.
Second argument: specifies the size of the queue.
3rd argument: specifies the type to be used in the queue.

Create a mail queue with osMailCreate().
1st argument: Specify the name you just associated.
Second argument: Specify NULL.

Sending side

strData_t *p = osMailAlloc(strMailHandle, osWaitForever);
p = &data;
osMailPut(strMailHandle, p);
osDelay(100);

Get the memory area with osMailAlloc().
First argument: Specify the value of type osMailQid obtained by osMailCreate().
2nd argument: Specify the waiting time. The unit is msec. If osWaitForever is specified, it waits indefinitely.

Assign a variable of the structure to the memory area obtained by p = &data;.

Send with osMailPut().
First argument: Specify the value of the osMailQid type obtained by osMailCreate().
Second argument: Specify the pointer to the data type returned by osMailAlloc().

Receiving side

osEvent event = osMailGet(strMailHandle, osWaitForever);
if (event.status == osEventMail)
{
  strData_t *p = (strData_t*)event.value.p;
  (Abbreviated)
}

osEvent event = osMessageGet(myQueue01Handle, 0);

Return value: osEvent type
When the status member of this variable event is osEventMail, it means that a message has been received.
The event.value.p at that time is a pointer to the data type that was sent.

First argument: Specify the value of the osMailQid type obtained by osMailCreate().
Second argument: Specify 0.

(abbreviated), but in the subsequent if() statement, if the value is an even number
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
If the value is odd, the LED is turned off.

The processing contents are the same as the message queue, but we have confirmed that we can send and receive data from the structure.

How was it, everyone? Did you get the LED-blinky right?

Leave a Reply