数据传递
Message queue 消息队列
消息队列是一种线程安全的数据容器,可容纳固定数量的数据(消息),多个线程可同时安全地访问这些数据。队列可以存储不同类型的数据,如变量、结构体、指针或其他任何你喜欢的类型。队列可容纳对象的最大数量仅受系统可用 RAM 容量的限制。 内核负责向队列中添加新数据和删除数据的所有必要操作和保护措施。换句话说,消息队列是一个内核对象。这意味着你不必担心手动管理队列的内部机制,内核会对其进行管理。 此外,队列还支持超时。您可以使用超时来执行以下操作:
- 如果队列已满,则使将数据放入队列的线程进入睡眠状态。
- 如果队列为空,则使从队列中获取数据的线程进入睡眠状态。 如果消息队列为空,则任意数量的接收线程可以同时等待,当数据项可用时,该数据项将提供给优先级最高的接收线程。同样的概念也适用于在消息队列已满时发送线程。 虽然可以使用带有中断的消息队列,但请注意不要触发长操作或阻塞操作,并且切勿将超时选项(例如,
K_FOREVER
)与中断一起使用。 在消息中放入哪些数据 消息的数据类型是静态设置的,因此以后无法动态更改。可以将消息定义为简单的整数、字符串、结构体或结构体中的联合体。与为每种数据类型分配单独的内存空间相比,在结构体中使用联合体可以节省内存,尤其是在不需要同时使用这些数据类型时。
struct MyStruct {
int dataType; // Indicates the active data type
union {
int intValue;
float floatValue;
char stringValue[24];
} data;
};
定义消息队列并初始化 可以使用 K_MSGQ_DEFINE() 同时执行这两个操作,它采用四个参数
- q_name - 信息队列的名称。
- q_msg_size- 报文大小(字节)。
- q_max_msgs - 可排队的最大信息数。
- q_align - 信息队列环形缓冲区的对齐方式。 下面的简单代码段展示了如何定义和初始化一个包含 16 条信息的信息队列(device_message_queue)。每个元素的大小为 4 字节(uint32_t)。为了对齐,代码段指定了 sizeof(uint32_t),但你也可以直接传入 4。
K_MSGQ_DEFINE(device_message_queue, sizeof(uint32_t), 16, 4);
将消息写入消息队列 使用 k_msgq_put()
函数编写消息,该函数需要三个参数:定义的消息队列 msgq 、指向定义的消息类型的指针 data 以及超时选项。使用 timeout 选项,您可以决定如果消息队列已满,会发生什么情况:
K_FOREVER
– 放置数据的线程无限期等待,直到消息队列中有空间,这意味着接收线程已经消耗了一条消息。K_MSEC()
– 使用此选项时,放置数据的线程等待指定的时间。K_NO_WAIT
– 放置数据的线程不执行任何操作。这意味着不会添加新数据,并且会丢失。或者可以使用此选项放弃队列中的所有消息并插入新数据。这是通过检查函数的返回值并手动调用k_msgq_purge()
来完成的。 从消息队列中读取消息 要读取消息,使用k_msgq_get()
函数。请注意,调用该函数将从消息队列中移除消息(称为 "弹出消息")。这是推荐的方法,因为不希望消息队列被填满。另一种方法是使用k_msgq_peek()
函数,它在读取消息时不会将其从消息队列中移除。 函数参数与写入函数相同
FIFO
(FIFO)是一种内核对象,用于简化传统的先进先出(FIFO)队列结构。它能让线程和中断服务例程(ISR)从队列结构中添加或删除任意数量、大小不一的数据项。 指定用于存储项目的堆内存池的大小。 默认情况下, CONFIG_HEAP_MEM_POOL_SIZE
设置为零。因此,您需要将其设置为一个值,该值表示给定时间FIFO中可以拥有的最大元素数。
CONFIG_HEAP_MEM_POOL_SIZE=4096
定义 FIFO 使用 K_FIFO_DEFINE()
宏静态定义 FIFO。 ```c K_FIFO_DEFINE(my_fifo);
与消息队列不同,不需要在定义中指定 FIFO 的数据类型和大小。
**指定项目的数据类型。**
**这里的要点是将数据项定义为结构体,其中第一个成员始终是一个保留的 void 指针**。之所以需要这样做,是因为内核内部将 FIFO 作为一个简单的链表来实现,而一个数据项的第一个字应被保留,作为指向 FIFO 中下一个数据项的指针。下面的第一个示例显示了一个数据项结构,其中的数据有一个定义的大小。
```c
struct data_item_t {
void *fifo_reserved;
uint8_t data[256];
uint16_t len;
};
或者显示可变大小内存指针
struct data_item_var_t {
void *fifo_reserved;
void *data;
uint16_t len;
};
将数据项添加到 FIFO 使用 k_fifo_put()
函数添加项目 ```c struct data_item_t *buf = k_malloc(sizeof(struct data_item_t)); if (buf == NULL){ return ; }
k_fifo_put(&my_fifo,buf);
**从 FIFO 读取数据项**
使用 `k_fifo_get()` 函数读取项目
调用 `k_fifo_get()` 会从 FIFO 中删除数据。但是,开发人员必须通过随后调用 `k_free()` 来处理从堆内存中删除分配的内存。如果无法正确释放分配给已使用数据项的内存,将导致运行时错误(堆溢出)。
使用 timeout 参数允许线程在 FIFO 的队列为空的情况下等待给定数据项。任意数量的线程可以同时等待空的FIFO。添加数据项时,该数据项将分配给等待时间最长的最高优先级线程。
```c
struct data_item_t *rec_item = k_fifo_get(&my_fifo, K_FOREVER);
k_free(rec_item);
此外,重要的是要记住,不能在 IFO中 重复添加相同的数据项。这很可能会破坏 FIFO 内部使用的链表,并导致未定义的行为。