Zephyr OS 中的 FIFO 接口应用介绍

目录

概述

[1 FIFO的接口函数](#1 FIFO的接口函数)

[1.1 K_FIFO_DEFINE函数](#1.1 K_FIFO_DEFINE函数)

[1.2 k_fifo_init函数](#1.2 k_fifo_init函数)

[1.3 k_fifo_put函数](#1.3 k_fifo_put函数)

[1.4 k_fifo_get 函数](#1.4 k_fifo_get 函数)

[1.5 k_fifo_is_empty 函数](#1.5 k_fifo_is_empty 函数)

[2 应用验证](#2 应用验证)

[2.1 UART中使用FIFO范例](#2.1 UART中使用FIFO范例)

[2.2 生产-消费类型范例](#2.2 生产-消费类型范例)

[3 注意事项](#3 注意事项)

[3.1 内存管理](#3.1 内存管理)

[3.2 线程安全边界](#3.2 线程安全边界)


概述

Zephyr RTOS 提供了多种 FIFO (First-In-First-Out) 实现方式,适用于不同场景的数据缓冲需求。以下是主要的 FIFO 接口和使用方法。

1 FIFO的接口函数

FIFO 接口主要API如下这些,其具体函数如下:

函数 描述
K_FIFO_DEFINE(name) 定义并初始化FIFO
k_fifo_init(fifo) 运行时初始化FIFO
k_fifo_put(fifo, data) 向FIFO放入数据
k_fifo_get(fifo, timeout) 从FIFO获取数据
k_fifo_is_empty(fifo) 检查FIFO是否为空

1.1 K_FIFO_DEFINE函数

K_FIFO_DEFINE 是 Zephyr RTOS 中用于定义和初始化 FIFO(先进先出队列)的宏,它是 Zephyr 内核提供的一种线程安全的数据传递机制。

1) 基本语法

cpp 复制代码
K_FIFO_DEFINE(name)

2)功能说明

  1. 作用

    • 静态定义并初始化一个 FIFO 队列

    • 创建的 FIFO 可以被多个线程安全地访问

    • 支持阻塞和非阻塞操作

  2. 特性

    • 线程安全:内置同步机制,无需额外锁

    • 动态扩展:基于链表实现,理论上无大小限制

    • 支持超时等待:消费者可以阻塞等待数据

3) 内部实现原理

K_FIFO_DEFINE 实际上创建了一个 struct k_fifo 结构体,其核心实现基于:

  1. 链表结构 :使用 Zephyr 的 sys_slist_t 单链表

  2. 同步机制:内置内核信号量保证线程安全

  3. 等待队列:当 FIFO 为空时,允许消费者线程阻塞等待

1.2 k_fifo_init函数

k_fifo_init 是 Zephyr RTOS 中用于运行时初始化 FIFO(先进先出队列) 的函数,与静态初始化宏 K_FIFO_DEFINE 相对应,提供了动态初始化 FIFO 的能力。

1)函数原型

cpp 复制代码
void k_fifo_init(struct k_fifo *fifo);

2)参数说明

参数说明

参数 类型 描述
fifo struct k_fifo* 指向要初始化的FIFO结构体的指针

3) 核心功能

  1. 初始化FIFO内部状态

    • 初始化链表头(用于存储数据项)

    • 初始化等待队列(用于阻塞的消费者线程)

    • 重置所有内部状态标志

  2. 线程安全保证

    • 初始化后的FIFO可安全用于多线程环境

    • 内置同步机制,无需额外锁

3)使用范例

  1. 动态创建FIFO
cpp 复制代码
#include <zephyr/kernel.h>

struct k_fifo my_fifo;

void init_my_fifo(void)
{
    k_fifo_init(&my_fifo);
}
  1. 模块化设计中的FIFO初始化
cpp 复制代码
// 模块头文件
struct data_processor {
    void *fifo_reserved;
    // 其他成员...
};

// 模块实现
int data_processor_init(struct data_processor *proc)
{
    if (proc == NULL) {
        return -EINVAL;
    }
    
    k_fifo_init(&proc->input_fifo);
    // 其他初始化...
    return 0;
}

4) 与K_FIFO_DEFINE对比

特性 k_fifo_init K_FIFO_DEFINE
初始化方式 运行时动态初始化 编译时静态初始化
存储位置 需自行管理内存 自动分配在全局数据区
使用场景 动态创建的对象需要FIFO时 全局或模块级FIFO
线程安全
初始化时机 显式调用时 系统启动时自动初始化

1.3 k_fifo_put函数

k_fifo_put 是 Zephyr RTOS 中用于向 FIFO (先进先出队列) 放入数据项的核心函数,它实现了线程安全的数据生产者-消费者模式。

1)函数原型

cpp 复制代码
void k_fifo_put(struct k_fifo *fifo, void *data);

2)参数说明

参数 类型 描述
fifo struct k_fifo* 指向已初始化的FIFO对象的指针
data void* 要放入队列的数据项指针

3)核心功能与特性

  1. 线程安全操作

    • 内部使用内核锁保证多线程/中断环境下的安全访问

    • 支持多生产者并发写入

  2. 唤醒机制

    • 如果有线程在 k_fifo_get 上阻塞等待数据,会自动唤醒最早等待的线程

    • 唤醒的线程将获得刚放入的数据项

  3. 无阻塞设计

    • 函数立即返回,不会阻塞调用线程

    • 适合在中断上下文使用(需配合 K_NO_WAIT 内存分配)

  4. 数据所有权转移

    • 数据项所有权从生产者转移到FIFO,再由消费者获取

    • 放入后生产者不应再访问该数据项

4)使用示例

