rt-thread邮箱使用教程
好的,这是一份 RT-Thread 邮箱(Mailbox)的使用中文教程。邮箱是 RT-Thread 中一种基本的进程间通信(IPC)机制,主要用于线程间传递短消息(一个 4 字节的整数值 或 一个指针)。
核心特点:
- 容量固定: 邮箱在创建时就确定了其能容纳的消息数量(深度)。
- 异步发送 & 可选阻塞接收: 发送通常是异步的(立即返回),接收可以是阻塞或非阻塞的。
- 存储指针或整数值: 一条消息要么是一个 32 位(4 字节)的整数,要么是一个 指针,可以用来指向实际的数据结构。
- 轻量高效: 相比消息队列,邮箱更适合传递简单的控制信息或小数据块的指针。
?️ 使用步骤
-
包含头文件:
#include <rtthread.h> #include <rtdevice.h> // 如果使用静态邮箱初始化宏,则需要包含 -
定义邮箱控制块 & 消息容器:
- 动态创建:
/* 定义一个邮箱控制块的指针 */ static rt_mailbox_t my_mailbox;动态创建时,RT-Thread 内核会自动分配邮箱结构体和消息容器所需的内存。
- 静态初始化:
/* 定义邮箱控制块 */ static struct rt_mailbox my_static_mbox; /* 定义存储消息的容器(缓冲区) */ static rt_ubase_t mail_pool[16]; // 数组类型为 rt_ubase_t (通常是 32 位整数),大小决定了邮箱深度(这里是16条消息)静态初始化需要手动定义邮箱控制块和一个
rt_ubase_t类型的数组作为消息池。
- 动态创建:
-
初始化/创建邮箱:
- 动态创建(推荐方式):
#define MAILBOX_SIZE 10 // 你想创建的邮箱能容纳的消息条数 my_mailbox = rt_mb_create("my_mail", // 邮箱名字,方便调试 (可选) MAILBOX_SIZE, // 邮箱容量(深度) RT_IPC_FLAG_PRIO); // 等待队列中线程的排队方式: 优先级(RT_IPC_FLAG_PRIO)或先进先出(RT_IPC_FLAG_FIFO) if (my_mailbox == RT_NULL) { rt_kprintf("Failed to create mailbox!\n"); // 错误处理 } - 静态初始化:
rt_err_t result; result = rt_mb_init(&my_static_mbox, // 指向静态邮箱控制块 "my_static_mail", // 邮箱名字 (可选) &mail_pool[0], // 指向消息池数组的起始地址 sizeof(mail_pool) / sizeof(rt_ubase_t), // 计算邮箱容量 = 数组元素个数 RT_IPC_FLAG_PRIO); // 排队方式 if (result != RT_EOK) { rt_kprintf("Failed to init static mailbox! Code: %d\n", result); // 错误处理 }
- 动态创建(推荐方式):
-
发送消息: 使用
rt_mb_send()(阻塞模式)或rt_mb_send_wait()(带超时的阻塞)通常用于线程上下文。 对于 中断服务程序(ISR),请使用rt_mb_send_wait(),并设置超时时间为 0 (RT_WAITING_NO),因为中断中不能挂起线程。rt_err_t result; rt_uint32_t my_int_value = 0x12345678; // 或一个指针: void *my_ptr = &some_data; /* 在普通线程中发送(阻塞,直到邮箱有空间或超时) */ result = rt_mb_send_wait(my_mailbox, // 邮箱句柄 (rt_ubase_t)my_int_value, // 或 (rt_ubase_t)my_ptr, 强制转换为 4 字节类型 RT_WAITING_FOREVER); // 等待时间:永远等待直到有空位。也可以用毫秒数设置超时 (如:10 * RT_TICK_PER_SECOND / 1000) // 或者使用 rt_mb_send(),它等价于 rt_mb_send_wait(mb, value, RT_WAITING_NO) // 即立刻返回,如果邮箱满,则返回错误 /* 在中断服务程序(ISR)中发送(非阻塞)*/ void MyIrqHandler(void) { result = rt_mb_send_wait(my_mailbox, (rt_ubase_t)my_int_value, RT_WAITING_NO); if (result != RT_EOK) { // 邮箱满,发送失败,中断中需要快速处理,通常记录错误或丢弃 } }成功发送返回值
RT_EOK,失败返回值通常是-RT_EFULL(邮箱已满) 或-RT_ETIMEOUT(等待超时)。 -
接收消息: 通常在需要消息的线程上下文中接收。接收可以是阻塞的(等待)或非阻塞的。
rt_err_t result; rt_ubase_t received_value; // 用于存储接收到的整数值或指针 /* 阻塞接收(等待消息) */ result = rt_mb_recv(my_mailbox, // 邮箱句柄 &received_value, // 指向接收缓冲区的指针(rt_ubase_t 类型) RT_WAITING_FOREVER); // 等待时间:永远等待直到有消息。也可以设置超时时间 // 或者使用 rt_mb_recv(),等效于 rt_mb_recv(mb, value, RT_WAITING_NO) if (result == RT_EOK) { // 成功接收到消息 // 根据发送的内容,received_value 可能是一个整数 (rt_uint32_t) 或者一个指针 (void*) // 如果是指针,需要转回原类型: void *my_ptr = (void *)received_value; rt_kprintf("Received value: 0x%x\n", (rt_uint32_t)received_value); } else if (result == -RT_ETIMEOUT) { // 等待超时,邮箱在规定时间内无消息 } else { // 其他错误,如参数错误 (result == -RT_ERROR) }成功接收返回值
RT_EOK,失败返回值通常是-RT_ETIMEOUT(等待超时) 或-RT_ERROR(参数错误)。邮箱空返回-RT_ETIMEOUT(在非阻塞接收中立即返回为空)。 -
删除/脱离邮箱:
- 动态创建的邮箱: 使用完毕后必须删除以防止内存泄漏。
rt_err_t result; result = rt_mb_delete(my_mailbox); if (result == RT_EOK) { rt_kprintf("Mailbox deleted.\n"); } - 静态初始化的邮箱: 使用
rt_mb_detach()将其从内核对象管理器中脱离(控制块和消息池内存由用户管理)。rt_err_t result; result = rt_mb_detach(&my_static_mbox); if (result == RT_EOK) { rt_kprintf("Static mailbox detached.\n"); }
- 动态创建的邮箱: 使用完毕后必须删除以防止内存泄漏。
? 示例代码:线程1通过邮箱向线程2发送计数值
#include <rtthread.h>
#define MAILBOX_SIZE 5
static rt_mailbox_t count_mailbox;
static void thread1_entry(void *parameter) {
rt_uint32_t count = 0;
while (1) {
// 每 500ms 发送一次计数
rt_thread_mdelay(500);
if (rt_mb_send(count_mailbox, count) != RT_EOK) {
rt_kprintf("Thread1: Mailbox full! Count %d dropped.\n", count);
} else {
rt_kprintf("Thread1: Sent count: %d\n", count);
}
count++;
}
}
static void thread2_entry(void *parameter) {
rt_ubase_t recv_value;
while (1) {
// 阻塞等待计数邮箱的消息
if (rt_mb_recv(count_mailbox, &recv_value, RT_WAITING_FOREVER) == RT_EOK) {
rt_kprintf("Thread2: Received count: %d\n", (int)recv_value);
}
}
}
int mailbox_sample(void) {
// 动态创建邮箱
count_mailbox = rt_mb_create("cnt_mbox", MAILBOX_SIZE, RT_IPC_FLAG_FIFO);
if (count_mailbox == RT_NULL) {
rt_kprintf("Failed to create count mailbox!\n");
return -1;
}
// 创建发送线程
rt_thread_t tid1 = rt_thread_create("thread1",
thread1_entry,
RT_NULL,
512, // 栈大小
25, // 优先级 (数字小优先级高)
10); // 时间片
if (tid1 != RT_NULL) rt_thread_startup(tid1);
// 创建接收线程
rt_thread_t tid2 = rt_thread_create("thread2",
thread2_entry,
RT_NULL,
512,
26, // 优先级比 thread1 低
10);
if (tid2 != RT_NULL) rt_thread_startup(tid2);
return RT_EOK;
}
/* 可导出到 msh 命令 */
MSH_CMD_EXPORT(mailbox_sample, Run mailbox sample);
? 关键注意事项
- 容量与溢出: 务必为邮箱设置合适的容量。发送前检查返回值(特别是使用
rt_mb_send()),处理邮箱满的情况(如等待、记录错误或丢弃消息)。避免未处理的溢出。 - 指针传递:
- 接收线程在接收到指针后才有权限访问该指针指向的内存。
- 通常 发送线程负责分配内存(如使用
rt_malloc),接收线程负责释放内存(使用rt_free),避免内存泄漏或非法访问。 - 确保指针指向的内存在传递期间是有效的(不被释放或覆盖)。如果需要长期保存,应进行深拷贝。
- 等待与阻塞:
- 使用
RT_WAITING_FOREVER要小心,需确保逻辑上一定会发生对方线程的动作(发送/接收),否则可能导致线程永久挂起。 - 在关键任务线程或优先级高的线程上尽量使用带超时的接收/发送,避免因邮箱空/满而导致高优先级任务被无限期阻塞(低优先级任务占用了邮箱),防止优先级反转。
- 使用
- 中断发送: 务必在中断服务例程(ISR)中使用
rt_mb_send_wait()并设置超时时间为RT_WAITING_NO(0)。 - 任务同步: 邮箱不仅用于传递数据,也常用于传递信号或事件,0x00000001这样的数值即可代表“任务完成”这类事件。
- 选择正确机制: 如果你的消息长度经常超过 4 字节(指针大小不计算在内,指针本身才4字节),或者传输的数据量较大且频繁,应该考虑使用 消息队列(Message Queue)。
- 资源释放: 动态创建的邮箱
(rt_mb_create)务必在使用完毕后删除(rt_mb_delete)。静态初始化的邮箱(rt_mb_init)在不需要时脱离(rt_mb_detach)。
? 通过理解以上概念和步骤,你可以在 RT-Thread 应用程序中有效地使用邮箱来实现线程间简单而高效的通信。祝你开发顺利!?
RT-Thread学习笔记 RT-Thread的架构概述
RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread
2022-07-09 11:27:42
RT-Thread文档_RT-Thread 潘多拉 STM32L475 上手指南
RT-Thread文档_RT-Thread 潘多拉 STM32L475 上手指南
资料下载
佚名
2023-02-22 18:23:17
RT-Thread AI kit开源:轻松实现一键部署AI模型至 RT-Thread
RT-AK 是 RT-Thread 团队为 RT-Thread 实时操作系统所开发的 AI 套件,能够一键将 AI 模型部署到
资料下载
李伟
2022-01-25 18:18:17
RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍
RT-Thread全球技术大会:RT-Thread对POSIX的实现情况介绍 审核编辑:彭静
2022-05-27 16:52:41
RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制
RT-Thread全球技术大会:Kconfig在RT-Thread中的工作机制 审核编辑:彭静
2022-05-27 14:49:12
RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态
RT-Thread全球技术大会:RT-Thread Smart更好的兼容Linux生态 审核编辑:彭静
2022-05-27 14:31:32
RT-Thread全球技术大会:萤石研发团队使用RT-Thread的技术挑战
RT-Thread全球技术大会:研发团队使用RT-Thread的技术挑战 审核编辑:彭静
2022-05-27 11:36:17
换一换
- 如何分清usb-c和type-c的区别
- 中国芯片现状怎样?芯片发展分析
- vga接口接线图及vga接口定义
- 芯片的工作原理是什么?
- 华为harmonyos是什么意思,看懂鸿蒙OS系统!
- 什么是蓝牙?它的主要作用是什么?
- ssd是什么意思
- 汽车电子包含哪些领域?
- TWS蓝牙耳机是什么意思?你真的了解吗
- 什么是单片机?有什么用?
- 升压电路图汇总解析
- plc的工作原理是什么?
- 再次免费公开一肖一吗
- 充电桩一般是如何收费的?有哪些收费标准?
- ADC是什么?高精度ADC是什么意思?
- dtmb信号覆盖城市查询
- EDA是什么?有什么作用?
- 中科院研发成功2nm光刻机
- 苹果手机哪几个支持无线充电的?
- type-c四根线接法图解
- 华为芯片为什么受制于美国?
- 怎样挑选路由器?
- 元宇宙概念股龙头一览
- 锂电池和铅酸电池哪个好?
- 如何进行编码器的正确接线?接线方法介绍
- 什么是场效应管?它的作用是什么?
- 虚短与虚断的概念介绍及区别
- 晶振的作用是什么?
- 大疆无人机的价格贵吗?大约在什么价位?
- amoled屏幕和oled区别
- 苹果nfc功能怎么复制门禁卡
- 单片机和嵌入式的区别是什么
- 复位电路的原理及作用
- BLDC电机技术分析
- dsp是什么意思?有什么作用?
- 苹果无线充电器怎么使用?
- iphone13promax电池容量是多少毫安
- 芯片的组成材料有什么
- 特斯拉充电桩充电是如何收费的?收费标准是什么?
- 直流电机驱动电路及原理图
- 传感器常见类型有哪些?
- 自举电路图
- 苹果笔记本macbookpro18款与19款区别
- 通讯隔离作用
- 新斯的指纹芯片供哪些客户
- 伺服电机是如何进行工作的?它的原理是什么?
- 无人机价钱多少?为什么说无人机烧钱?
- 以太网VPN技术概述
- 手机nfc功能打开好还是关闭好
- 十大公认音质好的无线蓝牙耳机