第13篇 zephyr 数据传递之Message Queues

本文详细介绍了Zephyr操作系统中的消息队列概念及其实现。消息队列作为一个内核对象,允许线程和ISR异步发送和接收固定大小的数据项。文章深入探讨了消息队列的关键属性、初始化过程、数据的发送与接收机制,以及如何查看队列头部数据项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

摘要

1 概念

2 实现

2.1 定义消息队列

2.2 写数据到消息队列

2.3 从消息队列读一个数项

2.4 查看消息队列的头数项

3 参考链接:


本学笔记基于zephyr 工程版本 2.2.99,主机环境为ubuntu18.04,开发平台 nrf52840dk_nrf52840

摘要

消息队列是一个内核对象,简单的实现了消息队列的功能。允许线程和ISR异步的发送和接收固定大小的数据项。

1 概念

任意多个消息队列可以被定义。引用消息队列时使用的是它的地址。

一个消息队列有如下几个关键属性:

ring buffer:它存储已经被发送但是没有被接收的数据项。

data item size:数据项的大小(字节)。

maximum quqntity:可以被入队在环形缓冲区中数据项的最大个数。

消息队列的ring buffer内存必须是N个字节对齐,N是2的幂数,比如(1,2,4,8...)。并且确保存储到队列的消息也是按照这种方式进行对齐。

消息队列使用之前必须被初始化,初始化会清空环形缓冲区。

一个数据项可以被县城或者ISR发送,如果有等待消息的线程存在,那么这个数据项直接拷贝到这个线程(注意不是把数据项地址给这个等待的线程,而是直接把数据项的内容到复制到接收线程指定的地址)。其他情况,如果ring buffer有空间,数据项会拷贝到消息队列的ring buffer。前面说的FIFO是存储传递的是地址,而消息队列是内容,这需要与之前的讲的FIFO进行区分。无论什么时候,发送的数据的大小,必须和定义是消息队列的数据项大小一致。

如果发送线程向一个已满的消息队列的ring buffer发送数据,那么线程将被阻塞,知道ring buffer有空闲的存储空间。这个动作可以任意多个线程同时执行。也就是多可线程可以同时向这个已满的队列中写数据,但是都会被阻塞。当队列有空间时,那个优先级最高等待时间最长的队列,可以被唤醒,然后向队列中写数据。

一个数据项可以被线程接收。这个数据项被拷贝到线程指定的内存地址(就是前面提到的,是传值,而不是址)。这个指定的接收区域的空间,必须等于数据项大小。

接收时也是类似,如果队列是空,也就是没有数据项时,那么这个接收线程,将被阻塞。任意多个线程可以同时接收同一个消息,没有消息时都被阻塞,当有消息时优先级最高等待时间最长的线程将被唤醒接收数据。

收受线程还可以查看消息队列中的head消息,换句话说,就是读取消息队列的数据项之后,不把这个数据项从队列中删除。

内核可以允许ISR去接收一个消息队列,但是不要尝试去等待一个空的消息队列。

2 实现

2.1 定义消息队列

可以使用struct k_msgq类型定义一个消息队列,定义之后必须使用k_msgq_init()进行初始化。

下面的示例代码,定义一个有10个12个字节长的数据项,并初始化为空。

struct data_item_type {
    u32_t field1;
    u32_t field2;
    u32_t field3;
};

char __aligned(4) my_msgq_buffer[10 * sizeof(data_item_type)];
struct k_msgq my_msgq;

k_msgq_init(&my_msgq, my_msgq_buffer, sizeof(data_item_type), 10);

或者,使用K_MSGQ_DEFINE在编译时定义定初始化。和上面的代码效果相同:

K_MSGQ_DEFINE(my_msgq, sizeof(data_item_type), 10, 4);

2.2 写数据到消息队列

可以使用k_msgq_put()添加一个数据项到消息队列。

下面的示例代码,从一个生产者传输数据到多个消费者。如果数据没有被消费者及时取出,那么生产者会清空队列,继续存入新数据。

void producer_thread(void)
{
    struct data_item_t data;

    while (1) {
        /* create data item to send (e.g. measurement, timestamp, ...) */
        data = ...

        /* send data to consumers */
        while (k_msgq_put(&my_msgq, &data, K_NO_WAIT) != 0) {
            /* message queue is full: purge old data & try again */
            k_msgq_purge(&my_msgq);
        }

        /* data item was successfully added to message queue */
    }
}

2.3 从消息队列读一个数项

可以使用k_msgq_get()从队列中读取一个数据项。

作为消费者去读取数据项:

void consumer_thread(void)
{
    struct data_item_t data;

    while (1) {
        /* get a data item */
        k_msgq_get(&my_msgq, &data, K_FOREVER);

        /* process data item */
        ...
    }
}

2.4 查看消息队列的头数项

可以使用k_msgq_peek()函数去看看一个数据项。

下面的示例代码,查看head数据项:

void consumer_thread(void)
{
    struct data_item_t data;

    while (1) {
        /* read a data item by peeking into the queue */
        k_msgq_peek(&my_msgq, &data);

        /* process data item */
        ...
    }
}

3 参考链接:

https://docs.zephyrproject.org/latest/reference/kernel/data_passing/message_queues.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值