RT-Thread message queue (study notes)

This article refers to [Wildfire EmbedFire] "RT-Thread Kernel Implementation and Application Development - Based on STM32", which is only used as a personal study note. For more detailed content and steps, please check the original text (you can download it from the Wildfire Data Download Center)

Basic Concepts of Message Queuing

A message queue is a commonly used communication method between threads. It can receive messages of variable length from threads or interrupt service routines and cache the messages in its own memory space. Other threads can also read the corresponding messages from the message queue, and when the message queue is empty, the reading thread can be suspended. When a new message arrives, the suspended thread will be woken up to receive and process the message. A message queue is an asynchronous communication method.

——RT-Thread Official Chinese Manual

With the message queue service, a thread or interrupt service routine can put one or more messages into a message queue. Likewise, one or more threads can get messages from the message queue. When multiple messages are sent to the message queue, the message that enters the message queue first is usually passed to the thread first, that is, the thread gets the message that enters the message queue first, that is, the first-in-first-out principle (FIFO). . At the same time, the message queue in RT-Thread supports priority, which means that among all threads waiting for messages, the one with the highest priority will get the message first.

In RT-Thread, the queue data structure is used to realize the asynchronous communication of threads, which has the following characteristics:

  • Messages support first-in, first-out queuing and priority queuing, and support asynchronous read and write work.
  • The read queue supports a timeout mechanism.
  • Supports sending urgent messages, where urgent messages are sent to the head of the queue.
  • Any type of message of different lengths (up to the maximum queue node value) can be allowed.
  • A thread can receive and send messages from any message queue.
  • Multiple threads can receive and send messages from the same message queue.
  • When the queue is used up, it needs to be reclaimed by releasing the memory function by deleting the queue operation.

--original

How message queues work

As shown in the working diagram of the message queue, through the message queue service, a thread or interrupt service routine can put one or more messages into the message queue. Likewise, one or more threads can get messages from the message queue. When multiple messages are sent to the message queue, usually the message that enters the message queue first should be passed to the thread first, that is, the thread gets the message that enters the message queue first, that is, the first-in-first-out principle (FIFO). .

——RT-Thread Official Chinese Manual

Image source: RT-Thread official Chinese manual

insert image description here

The blocking mechanism of the message queue

The message queue does not belong to a thread, so when multiple threads operate on the message queue, it is necessary to protect each thread from reading and writing to the queue, but the operating system already has this protection operation, which is called the blocking mechanism of the message queue.

Each function that reads and writes to the message queue has its own blocking mechanism. When a thread reads an empty queue, it has 3 options:

  1. Do not wait for the message, skip reading directly;
  2. Wait for a certain time, then enter the ready state, and enter the blocking state after timeout;
  3. Keep waiting and enter the blocking state directly.

When there is no available message block on the idle message list, the queue is full. At this time, the sender (thread or interrupt) will receive an error code, and then continue to execute. The message is sent without blocking characteristics.

Application Scenarios of Message Queuing

Message queues can be used in situations where messages of variable length are sent, including message exchange between threads, and sending messages to threads in interrupt service functions (interrupt service routines cannot receive messages).

message queue control block

The message queue control block contains the detailed information of the message queue, including queue pool size, queue message size, linked list pointer, etc.

struct rt_messagequeue
{
    
    
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    void                *msg_pool;                      /**< 队列开始地址 */

    rt_uint16_t          msg_size;                      /**< 每条消息大小 */
    rt_uint16_t          max_msgs;                      /**< 最大消息数量 */

    rt_uint16_t          entry;                         /**< 消息索引,记录消息个数 */

    void                *msg_queue_head;                /**< 链表指头针 */
    void                *msg_queue_tail;                /**< 链表尾指针 */
    void                *msg_queue_free;                /**< 空闲消息指针 */

    rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this message queue */
};

Message queue related interface

Only a few commonly used interfaces are introduced

Create message queue rt_mq_create()

rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag);

When creating a message queue, first create a message queue control block, then allocate a memory space to the message queue, organize it into a linked list of free messages, and then initialize the message queue.

parameter describe
name the name of the message queue
msg_size The maximum length of a message in the message queue
max_msgs The maximum capacity of the message queue (number of messages)
flag The waiting method used by the message queue

Initialize the message queue rt_mq_init()

rt_err_t rt_mq_init(rt_mq_t mq, const char* name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size,
rt_uint8_t flag);

Similar to creating a message queue, but the memory for initializing the message queue function is not dynamically allocated by the system, but statically defined.

parameter describe
mq a handle to a static message queue object
name the name of the message queue
msgpool buffer for storing messages
msg_size The maximum length of a message in the message queue
pool_size The size of the buffer to store the message
flag The waiting method used by the message queue

Send message function rt_mq_send()

rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);

When sending a message, the message queue object first takes an idle message block from the idle message linked list, copies the message content sent by the thread or the interrupt service program to the idle message block, and then hangs the message block at the end of the message queue. (After sending a normal message, the message at the head of the queue on the idle message list is transferred to the tail of the message queue)

parameter describe
mq A handle to the message queue object
buffer Message content
size message size

Receive message function rt_mq_recv()