-1) 基础使用模式

cpp 复制代码
#include <zephyr/kernel.h>

// 定义数据结构
struct sensor_data {
    void *fifo_reserved;
    int32_t temperature;
    uint32_t timestamp;
};

K_FIFO_DEFINE(sensor_fifo);

void sampling_thread(void)
{
    while (1) {
        // 动态分配数据项
        struct sensor_data *data = k_malloc(sizeof(*data));
        
        // 填充数据
        data->temperature = read_temperature();
        data->timestamp = k_uptime_get_32();
        
        // 放入FIFO
        k_fifo_put(&sensor_fifo, data);
        
        k_sleep(K_MSEC(1000));
    }
}

-2) 中断上下文使用

cpp 复制代码
void isr_handler(const struct device *dev, void *user_data)
{
    static struct event_data *evt;
    
    if (!evt) {
        // 预分配避免ISR中动态分配
        evt = k_malloc(sizeof(*evt));
    }
    
    if (evt) {
        evt->event_type = DEVICE_EVENT;
        evt->event_code = read_event_code();
        
        // ISR中只能使用非阻塞操作
        if (k_fifo_put(&event_fifo, evt) == 0) {
            evt = NULL;  // 成功放入后重置指针
        }
    }
}
  1. 与相关函数对比
函数 特点 适用场景
k_fifo_put 单数据项放入 通用场景
k_fifo_put_list 批量放入多个数据项 批量生产场景
k_fifo_put_slist 使用系统单链表批量放入 已有链表结构的场景
k_queue_alloc_append 带内存分配的放入 需要自动分配内存的场景

1.4 k_fifo_get 函数

k_fifo_get 是 Zephyr RTOS 中用于从 FIFO (先进先出队列) 获取数据项的核心函数,它实现了线程安全的消费者接口,支持阻塞和非阻塞两种模式。

1)函数原型

cpp 复制代码
void *k_fifo_get(struct k_fifo *fifo, k_timeout_t timeout);

2)参数说明

参数 类型 描述
fifo struct k_fifo* 指向已初始化的FIFO对象的指针
timeout k_timeout_t 指定等待超时时间,可以是: K_NO_WAIT(非阻塞) K_FOREVER(永久阻塞) 具体时间值

3)返回值

  • 成功时:返回获取到的数据项指针

  • 超时或失败:返回 NULL

4)核心功能和特性

  1. 线程安全操作

    • 内部使用内核锁保证多线程环境下的安全访问

    • 支持多消费者并发获取

  2. 灵活的等待策略

    • 非阻塞模式 (K_NO_WAIT):立即返回,不等待

    • 阻塞模式 :可以指定超时时间或无限等待(K_FOREVER)

    • 定时等待 :如 K_MSEC(100) 表示最多等待100毫秒

  3. 优先级继承

    • 当多个线程等待同一个FIFO时,高优先级线程会优先被唤醒
  4. 内存所有权转移

    • 获取到的数据项所有权从FIFO转移到消费者

    • 消费者负责后续的内存管理(通常需要释放)

