RT-Thread 入门学习笔记 - 熟悉消息队列的使用

背景

  • RT-Thread 的消息队列,用于线程间的通信,命令、数据的传输,非常的方便
  • 消息队列,比邮箱,更方便使用,传输较短的不定长的数据,无须动态申请内存
  • 消息队列接收需要线程
  • 消息队列的发送,可以在中断回调、线程、shell cmd中发送。

 

消息队列可以做什么

  • 线程之间数据与命令的传输,状态机的实现,有了队列缓存,不会丢失操作
  • 消息队列,内容可以不定长,可以不是全局变量,消息队列会备份传输的数据
  • 消息队列可以传输命令与数据,复杂的数据,可以使用自定义的消息体。

 

操作实例

  • 定义消息队列用户的消息体
#ifndef MAX_PHONE_MSG_MQ_SIZE
#define MAX_PHONE_MSG_MQ_SIZE   32
#endif

/* messagequeue struct */
struct mq_msg
{
    rt_uint32_t cmd; /* command or event, msg id */
    rt_uint32_t dat; /* mq load data */
    rt_uint32_t len; /* mq load buffer length */
    char *buf;       /* mq load buffer pointer */
};

enum PHONE_MSG_
{
    MSG_PHONE_OPEN_DIAL = 0,
    MSG_PHONE_INCOMING,
    MSG_PHONE_ANSWER,
    MSG_PHONE_END_DIAL,
};
  • 初始化消息队列,注意使用前,需要初始化消息队列
static rt_err_t phone_mq_init(void)
{
    rt_err_t ret = RT_EOK;

    ret = rt_mq_init(&phone_mq, "phone_mq", &phone_msg_buf,
        sizeof(struct mq_msg), sizeof(phone_msg_buf), RT_IPC_FLAG_FIFO);

    if (ret != RT_EOK)
    {
        LOG_E("%s : error!", __func__);
        return -RT_ERROR;
    }

    return ret;
}
  • 创建消息队列的接收处理线程,接收是堵塞的,需要放在线程
static void phone_task_entry(void *param)
{
    struct mq_msg recv_msg = { 0 };

    while(1)
    {
        if (rt_mq_recv(&phone_mq, &recv_msg, sizeof(recv_msg), RT_WAITING_FOREVER) == RT_EOK)
        {
            switch (recv_msg.cmd)
            {
                case MSG_PHONE_OPEN_DIAL:
                    LOG_D("%s: MSG_PHONE_OPEN_DIAL.", __func__);
                    break;
                case MSG_PHONE_INCOMING:
                    LOG_D("%s: MSG_PHONE_INCOMING.", __func__);
                    break;
                case MSG_PHONE_END_DIAL:
                    LOG_D("%s: MSG_PHONE_END_DIAL.", __func__);
                    break;
                case MSG_PHONE_ANSWER:
                    LOG_D("%s: MSG_PHONE_ANSWER.", __func__);
                    break;
                default:
                    break;
            }
        }
        phone_mq_free(&recv_msg);
    }
}

/* 消息队列接收线程初始化 */
int phone_task_init(void)
{
    rt_thread_t tid;
    phone_mq_init();

    tid = rt_thread_create("phone", phone_task_entry, RT_NULL, 2048, 10, 20);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
        LOG_D("%s: end.", __func__);
        return 0;
    }
    else
    {
        LOG_E("%s: error!", __func__);
        return -1;
    }
}

INIT_APP_EXPORT(phone_task_init);
  • 发送消息,这里使用shell命令,发送可以放在线程、中断、回调里面,注意不要在中断中申请内存。
/* 发送消息 */
rt_err_t phone_send_msg(struct mq_msg *msg)
{
    if (msg == RT_NULL)
        return -RT_ERROR;

    return rt_mq_send(&phone_mq, msg, sizeof(struct mq_msg));
}

/* 消息中free掉用户动态申请的内存【按需】 */
static void phone_mq_free(struct mq_msg *msg)
{
    if (msg)
    {
        if (msg->buf)
        {
            rt_free(msg->buf);
            msg->buf = RT_NULL;
        }
    }
}

/* shell命令,用于验证发送命令 */
void phone_open_dial(void)
{
    struct mq_msg msg = { 0 };

    msg.cmd = MSG_PHONE_OPEN_DIAL;
    phone_send_msg(&msg);
}

void phone_end_dial(void)
{
    struct mq_msg msg = { 0 };

    msg.cmd = MSG_PHONE_END_DIAL;
    phone_send_msg(&msg);
}

void phone_answer_dial(void)
{
    struct mq_msg msg = { 0 };

    msg.cmd = MSG_PHONE_ANSWER;
    phone_send_msg(&msg);
}

MSH_CMD_EXPORT(phone_open_dial, phone_open_dial);
MSH_CMD_EXPORT(phone_end_dial, phone_end_dial);
MSH_CMD_EXPORT(phone_answer_dial, phone_answer_dial);

编译、下载与功能验证

  • 使用STM32L4平台,或PC 模拟器
  • list_thread查看消息队列接收线程
msh >list_thread
thread   pri  status      sp     stack size max used left tick  error
-------- ---  ------- ---------- ----------  ------  ---------- ---
tshell    20  running 0x00000088 0x00001000    12%   0x00000005 000
phone     10  suspend 0x000000ac 0x00000800    08%   0x00000005 000
tidle0    31  ready   0x00000080 0x00000400    12%   0x00000003 000
main      10  suspend 0x0000008c 0x00000800    13%   0x00000009 000
  • 发送消息
msh >phone_open_dial
[D/phone] phone_task_entry: MSG_PHONE_OPEN_DIAL.
msh >phone_end_dial
[D/phone] phone_task_entry: MSG_PHONE_END_DIAL.
msh >phone_answer_dial
[D/phone] phone_task_entry: MSG_PHONE_ANSWER.

 

问题总结

  • 消息队列初始化后才能使用,如果使用模拟器器,自动初始化功能可能失效,需要手动初始化。
  • rt_mq_recv接收函数,需要判断是否执行成功,== RT_EOK不能省掉,否则接收消息成功,判断失败造成不处理消息。
if (rt_mq_recv(&phone_mq, &recv_msg, sizeof(recv_msg), RT_WAITING_FOREVER) == RT_EOK) /* 【== RT_EOK不能丢,否则无法进入接收,原因是RT_EOK是0 】 */

 

总结

  • 正确使用RT-Thread,解决线程间的数据与命令交换问题,可以用于数据的传输
  • 熟悉线程间的通信IPC。

猜你喜欢

转载自blog.csdn.net/tcjy1000/article/details/114900996