基于RT-Thread Studio下的正点原子STM32F103ZET6开发————(8)线程间通信(邮箱)

本文介绍了RT-Thread操作系统中的邮箱机制,包括其工作原理、控制块结构、相关API如创建、发送、接收邮件等,以及通过示例代码展示了如何在两个线程间使用邮箱进行通信。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、邮箱

1.1 工作机制

1.2 控制块

1.3 相关API

1.3.1 创建和删除邮箱

1.3.2 初始化和脱离邮箱

1.3.3 发送邮件

1.3.4 等待方式发送邮件

1.3.5 发送紧急邮件

1.3.6 接收邮件

二、代码实现

三、总结

前言

接下来我们将学习内核中的线程间的通信方式之一,邮箱,我们实现创建两个线程,线程1按键按下发送邮件,线程二接收邮件灭灯。


一、邮箱

1.1 工作机制

RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封 4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。

1.2 控制块

邮箱控制块结构的详细定义请见以下代码:

struct rt_mailbox
{
    struct rt_ipc_object parent;

    rt_uint32_t* msg_pool;                /* 邮箱缓冲区的开始地址 */
    rt_uint16_t size;                     /* 邮箱缓冲区的大小     */

    rt_uint16_t entry;                    /* 邮箱中邮件的数目     */
    rt_uint16_t in_offset, out_offset;    /* 邮箱缓冲的进出指针   */
    rt_list_t suspend_sender_thread;      /* 发送线程的挂起等待队列 */
};
typedef struct rt_mailbox* rt_mailbox_t;

1.3 相关API

邮箱的相关接口如下图所示,对一个邮箱的操作包含:创建 / 初始化邮箱、发送邮件、接收邮件、删除 / 脱离邮箱。

1.3.1 创建和删除邮箱
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)		
创建邮箱
调用该函数创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间用来存放邮件,这块内存的大小等于邮件大小(4 字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。

参数
name	邮箱的名称
size	邮箱容量
flag	邮箱标志,它可以取值:RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回
返回创建的邮箱对象,创建失败时返回 RT_NULL
rt_err_t rt_mb_delete(rt_mailbox_t mb)	
删除邮箱
当用rt_mb_create()创建的邮箱不再被使用时,应该调用该函数删除它来释放相应的系统资源,一旦操作完成,邮箱将被永久性的删除。删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是- RT_ERROR),然后再释放邮箱使用的 内存,最后删除邮箱对象。

参数
mb	邮箱对象的句柄
返回
错误代码
1.3.2 初始化和脱离邮箱

rt_err_t rt_mb_init	(rt_mailbox_t mb, const char *name, void *msgpool, rt_size_t size, rt_uint8_t flag)		
初始化邮箱
该函数将初始化邮箱并将其置于内核管理器的控制之下。

参数
mb	    邮箱对象的句柄
name	邮箱的名称
msgpool	缓冲区指针
size	邮箱容量
flag	邮箱标志,它可以取数值:RT_IPC_FLAG_FIFO或RT_IPC_FLAG_PRIO
返回
RT_EOK

rt_err_t rt_mb_detach(rt_mailbox_t mb)	
脱离邮箱
该函数将将把静态初始化的邮箱对象从内核对象管理器中删除。使用该函数接口后, 内核先唤醒所有挂在该邮箱上的线程(线程获得返回值是 - RT_ERROR ),然后将 该邮箱对象从内核对象管理器中删除。

参数
mb	    邮箱对象的句柄
返回
RT_EOK
1.3.3 发送邮件
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value)		
发送邮件
线程或者中断服务程序可以通过调用该函数给其他线程发送邮件。发送的邮件可以是32位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到- RT_EFULL 的返回值。

参数
mb	    邮箱对象的句柄
value	邮件内容
返回
RT_EOK 发送成功;-RT_EFULL 邮箱已经满了
1.3.4 等待方式发送邮件

rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout)		
等待方式发送邮件
用户也可以通过调用该函数接口向指定邮箱发送邮件。它与rt_mb_send()的区别在于,如果邮箱已经满了,那么发送线程将根据设定的timeout参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒返回错误码。

参数
mb	    邮箱对象的句柄
value	邮件内容
timeout	超时时间
1.3.5 发送紧急邮件
rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value)
发送紧急邮件的过程与发送邮件几乎一样,唯一的不同是,当发送紧急邮件时,邮件被直接插队放入了邮件队首,这样,接收者就能够优先接收到紧急邮件,从而及时进行处理。

参数
mb	        邮箱对象的句柄
value	    邮件内容
返回
RT_EOK	    发送成功
-RT_EFULL	邮箱已满
1.3.6 接收邮件
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout)		
接收邮件
只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值, 否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。

参数
mb	    邮箱对象的句柄
value	邮件内容
timeout	超时时间
返回
RT_EOK 发送成功;-RT_ETIMEOUT 超时;-RT_ERROR 失败,返回错误号

二、代码实现

#include <rtthread.h>
#include <drv_common.h>
#include <rtdevice.h>
#include <rtdbg.h>

#define RED_LED     GET_PIN(B,5)
#define GREEN_LED   GET_PIN(E,5)
#define KEY_0       GET_PIN(E,4)
#define KEY_1       GET_PIN(E,3)
#define STACK_SIZE  512
#define PRIORITY    5
#define TICK        10

static rt_thread_t  key_tid = RT_NULL;
static rt_mailbox_t mb =   RT_NULL;
static rt_thread_t  dect = RT_NULL;

char test[] = "press";

static void KEY_thread_entry(void *paramenter)
{
    while(1)
    {
        if(rt_pin_read(KEY_0) == PIN_LOW)
        {
            rt_thread_mdelay(100);
            if(rt_pin_read(KEY_0) == PIN_LOW)
            {
                rt_mb_send(mb, (rt_uint32_t)&test);
                rt_kprintf("send sucessfully\n");
            }
        }
    }
}

static void DECT_thread_entry(void *paramenter)
{
    rt_err_t result = RT_NULL;
    char *str = RT_NULL;
    while(1)
    {
        result = rt_mb_recv(mb, (rt_uint32_t)&str, RT_WAITING_FOREVER);
        if(result == RT_EOK)
        {
            rt_kprintf("recv sucessfully\n");
            rt_kprintf("%s\n",str);
            rt_pin_write(RED_LED, PIN_HIGH);
        }
    }
    rt_mb_detach(mb);
}

int main(void)
{
    rt_kprintf("start\n");
    rt_pin_mode(RED_LED, PIN_MODE_OUTPUT);
    rt_pin_write(RED_LED, PIN_LOW);
    rt_pin_mode(KEY_0, PIN_MODE_INPUT_PULLUP);

    mb = rt_mb_create("send", STACK_SIZE, RT_IPC_FLAG_PRIO);
    if(mb != RT_NULL)
    {
        rt_kprintf("create mb successfully\n");
    }

    key_tid = rt_thread_create("key", KEY_thread_entry, RT_NULL, STACK_SIZE, PRIORITY, TICK);
    dect = rt_thread_create("detection", DECT_thread_entry, RT_NULL, STACK_SIZE, PRIORITY + 1, TICK);
    rt_thread_startup(key_tid);
    rt_thread_startup(dect);

    while (1)
    {
        rt_thread_mdelay(1000);
    }
    return RT_EOK;
}

三、总结

邮箱是一种简单的线程间消息传递方式,特点是开销比较低,效率较高。在 RT-Thread 操作系统的实现中能够一次传递一个 4 字节大小的邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数 (邮件数由创建、初始化邮箱时指定的容量决定)。邮箱中一封邮件的最大长度是 4 字节,所以邮箱能够用于不超过 4 字节的消息传递。由于在 32 系统上 4 字节的内容恰好可以放置一个指针,因此当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中,即邮箱也可以传递指针。

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱跑流水灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值