线程间通信的几种方法_一起学RTT8 线程通信之邮箱

1什么是线程间通信

    在裸机编程中,经常会使用全局变量进行功能间的通信。如在中断处理函数中改变一个全局变量的值,在main函数中对此全局变量进行读取,根据读取到的全局变量值执行相应的动作,达到通信协作的目的。这其实也是一种通信的方式。在多线程中,主要提供了以下几种多线程的通信方式:邮箱、消息队列、信号。今天就来说说邮箱的方式

2什么是邮箱

    邮箱服务是实时操作系统中一种典型的线程间通信方法。举一个简单的例子,按键控制LED灯的亮灭。在裸机编程中,最简单的方式就是轮询了,伪代码如下所示:

int main(int argc,char *argv[]){    //硬件相关初始化    //按键读取采用非中断的方式    while(1)    {        if(按键的状态)//即IO读取的状态        {          //按下          执行灯亮操作        }        else        {          //弹起          执行灯灭操作        }    }}

上诉的这个代码,看不到有通信的概念在里面,让我们来看一个中断版的伪代码:

static rt_uint8_t g_key_status = 0;//按键中断的处理函数void Exit_IRQ_Handler(void){    if(irq_happen)    {        //清除中断标志        g_key_status = 1;//1代表有键按下    }}int main(int argc,char *argv[]){    //硬件相关初始化    //按键读取采用中断的方式    while(1)    {        if(g_key_status)//读取全局变量的值        {          //按下          g_key_status = 0;//清零          执行灯亮操作        }        else        {          //弹起          执行灯灭操作        }    }}

由读取IO的状态改为读取全局变量的状态,全局变量的状态是在中断函数中进行修改的,这里就有一点点通信的概念的。

如果用多线程,应该是怎样设计的呢?

有两个线程,分别为线程1和线程2。线程1将按键的状态作为邮件发送到邮箱,线程2在邮箱中读取邮件获得按键状态并对LED执行亮灭操作。

代码先不给出,代码在文末的例子里,这里主要说在线程中使用邮箱进行通信的方法。下面我们来看看在RTT中,邮箱的简单使用吧,我们先来看看它的工作机制。

 3RTT中邮箱的工作机制

RTT中定义的邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。发送和接收的方式都分为阻塞和非阻塞两种;非阻塞方式的邮件发送能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为 0 时,邮件收取过程将变成阻塞方式。在这类情况下,只能通过线程进行邮件的收取。

4RTT中邮箱的常用函数

RTT的邮箱结构体定义如下:

/** * mailbox structure */struct rt_mailbox{    struct rt_ipc_object parent;                        /**< inherit from ipc_object */    rt_uint32_t         *msg_pool;                      /**< start address of message buffer */    rt_uint16_t          size;                          /**< size of message pool */    rt_uint16_t          entry;                         /**< index of messages in msg_pool */    rt_uint16_t          in_offset;                     /**< input offset of the message buffer */    rt_uint16_t          out_offset;                    /**< output offset of the message buffer */    rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this mailbox */};typedef struct rt_mailbox *rt_mailbox_t;

rt_mailbox 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。

常用的api函数如下所示:

4a223a0c06250e7a03037bea1af89bbf.png

5示例代码
#include static struct rt_mailbox static_mail_box;static rt_uint8_t mail_box_pool[256] = {0};//一共能放下256/4封邮件static rt_uint8_t mail_box_key_down[]="key_dowm_mb";static rt_uint8_t mail_box_key_up[]="key_up_mb";static rt_uint8_t mail_box_stop[]="stop";/*按键检测,并发送邮箱消息*/static void key_check_thread_entry(void *parameter){    rt_uint8_t count = 0;        while(count < 2)    {        rt_kprintf("key down!count=%d\r\n",count);        /*发送key_down邮箱信息*/        rt_mb_send(&static_mail_box,(rt_uint32_t)&mail_box_key_down);        rt_thread_delay(100);        rt_kprintf("key up!count=%d\r\n",count);        /*发送key_up邮箱信息*/        rt_mb_send(&static_mail_box,(rt_uint32_t)&mail_box_key_up);        rt_thread_delay(100);        count += 1;    }    rt_mb_send(&static_mail_box,(rt_uint32_t)&mail_box_stop);    rt_kprintf("%s will return!\r\n",rt_thread_self()->name);}/*接收邮箱消息,根据接收到的消息控制led灯的亮灭*/static void led_thread_entry(void *parameter){    rt_uint8_t *str = RT_NULL;    while(1)    {        rt_mb_recv(&static_mail_box,(rt_uint32_t *)&str,RT_WAITING_FOREVER);        if(str == mail_box_key_down)        {            rt_kprintf("key down,led will turn on!\r\n");        }        else if(str == mail_box_key_up)        {            rt_kprintf("key up,led will turn off!\r\n");        }        else if(str == mail_box_stop)        {            rt_kprintf("%s will return!\r\n",rt_thread_self()->name);            return ;        }    }}static void mail_box_example_init(void){    rt_err_t err  = 0;    err = rt_mb_init(&static_mail_box,                    "mb",                    mail_box_pool,                    sizeof(mail_box_pool) / 4,                    RT_IPC_FLAG_FIFO);    if(err != RT_EOK)    {        rt_kprintf("init mail box error");        RT_ASSERT(0);    }        rt_thread_t key_check_thread = rt_thread_create("key_t",                                                    key_check_thread_entry,                                                    RT_NULL,                                                    1024,                                                    10,                                                    10);    if(key_check_thread != RT_NULL)    {        rt_thread_startup(key_check_thread);    }        rt_thread_t led_thread = rt_thread_create("led_t",                                              led_thread_entry,                                              RT_NULL,                                              1024,                                              11,                                              10);    if(led_thread != RT_NULL)    {        rt_thread_startup(led_thread);    }}/* 导出到 msh 命令列表中 */MSH_CMD_EXPORT(mail_box_example_init, mail box sample);

进入软件仿真:

在串口窗口输入help命令

064199cad46cfe9c618adb820be489d7.png

紧接着输入mail_box_example_init,支持tab键补全。执行两次后,线程退出。

7f08363c6f00ef15e4b3f13f9268c8a8.png


0b6f174a59699a033fbe7cd2da2e537b.gif原创不易,据说点「在看」会更好看哦~0b6f174a59699a033fbe7cd2da2e537b.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码是用于初始化和启用日志库的后端(backend)的代码片段。它根据所定义的宏来选择性地启用不同的后端。 首先,`backend_id` 是一个整型变量,初始值为 -1。它用于保存后端的标识符,后面会用于检查后端是否成功添加。 然后,通过条件编译语句 `#if defined(NRF_LOG_BACKEND_RTT_ENABLED) && NRF_LOG_BACKEND_RTT_ENABLED` 和 `#if defined(NRF_LOG_BACKEND_UART_ENABLED) && NRF_LOG_BACKEND_UART_ENABLED` 来判断是否启用了 RTT 后端和 UART 后端。 如果启用了 RTT 后端,会调用 `nrf_log_backend_rtt_init()` 函数来进行 RTT 后端的初始化。然后,通过 `nrf_log_backend_add()` 函数将 RTT 后端添加到日志库中,并将其日志级别设置为 `NRF_LOG_SEVERITY_DEBUG`。添加成功后,会对 `backend_id` 进行断言检查,确保添加成功。最后,通过调用 `nrf_log_backend_enable()` 函数来启用 RTT 后端。 如果启用了 UART 后端,会调用 `nrf_log_backend_uart_init()` 函数来进行 UART 后端的初始化。然后,通过 `nrf_log_backend_add()` 函数将 UART 后端添加到日志库中,并将其日志级别设置为 `NRF_LOG_SEVERITY_DEBUG`。添加成功后,会对 `backend_id` 进行断言检查,确保添加成功。最后,通过调用 `nrf_log_backend_enable()` 函数来启用 UART 后端。 通过这段代码,可以根据需要选择性地启用 RTT 或 UART 后端,并将其添加到日志库中,以实现日志的输出功能。请注意,在使用这些后端之前,需要确保已正确初始化相关模块(如 RTT、UART 等)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值