rt_err_t ·rt_mq_recv (rt_mq_t mq, void* buffer, rt_size_t size, rt_int32_t timeout);

Messages can be received only when the message queue is not empty, otherwise the thread will be suspended when receiving messages, and the waiting time is timeoutdetermined by the parameters.

parameter describe
mq A handle to the message queue object
buffer Message content
size message size
timeout specified timeout

delete queue rt_mq_delete()

rt_err_t rt_mq_delete(rt_mq_t mq)

When deleting a message queue, if a thread is suspended on the message queue waiting queue, the kernel first wakes up all threads hanging on the message waiting queue, then releases the memory used by the message queue, and finally deletes the message queue object.

parameter describe
mq A handle to the message queue object

message queue experiment

To use message queues, you need to rtconfig.henable this feature in .

insert image description here

This experiment refers to the original text corresponding to the experimental code, which only contains man()functions, and the peripheral initialization related code is not given below.

Different from the experiment in the previous section, the priority of the button thread in the previous experiment must be higher than that of the LED thread (otherwise the response speed of the button will be affected), but the receiving thread recv_thread of this experiment is basically in a suspended state, so the send_thread (press button) ) thread can have the same priority as rend_thread (or even lower priority).

#include "board.h"
#include "rtthread.h"

// 定义线程控制块指针
static rt_thread_t recv_thread = RT_NULL;
static rt_thread_t send_thread = RT_NULL;

// 定义消息队列控制块
static rt_mq_t test_mq = RT_NULL;


/******************************************************************************
* @ 函数名  : recv_thread_entry
* @ 功  能  : 消息接收线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void recv_thread_entry(void *parameter)
{
    
    
	rt_err_t uwRet = RT_EOK;
	uint32_t recv_queue; // 接收数据保存的位置
	while(1)
	{
    
    
		// 队列接收,等待方式为一直阻塞等待
		uwRet = rt_mq_recv(test_mq, &recv_queue, sizeof(recv_queue), 
				RT_WAITING_FOREVER); 
		if(RT_EOK == uwRet)
		{
    
    
			rt_kprintf("recv_thread 接收到的数据为%d.\n", recv_queue);
		}
		else
		{
    
    
			rt_kprintf("recv_thread 接收数据出错!\n");
		}
		rt_thread_delay(200);
	}
}

/******************************************************************************
* @ 函数名  : send_thread_entry
* @ 功  能  : 消息发送线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void send_thread_entry(void *parameter)
{
    
    
	rt_err_t uwRet = RT_EOK;
	uint32_t send_data1 = 1;
	uint32_t send_data2 = 2;
	
	while(1)
	{
    
    
		// KEY0 被按下
		if(Key_Scan(KEY0_GPIO_PORT, KEY0_GPIO_PIN) == KEY_ON)
		{
    
    
			// 将data1发送到消息队列
			uwRet = rt_mq_send(test_mq, &send_data1, sizeof(send_data1)); 
			
			if(uwRet != RT_EOK)
			{
    
    
				rt_kprintf("send_thread 发送数据失败--data1.\n");
			}
		}
		
		// WK_UP 被按下
		if(Key_Scan(WK_UP_GPIO_PORT, WK_UP_GPIO_PIN) == KEY_ON)
		{
    
    
			{
    
    
			// 将data2发送到消息队列
			uwRet = rt_mq_send(test_mq, &send_data2, sizeof(send_data2)); 
				
			if(uwRet != RT_EOK)
			{
    
    
				rt_kprintf("send_thread 发送数据失败--data2.\n");
			}
		}
		
		}
		rt_thread_delay(20);
	}
}

int main(void)
{
    
    
	// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
	
	// 创建一个队列
	test_mq =                                     // 消息队列控制块指针
	rt_mq_create("test_mq",                       // 消息队列名字
	                50,                           // 消息队列最大长度(单条消息)
	                20,                           // 消息队列最大容量(消息数量)
	                RT_IPC_FLAG_FIFO);            // FIFO队列模式(先进先出)
	
	if(test_mq != RT_NULL)
		rt_kprintf("消息队列创建成功!\n");

	// 创建一个动态线程
	recv_thread =                                 // 线程控制块指针
	rt_thread_create("recv",                      // 线程名字
	                recv_thread_entry,            // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    5,                            // 线程优先级
					10);                          // 线程时间片
	
	
	// 开启线程调度
	if(recv_thread != RT_NULL)
		rt_thread_startup(recv_thread);
	else
		return -1;
							
	// 创建一个动态线程
	send_thread =                                 // 线程控制块指针
	rt_thread_create("send",                      // 线程名字
	                send_thread_entry,            // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    5,                            // 线程优先级
					10);                          // 线程时间片
	// 开启线程调度
	if(send_thread != RT_NULL)
		rt_thread_startup(send_thread);
	else
		return -1;
}

Experimental phenomena

When the button KEY0is pressed, the send_thread thread sends data1, and then the recv_thread thread prints the received data; when the button WK_UPis pressed, the send_thread thread sends data2, and then the recv_thread thread prints the received data.

insert image description here

Guess you like

Origin blog.csdn.net/weixin_43772810/article/details/123689088