Message queues provide buffers between tasks and interrupt service routines.
A message queue is a structure that enables tasks to post and receive messages. A message is a generic (void) pointer and, as such, can be used to send data that will fit into a pointer type (two or four bytes, depending upon the processor's word size) or to pass a pointer to a block of memory. The message queue uses a buffer to enable a number of posts to be completed without receives occurring. The buffer keeps the posted messages in FIFO order, so the oldest message is received first. When the buffer isn't full, a post will put the message at the back of the queue and the calling task continues execution. When the buffer is full, a post will block the calling task until there is room for the message. When the buffer isn't empty, a receive will return the message from the front of the queue and continue execution of the calling task. When the buffer is empty, a receive will block the calling task until a message is posted.
You allocate a message queue by declaring it as a C variable:
CTL_MESSAGE_QUEUE_t m1;
A message queue is initialized using ctl_message_queue_init:
void *queue[20]; ⁞ ctl_message_queue_init(&m1, queue, 20);
This example uses a 20-element array for the message queue. The array is a void * so pointers to memory or (cast) integers can be communicated via a message queue.
You can post a message to a message queue with an optional timeout by using the ctl_message_queue_post function.
ctl_message_queue_post(&m1, (void *)45, CTL_TIMEOUT_NONE, 0);
This example posts the integer 45 to the message queue.
You can post multiple messages to a message queue with an optional timeout using ctl_message_queue_post_multi:
if (ctl_message_queue_post_multi(&m1, 4, messages, CTL_TIMEOUT_ABSOLUTE, ctl_get_current_time()+1000) != 4) { // timeout occurred }
This example tests the return result to see if the timeout occurred.
If you want to post a message and you cannot afford to block (e.g. inside an interrupt service routine), you can use ctl_message_queue_post_nb (or ctl_message_queue_post_multi_nb if you want to post multiple messages):
if (ctl_message_queue_post_nb(&m1, (void *)45) == 0) { // queue is full }
This example tests the return result to see if the post failed.
You can use ctl_message_queue_receive to receive a message with an optional timeout:
void *msg; ctl_message_queue_receive(&m1, &msg, CTL_TIMEOUT_NONE, 0);
This example receives the oldest message in the message queue.
Use ctl_message_queue_receive_multi to receive multiple messages from a message queue with an optional timeout:
if (ctl_message_queue_multi_receive(&m1, 4, msgs, CTL_TIMEOUT_DELAY, 1000) != 4) { // timeout occurred }
This example tests the return result to see if the timeout occurred.
If you want to receive a message and you don't want to block (e.g., when executing interrupt service routine), you can use ctl_message_queue_receive_nb (or ctl_message_queue_receive_multi_nb to receive multiple messages).
if (ctl_message_queue_receive_nb(&m1, &msg) == 0) { // queue is empty }
The following example uses a message queue to implement the producer-consumer problem.
CTL_MESSAGE_QUEUE_t m1; void *queue[20]; void task1(void) { ⁞ ctl_message_queue_post(&m1, (void *)i, CTL_TIMEOUT_NONE, 0); ⁞ } void task2(void) { void *msg; ⁞ ctl_message_queue_receive(&m1, &msg, CTL_TIMEOUT_NONE, 0); ⁞ } int main(void) { ⁞ ctl_message_queue_init(&m1, queue, 20); ⁞ }
You can associate event flags with a message queue that are set (and similarly cleared) when the message queue is not full and not empty using the function ctl_message_queue_setup_events.
For example, you can use this to wait for messages to arrive from multiple message (or byte) queues:
CTL_MESSAGE_QUEUE_t m1, m2; CTL_EVENT_SET_t e; ctl_message_queue_setup_events(&m1, &e, 1<<0, 1<<1)); ctl_message_queue_setup_events(&m2, &e, 1<<2, 1<<3)); ⁞ switch (ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS, &e, (1<<0) | (1<<2), CTL_TIMEOUT_NONE, 0)) { case 1<<0: ctl_message_queue_receive(&m1, … break; case 1<<2: ctl_message_queue_receive(&m2, … break; }
This example sets up and waits for the not-empty event of message queue m1 and the not-empty event of message queue m2. When the wait completes, it reads from the appropriate message queue. Note that you should not use a ‘with auto clear’ event wait type when waiting for events associated with a message queue.
You can use ctl_message_queue_num_used to test how many messages are in a message queue and ctl_message_queue_num_free to learn how many free messages are in a message queue. With these functions you can poll the message queue:
while (ctl_message_queue_num_free(&m1) < 10) ctl_task_timeout_wait(ctl_get_current_time() + 1000); ctl_message_queue_post_multi(&m1, 10, …
This example waits for 10 elements to be free before it posts 10 elements.