How do ThreadX Message Queues work on STM32?
Message queues are the primary means of inter-thread communication in ThreadX. One or more messages can reside in a message queue. A message queue that holds a single message is commonly called a mailbox.
Messages are copied to a queue by tx_queue_send and are copied from a queue by tx_queue_receive. The only exception to this is when a thread is suspended while waiting for a message on an empty queue. In this case, the next message sent to the queue is placed directly into the thread's destination area.Each message queue supports a number of fixed-sized messages. The available message sizes are 1 through 16 (32-bit words inclusive). The message size is specified when the queue is created. Application messages greater than 16 words must be passed by pointer. This is accomplished by creating a queue with a message size of 1 word (enough to hold a pointer) and then sending and receiving message pointers instead of the entire message.The total message capacity of the queue is calculated by dividing the number of bytes in each message into the total number of bytes in the supplied memory area. For example, if a message queue that supports a message size of one 32-bit word (4 bytes) is created with a 100-byte memory area, its capacity is 25 messages.
1. Goal:
The purpose of this article is to provide a brief explanation with a working example on how the message queue worksAlthough the example is using the NUCLEO-H723ZG, you can use the same steps for other STM32H7 based boards. The main differences are usually pinout and clock configuration. This article will start a project from scratch, add the AzureRTOS Middleware using the Software Packs available in the STM32CubeIDE and add a few lines of code, with the goal of getting 2 Threads with the same priorities, that will be named as Sender1 and Receiver. These will be used to showcase the usage of the message queue services. After that, we’ll add a second sender, that will be named Sender2, raise the priority of the Receiver thread and have 2 messages sent using the same queue. This is the final demo representation:
2. Getting to know the functions:
UINT tx_queue_create(
TX_QUEUE *queue_ptr,
CHAR *name_ptr,
UINT message_size,
VOID *queue_start,
ULONG queue_size);
This service creates a message queue that is typically used for interthread communication. The total number of messages is calculated from the specified message size and the total number of bytes in the queue Parameters:
- queue_ptr: Pointer to a message queue control block.
- name_ptr: Pointer to the name of the message queue.
- message_size: Specifies the size of each message in the queue. Message sizes range from one 32-bit word to sixteen 32-bit words. Valid message size options are numerical values from 1 through 16, inclusive.
- queue_start: Starting address of the message queue. The starting address must be aligned to the size of the ULONG data type.
- queue_size: Total number of bytes available for the message queue.
UINT tx_queue_receive(
TX_QUEUE *queue_ptr,
VOID *destination_ptr,
ULONG wait_option);
This service retrieves a message from the specified message queue. The retrieved message is copied from the queue into the memory area specified by the destination pointer. That message is then removed from the queue. Parameters:
- queue_ptr:
Pointer to a previously created message queue. - destination_ptr:
Location of where to copy the message. - wait_option:
Defines how the service behaves if the message queue is empty. The wait options are defined as follows:
- *TX_NO_WAIT: (0x00000000) - Selecting TX_NO_WAIT results in an immediate return from this service regardless of whether or not it was successful. This is the only valid option if the service is called from a non-thread; e.g., Initialization, timer, or ISR.
- TX_WAIT_FOREVER (0xFFFFFFFF) - Selecting TX_WAIT_FOREVER causes the calling thread to suspend indefinitely until a message is available.
- timeout value (0x00000001 through 0xFFFFFFFE) - Selecting a numeric value (1-0xFFFFFFFE) specifies the maximum number of timer-ticks to stay suspended while waiting for a message.
UINT tx_queue_send(
TX_QUEUE *queue_ptr,
VOID *source_ptr,
ULONG wait_option);
This service sends a message to the specified message queue. The sent message is copied to the queue from the memory area specified by the source pointer.Parameters:
- queue_ptr:
Pointer to a previously created message queue. - source_ptr:
Pointer to the message. - wait_option:
Defines how the service behaves if the message queue is full. The wait options are defined as follows:
- *TX_NO_WAIT: (0x00000000) - Selecting TX_NO_WAIT results in an immediate return from this service regardless of whether or not it was successful. This is the only valid option if the service is called from a non-thread; e.g., Initialization, timer, or ISR.
- TX_WAIT_FOREVER (0xFFFFFFFF) - Selecting TX_WAIT_FOREVER causes the calling thread to suspend indefinitely until there is room in the queue.
- timeout value (0x00000001 through 0xFFFFFFFE) - Selecting a numeric value (1-0xFFFFFFFE) specifies the maximum number of timer-ticks to stay suspended while waiting for room in the queue.
It is worth mentioning that the ThreadX also includes a service called tx_queue_send_notify. This service registers a notification callback function that is called whenever a message is sent to the specified queue. The processing of the notification callback is defined by the application.
In Part 2 we'll cover the step by step of creating the project from scratch and doing a simple queue transfer, using 2 threads.
In Part 3 we'll explain how to add our Sender 2 thread and evaluate the behavior.