RT-Thread mailbox (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 Mailboxes

The mailbox is a common IPC communication method in the operating system, and the mailbox can be between threads. Messages are transmitted between interrupts and threads. In addition, mailboxes have lower overhead and higher efficiency than semaphores and message queues, so they are often used for thread-to-thread, interrupt-to-thread communication. Each mail in the mailbox can only hold a fixed 4-byte content (STM32 is a 32-bit processing system, the size of a pointer is 4 bytes, so a mail can hold exactly one pointer), when the thread needs to When transferring larger messages between each other, a pointer to a buffer can be sent to the mailbox as a message.

--original

Mailboxes are similar to message queues, except that the size of mailboxes can only be a fixed 4 bytes, but message queues can be of any length.

How mailboxes work

The operating principle of the mailbox is shown in the figure below. In the figure below, the thread or interrupt service function sends a 4-byte email to the mailbox. There are several threads in the thread waiting queue waiting to receive the mailbox.

Image source: RT-Thread official Chinese manual

insert image description here

The thread can read mail messages from the mailbox. When the mail in the mailbox is empty, it decides whether to suspend the reading thread according to the user-defined blocking time; when there are new mails in the mailbox, the suspended reading thread is awakened , the mailbox is also an asynchronous communication method.

One or more messages can be put into a mailbox by a mailbox, a thread, or an interrupt service function. Likewise, one or more threads can get mail messages from mailboxes. When multiple emails are sent to the mailbox, usually the email that enters the mailbox first should be passed to the thread first, that is, the thread gets the message that enters the mailbox first, that is, the first-in-first-out principle (FIFO), while RT - Mailboxes in Thread support priority, which means that among all threads waiting for mail, the one with the highest priority will get the mail first.

--original

Application scenarios of mailboxes

The mailbox of RT-Thread can store a fixed number of mails, which is determined when the mailbox is initialized or created. Although a message can only be 4 bytes in size, it is possible to use a pointer to a buffer (array, 4 bytes per element) as a message, so that larger content can be sent.

Compared with other communication methods, mailboxes are characterized by low overhead and high efficiency. Both sending and receiving mailboxes support blocking waiting, which is very suitable for inter-thread, interrupt and inter-thread communication, but be careful not to use blocking to send emails in interrupts.

Since the mailbox message size can only be 4 bytes, and the pointer of the 32-bit system is exactly 4 bytes, the mailbox is very suitable for passing pointer data.

How to use email

In actual project development, a lot of information is a structure variable, can it be delivered by email? The answer is no problem, just use a struct pointer.

//定义一个结构体指针(结构体成员自定义)
struct temp *pTemp;
//申请动态堆空间
pTemp = (struct temp*)rt_malloc(sizeof(struct temp));
//发送方在数据处理完成后,将这个消息指针发送给一个邮箱,邮箱名为mb
rt_mb_send(mb, (rt_uint32_t)pTemp);

Since the memory space of the above message pointer is manually applied for, after the thread receives the mailbox information and the processing is completed, it is also necessary to manually release the space of the message pointer

struct temp *pTemp;
if(rt_mb_recv(mb, (rt_uint32_t *)&pTemp) == RT_EOK)
{
    
    
	//数据处理完成后,释放消息指针内存空间
	rt_free(pTemp);
}

mailbox control block

struct rt_mailbox
{
    
    
    struct rt_ipc_object parent;                        /**< 继承自ipc_object类 */

    rt_ubase_t          *msg_pool;                      /**< 邮箱缓冲区的开始地址 */

    rt_uint16_t          size;                          /**< 邮箱缓冲区的大小 */

    rt_uint16_t          entry;                         /**< 邮箱中邮件的数目 */
    rt_uint16_t          in_offset;                     /**< 邮箱缓冲的入口指针 */
    rt_uint16_t          out_offset;                    /**< 邮箱缓冲的出口指针 */

    rt_list_t            suspend_sender_thread;         /**< 发送线程的挂起等待队列 */
};

Mailbox function interface

Create mailbox rt_mb_create()

rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);

When creating a mailbox object, a mailbox object control block is first created, and then a memory space is allocated to the mailbox to store the mail. The size of this memory is equal to the product of the mail size (4 bytes) and the mailbox capacity, and then initialized to receive mail and send The offset of the message in the mailbox. For IPC objects created with the RT_IPC_FLAG_PRIO priority flag, when multiple threads are waiting for resources, the thread with higher priority will get the resources first. The IPC object created by using the RT_IPC_FLAG_FIFO FIFO flag will obtain resources in a first-come, first-served order when multiple threads are waiting for resources.

parameter describe
name the name of the mailbox
size Mailbox Capacity
flag mailbox sign

Delete mailbox rt_mb_delete()

rt_err_t rt_mb_delete (rt_mailbox_t mb);

When deleting a mailbox, if a thread is suspended on the mailbox object, the kernel first wakes up all threads suspended on the mailbox (the thread gets the return value of -RT_ERROR), then releases the memory used by the mailbox, and finally deletes the mailbox object .

parameter describe
mb handle to the mailbox object

Initialize mailbox rt_mb_init()

rt_err_t rt_mb_init(rt_mailbox_t mb, const char* name, void* msgpool,
rt_size_t size, rt_uint8_t flag)

