目录
摘要
1 概念
2 实现
3 参考链接
本学笔记基于zephyr 工程版本 2.2.99,主机环境为ubuntu18.04,开发平台 nrf52840dk_nrf52840
摘要
FIFO是一个内核对象,实现了传统的先进先出(first in, first out)队列,允许线程和ISR添加或者移除任意大小的数据项。
1 概念
任意数量的FIFO可以被定义。引用fifo时,使用的是FIFO定义的内存地址,同样只要你的内存允许,就可以随便定义。
FIFO有如下关键属性:
一个queue,他存储已经被添加但是还没有被移除的数据项。这个queue是用简单的链表实现的(实际上就是一个由链表实现的队列)。FIFO的功能是基于这个先进先出的queue实现的,FIFO就相当于queue的head。后面还会讲LIFO,也是一个queue,但是是一个后进先出的queue。
一个FIFO必须先初始化在使用,初始化设置这个队列为空。
FIFO的数据项对必须保持字(word,cortext-M是4字节)对齐,这个数据项的第一个字节被内核使用,用于存储在queue中下一个数据项的内存地址。因此,一个有N字节应用数据的数据项,需要占用N+4(or N+8, 64位系统中word是8字节)内存空间。如果使用k_fifo_alloc_put()个函数添加一个数据项到一个队列,就不需要指向下一个地址的保留word。因为这个函数会从线程的资源池中分配临时的内存用于存储下一个数据项的地址。
一个数据项可以被ISR或者线程添加,如果有正在等待数据项的线程(就是读取队列,队列中没有数据项,线程休眠了),那么添加一个数据项之后,数据项不被添加到FIFO的quue中,而是直接给等待的线程。也就是数据项不是由发送线程存储到FIFO的queue中,然后再由等在线程从queue中取出。而是直接从发送线程传给因为接收而等待的线程,不会经过queue的。相反,如果没有等待的接收线程,那么才会添加数据项到FIFO的queue。
一个数据可以被一个线程移除(也就是取出),如果FIFO中没有数据元素,那么线程可以选择等待FIFO不为空(就是说线程获取不到FIFO进入睡眠,当FIFO不为空的时候再唤醒)。可以有多个线程同时等待一个空的FIFO。当有数据项添加到FIFO中,那个优先级最高等待时间最长的线程,会优先唤醒去读取数据。
2 实现
2.1 定义FIFO
使用struct k_fifo类型定义一个FIFO变量。使用之前必须用k_fifo_init()进行初始化:
struct k_fifo my_fifo;
k_fifo_init(&my_fifo);
或者,在编译时使用K_FIFO_DEFINE宏定义和初始化一个FIFO:
K_FIFO_DEFINE(my_fifo);
2.2 写数据项到FIFO
可以调用k_fifo_put()函数,向FIFO中添加一个数据项。
下面的示例发送数据到一个或多个用户线程:
struct data_item_t {
void *fifo_reserved; /* 1st word reserved for use by fifo */
...
};
struct data_item_t tx_data;
void producer_thread(int unused1, int unused2, int unused3)
{
while (1) {
/* create data item to send */
tx_data = ...
/* send data to consumers */
k_fifo_put(&my_fifo, &tx_data);
...
}
}
也可以使用k_fifo_alloc_put()函数添加,那么添加的数据项内存中第一个word也是有效的,不会被内核占中。
2.3 从FIFO中读数据
可以调用k_fifo_get()从FIFO中读取一个数据项:
void consumer_thread(int unused1, int unused2, int unused3)
{
struct data_item_t *rx_data;
while (1) {
rx_data = k_fifo_get(&my_fifo, K_FOREVER);
/* process fifo data item */
...
}
}
3 参考链接
https://docs.zephyrproject.org/latest/reference/kernel/data_passing/fifos.html