5)使用范例

-1)基础使用模式

cpp 复制代码
#include <zephyr/kernel.h>

K_FIFO_DEFINE(data_fifo);

void consumer_thread(void)
{
    while (1) {
        // 阻塞等待数据,最多等待200ms
        struct sensor_data *data = k_fifo_get(&data_fifo, K_MSEC(200));
        
        if (data != NULL) {
            // 处理数据
            process_data(data);
            
            // 释放内存
            k_free(data);
        } else {
            // 超时处理
            handle_timeout();
        }
    }
}

-2)多消费者协作模式

cpp 复制代码
void consumer_group(void)
{
    struct k_fifo *fifo = &shared_fifo;
    
    while (1) {
        void *data = k_fifo_get(fifo, K_FOREVER);
        
        // 根据数据类型分发处理
        if (is_type_a(data)) {
            process_type_a(data);
        } else if (is_type_b(data)) {
            process_type_b(data);
        }
        
        k_free(data);
    }
}

-3)检查返回值

cpp 复制代码
// 错误:未检查返回值
struct data *item = k_fifo_get(&fifo, K_MSEC(100));
item->value = 0;  // 可能解引用NULL

// 正确:必须检查返回值
struct data *item = k_fifo_get(&fifo, K_MSEC(100));
if (item != NULL) {
    // 安全访问
    process(item);
    k_free(item);
}

-4)内存管理责任

cpp 复制代码
void *data = k_fifo_get(&fifo, K_FOREVER);
process(data);
k_free(data);  // 必须释放

1.5 k_fifo_is_empty 函数

k_fifo_is_empty 是 Zephyr RTOS 中用于检查 FIFO (先进先出队列) 是否为空的辅助函数,它提供了一种非破坏性的方式来查询 FIFO 的当前状态。

1)函数原型

cpp 复制代码
bool k_fifo_is_empty(struct k_fifo *fifo);

2)参数说明

参数 类型 描述
fifo struct k_fifo* 指向已初始化的FIFO对象的指针

3) 返回值

  • true:FIFO 为空(没有数据项)

  • false:FIFO 不为空(至少有一个数据项)

4)核心功能与特性

  1. 非破坏性检查

    • 仅查询状态,不会修改FIFO内容

    • 不会影响任何等待线程

  2. 线程安全

    • 内部使用内核锁保证多线程环境下的安全访问

    • 可以在任何上下文中调用(包括中断)

  3. 轻量级操作

    • k_fifo_get 更轻量,适合状态检查

    • 无阻塞,立即返回结果

5)使用范例

-1)基本使用模式

cpp 复制代码
#include <zephyr/kernel.h>

K_FIFO_DEFINE(data_fifo);

void consumer_thread(void)
{
    // 先检查是否有数据
    if (!k_fifo_is_empty(&data_fifo)) {
        struct data_item *item = k_fifo_get(&data_fifo, K_NO_WAIT);
        process_item(item);
        k_free(item);
    } else {
        printk("No data available\n");
    }
}

-2) 中断上下文使用

cpp 复制代码
void isr_handler(const struct device *dev, void *user_data)
{
    // 在ISR中安全检查FIFO状态
    if (!k_fifo_is_empty(&isr_fifo)) {
        // 可以安全地从ISR中获取数据(非阻塞)
        struct isr_event *evt = k_fifo_get(&isr_fifo, K_NO_WAIT);
        handle_isr_event(evt);
    }
}

-3) 多线程协调

cpp 复制代码
void worker_thread(void)
{
    while (1) {
        // 先非阻塞检查
        if (k_fifo_is_empty(&work_queue)) {
            // 无工作时休眠
            k_sleep(K_MSEC(100));
            continue;
        }
        
        // 有工作则获取处理
        struct work_item *item = k_fifo_get(&work_queue, K_NO_WAIT);
        process_work_item(item);
        k_free(item);
    }
}
  1. 关键注意事项

-1) 竞态条件

cpp 复制代码
// 错误用法:检查和使用之间存在时间间隙
if (!k_fifo_is_empty(&fifo)) {
    // 在这期间可能有其他线程取走数据
    item = k_fifo_get(&fifo, K_NO_WAIT);  // 可能得到NULL
}