When initializing the mailbox, this function interface needs to obtain the mailbox object control block, the pointer of the buffer, and the mailbox name and mailbox capacity that the user has applied for. For IPC objects created with the RT_IPC_FLAG_PRIO priority flag, when multiple threads are waiting for resources, the thread with higher priority will get the resources first. The IPC object created by using the RT_IPC_FLAG_FIFO FIFO flag will obtain resources in a first-come, first-served order when multiple threads are waiting for resources.

parameter describe
mb handle to the mailbox object
name the name of the mailbox
msgpool buffer pointer
size Mailbox Capacity
flag mailbox sign

Mailbox receive rt_mb_recv()

rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);

When receiving emails, the recipient needs to specify the mailbox handle to receive the emails, and specify the storage location of the received emails and the maximum timeout period that can be waited. If a timeout is set when receiving, when the email is still not received within the specified time, -RT_ETIMEOUT will be returned.

parameter describe
mb handle to the mailbox object
value content of email
timeout Specify timeout

Mailbox send rt_mb_send() (non-blocking)

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);

The sent mail can be 32-bit data in any format, an integer value or a pointer to a buffer. When the mail in the mailbox is full, the thread sending the mail or the interrupt program will receive the return value of -RT_EFULL.

parameter describe
mb handle to the mailbox object
value content of email

Mailbox send rt_mb_send_wait() (blocking)

rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout);

The difference between rt_mb_send_wait and rt_mb_send is that if the mailbox is full, the sending thread will wait for the space in the mailbox to be vacated for receiving mail according to the set timeout parameter. If there is still no free space after the set timeout time, the sending thread will be woken up and return an error code.

parameter describe
mb handle to the mailbox object
value content of email
timeout Specify timeout

mailbox experiment

To use mailbox communication in RT-Thread, you need to modify the rtconfighconfiguration file first. You can use the following two methods:

  1. Uncomment, open macro definition,

insert image description here

  1. Or use the Configuration Wizardwizard for graphical configuration,

insert image description here

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

This experiment needs to create two threads, one is the receiving thread and the other is the sending thread. The receiving thread is responsible for receiving mailbox information and printing it to the serial port. The sending thread is responsible for scanning the buttons. When the button is detected, it sends the corresponding mailbox information to the mailbox.

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

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

// 定义邮箱控制块
static rt_mailbox_t test_mb = RT_NULL;


// 实验需要用到的全局变量
char test_str1[] = "this is a mail test 1";
char test_str2[] = "this is a mail test 2";



/******************************************************************************
* @ 函数名  : recv_thread_entry
* @ 功  能  : 接收线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void recv_thread_entry(void *parameter)
{
    
    
	rt_err_t uwRet = RT_EOK;
	char *r_str;
	while(1)
	{
    
    
		// 等待接收邮箱信息
		uwRet = rt_mb_recv(test_mb,                          // 邮箱对象句柄
					(rt_uint32_t*)&r_str,                    // 接收邮箱信息
					RT_WAITING_FOREVER);                     // 超时一直等
		
		if(uwRet == RT_EOK)
		{
    
    
			rt_kprintf("邮箱接收到的内容:%s\n\n", r_str);
			LED0_TOGGLE;    // LED0 反转
		}
		else
		{
    
    
			rt_kprintf("邮箱接收错误!\n");
		}
	}	
}

/******************************************************************************
* @ 函数名  : send_thread_entry
* @ 功  能  : 发送线程入口函数
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void send_thread_entry(void *parameter)
{
    
    
	rt_err_t uwRet = RT_EOK;
	while(1)
	{
    
    
		
		// KEY0 被按下
		if(Key_Scan(KEY0_GPIO_PORT, KEY0_GPIO_PIN) == KEY_ON)
		{
    
    
			rt_kprintf("send:KEY0被单击\n");
			// 发送邮箱信息1
			uwRet = rt_mb_send(test_mb, (rt_uint32_t)&test_str1);
			if(uwRet == RT_EOK)
				rt_kprintf("邮箱信息发送成功!\n\n");
			else
				rt_kprintf("邮箱信息发送失败!\n\n");
		}
		
		// WK_UP 被按下
		if(Key_Scan(WK_UP_GPIO_PORT, WK_UP_GPIO_PIN) == KEY_ON)
		{
    
    
			rt_kprintf("send:WK_UP被单击\n");
			// 发送邮箱2
			uwRet = rt_mb_send(test_mb, (rt_uint32_t)&test_str2);
			if(uwRet == RT_EOK)
				rt_kprintf("邮箱信息发送成功!\n\n");
			else
				rt_kprintf("邮箱信息发送失败!\n\n");
		}
		rt_thread_delay(20);     //每20ms扫描一次
	}
}

int main(void)
{
    
    
	// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
	
	// 创建一个邮箱
	test_mb =                                     // 邮箱控制块指针
	rt_mb_create("test_mb",                       // 邮箱初始值
				    10,                           // 邮箱大小
	                RT_IPC_FLAG_FIFO);            // FIFO队列模式(先进先出)
	
	if(test_mb != 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 KEY0 is pressed, the send thread sends mailbox information 1 to the recv thread. When WK_UP is pressed, the send thread sends mailbox information 2 to the recv thread; when the recv thread receives the mailbox information, it prints the received content:

insert image description here

Guess you like

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