// 正确用法:直接使用k_fifo_get的超时机制
item = k_fifo_get(&fifo, K_NO_WAIT);
if (item != NULL) {
    // 安全处理
}

-2) 性能考虑:不应作为主要的数据获取机制

cpp 复制代码
// 优化:避免不必要的阻塞
if (!k_fifo_is_empty(&fifo)) {
    item = k_fifo_get(&fifo, K_NO_WAIT);
    // 处理item...
}

-3) k_fifo_get 的关系

函数 特点 适用场景
k_fifo_is_empty 仅检查状态,不修改FIFO 需要预先知道状态的场景
k_fifo_get 获取数据并修改FIFO状态 实际消费数据的场景
  1. 特性

k_fifo_is_empty 作为 Zephyr FIFO 机制的辅助函数,虽然简单但非常实用。它最适合用于状态监控、性能优化和资源管理场景,而不应作为数据消费的主要机制。正确使用时可以构建出更高效和响应性更好的系统,但需要注意避免常见的竞态条件陷阱。

2 应用验证

2.1 UART中使用FIFO范例

Step-1: 定义FIFO

cpp 复制代码
 /* UART payload buffer element size. */
 #define UART_BUF_SIZE 20

 struct uart_data_t {
     void *fifo_reserved;
     uint8_t  data[UART_BUF_SIZE];
     uint16_t len;
 };
 
 static K_FIFO_DEFINE(fifo_uart_tx_data);
 static K_FIFO_DEFINE(fifo_uart_rx_data);

Step-2: 使用k_fifo_put准备数据

cpp 复制代码
  struct uart_data_t *tx = k_malloc(sizeof(*tx));
      

  err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
  if (err) 
  {
       k_fifo_put(&fifo_uart_tx_data, tx);
  }

Step-3: 使用 k_fifo_get消费数据

cpp 复制代码
 buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
 if (!buf) {
       return;
 }
 
 if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
       LOG_WRN("Failed to send data over UART");
 }

2.2 生产-消费类型范例

3 注意事项

3.1 内存管理

cpp 复制代码
// 正确:确保数据项在消费前有效
struct data_item *item = k_malloc(sizeof(*item));
k_fifo_put(fifo, item);

// 错误:栈变量在离开作用域后无效
struct data_item item;
k_fifo_put(fifo, &item);  // 严重错误!

3.2 线程安全边界

  • FIFO操作本身是线程安全的

  • 但数据内容的安全性需要开发者自己保证

示例安全模式:

cpp 复制代码
// 生产者
struct data *item = k_malloc(sizeof(*item));
item->value = compute_value();  // 在放入FIFO前完成所有数据准备
k_fifo_put(fifo, item);        // 之后不再修改item

// 消费者
struct data *item = k_fifo_get(fifo, K_FOREVER);
use_value(item->value);        // 安全使用数据
k_free(item);
相关推荐
小莞尔39 分钟前
【51单片机】【protues仿真】基于51单片机四层电梯系统
单片机·嵌入式硬件
F133168929571 小时前
WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
stm32·单片机·嵌入式硬件·51单片机·硬件工程·pcb工艺
易享电子3 小时前
基于单片机电器断路器保护器系统Proteus仿真(含全部资料)
单片机·嵌入式硬件·fpga开发·51单片机·proteus
爱倒腾的老唐5 小时前
01、如何学习单片机
单片机·嵌入式硬件·学习
点灯小铭5 小时前
基于单片机的夹具压力控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计
雾削木11 小时前
stm32解锁芯片
javascript·stm32·单片机·嵌入式硬件·gitee
璞致电子13 小时前
fpga开发板ZYNQ 璞致 PZ7010/7020 邮票孔核心板简介-ZYNQ7000系列小系统学习板
linux·嵌入式硬件·学习·fpga开发·fpga·fpga开发板·xilinx开发板
三佛科技-1341638421213 小时前
手持小风扇MCU方案,智能风扇方案设计开发
单片机·嵌入式硬件
btzhy13 小时前
STM32单片机:基本定时器应用:PWM 生成(STM32L4xx)
stm32·单片机·嵌入式硬件·基本定时器应用:pwm生成
guangshui51614 小时前
18006.STM32通过SPI读取LAN9253数据
stm32·单片机·嵌入式硬件