第一部分 Zephyr (西风)操作系统

一、Zephyr 操作系统核心特点

  1. 高度模块化与可配置性:通过 Kconfig 系统(类似于 Linux 内核的配置方式)和设备树(Devicetree),开发者可以精细裁剪功能,构建出极小或功能丰富的系统。

  2. 多架构支持:支持 ARM (Cortex-M/A/R)、x86、RISC-V、ARC、Xtensa 等多种架构,并提供了统一的 API 接口。

  3. 强大的设备驱动模型 (Device Driver Model):基于设备树的驱动框架,实现了驱动与硬件的解耦,增加了代码的可移植性和复用性。

  4. 内置实时内核:提供了抢占式多任务调度、线程同步(互斥锁、信号量、消息队列)和中断管理。

  5. 内存保护与安全

    • 用户空间:支持将线程分为用户模式和内核模式,防止用户线程破坏系统核心。

    • MPU/MMU 支持:对支持 MPU 的 Cortex-M 或 MMU 的 Cortex-A/R 处理器提供硬件内存保护。

  6. 网络协议栈:集成了 Zephyr 原生 TCP/IP 栈 (Z-NET),并支持蓝牙、802.15.4、LoRaWAN、以太网等多种有线/无线通信协议。

  7. 可移植中间件:支持大量中间件,如 LWM2M、MQTT、CoAP、USB 堆栈、文件系统(FatFS, LittleFS)和加密子系统(Crypto API)。

  8. 调试与测试:内置了丰富的调试工具(ztest 测试框架、tracefatal error 处理)和仿真平台支持(如 QEMU)。


二、文件目录全文件树形功能分析

Zephyr 的源码根目录结构清晰,遵循“架构-板级-驱动-子系统”的分类。以下以 Zephyr v3.5.0 为例进行分析。

zephyr/
├── arch/                  <-- 架构相关代码
│   ├── arm/              <-- ARM 架构 (Cortex-M/R/A)
│   ├── riscv/            <-- RISC-V 架构
│   ├── x86/              <-- x86 架构
│   ├── arc/              <-- ARC 架构
│   └── include/          <-- 架构通用头文件
├── boards/                <-- 开发板定义与配置
│   ├── arm/              <-- 基于 ARM 的开发板
│   ├── riscv/            <-- 基于 RISC-V 的开发板
│   ├── x86/              <-- 基于 x86 的开发板
│   └── board.cmake       <-- 板级构建脚本
├── soc/                   <-- SoC (System-on-Chip) 具体实现
│   ├── arm/              <-- ARM SoC (如 STM32, nRF, IMX)
│   └── riscv/            <-- RISC-V SoC (如 SiFive, Andes)
├── include/               <-- 公共 API 头文件
│   ├── zephyr/           <-- Zephyr 核心 API
│   ├── kernel.h          <-- 内核 API
│   ├── device.h          <-- 设备模型 API
│   └── sys/              <-- 通用系统接口 (如 byteorder, time)
├── kernel/                <-- 内核核心代码
│   ├── arch.c            <-- 架构相关内核实现
│   ├── thread.c          <-- 线程管理
│   ├── timer.c           <-- 软件定时器
│   ├── mutex.c           <-- 互斥锁
│   └── semaphore.c       <-- 信号量
├── drivers/               <-- 设备驱动
│   ├── gpio/             <-- GPIO 驱动
│   ├── uart/             <-- UART 驱动
│   ├── spi/              <-- SPI 驱动
│   ├── i2c/              <-- I2C 驱动
│   ├── flash/            <-- Flash 存储驱动
│   ├── sensor/           <-- 传感器驱动
│   ├── crypto/           <-- 加密引擎驱动
│   ├── net/              <-- 网络接口驱动
│   └── usb/              <-- USB 设备驱动
├── subsys/                <-- 子系统 (网络、USB、文件系统等)
│   ├── net/              <-- Zephyr 原生 TCP/IP 栈 (Z-NET)
│   ├── usb/              <-- USB 设备/主机栈
│   ├── fs/               <-- 文件系统 (FatFS, LittleFS)
│   ├── bluetooth/        <-- 蓝牙协议栈
│   ├── power/            <-- 电源管理
│   └── logging/          <-- 日志系统
├── lib/                   <-- 标准库与中间件
│   ├── libc/             <-- 标准 C 库实现 (Newlib, Minimal C)
│   ├── posix/            <-- POSIX 兼容层
│   └── crypto/           <-- 加密算法库 (如 mbedTLS 封装)
├── tests/                 <-- 测试用例
│   ├── unit/             <-- 单元测试
│   ├── integration/      <-- 集成测试
│   └── kernel/           <-- 内核功能测试
├── samples/               <-- 示例应用程序
│   ├── hello_world/      <-- 最简单的打印程序
│   ├── blinky/           <-- LED 闪烁示例
│   ├── thread_sync/      <-- 线程同步示例
│   ├── usb/              <-- USB 示例
│   └── net/              <-- 网络示例
├── scripts/               <-- 构建脚本与工具
│   ├── west/             <-- Zephyr 构建工具 (West)
│   ├── zephyr_module.py  <-- 模块加载
│   └── dts/              <-- 设备树处理脚本
├── dts/                   <-- 设备树源文件
│   ├── bindings/         <-- 设备树绑定定义 (YAML)
│   ├── common/           <-- 通用设备树定义
│   └── arm/              <-- ARM 架构设备树
├── cmake/                 <-- CMake 构建系统
├── doc/                   <-- 文档 (Sphinx)
└── zephyr-env.cmake      <-- 环境配置脚本

关键目录分析

  • arch/:包含所有支持架构的底层实现,如中断管理、上下文切换、内存布局等。

  • boards/:开发板顶层配置。它通过 board.cmake 和 Kconfig 配置具体硬件,并引用 soc/drivers/ 中的组件。

  • soc/:芯片级的硬件抽象。它实现了特定 SoC 的特殊功能,如电源管理、DMA 控制器配置等。

  • drivers/:所有外设驱动,驱动代码通过设备树实例化。

  • subsys/:上层功能模块,如网络栈、蓝牙、日志、文件系统等。这些子系统不直接操作硬件,而是使用 drivers/ 提供的统一接口。

  • tests/samples/:学习和验证系统的宝库。tests/ 用于功能验证,samples/ 用于原型开发。


三、主要函数树形分析

Zephyr 的启动流程和核心 API 设计非常清晰。以下是核心函数调用链的树形分析。

3.1 系统启动与内核初始化
系统复位 (Reset Vector)
  └─> _start (arch/arm/core/cortex_m/reset.S)
      └─> z_start()
          └─> z_cstart()  (kernel/init.c)
              ├─> early_init()              <-- 早期硬件初始化 (中断控制器)
              ├─> kernel_init()             <-- 内核对象初始化 (线程、定时器)
              │   ├─> z_thread_init()       <-- 初始化主线程和空闲线程
              │   └─> z_sched_init()        <-- 调度器初始化
              ├─> device_init()             <-- 设备驱动初始化 (通过设备树)
              │   ├─> z_device_state_init() <-- 设备状态初始化
              │   └─> driver initialization (各驱动 init 函数)
              ├─> init_app()                <-- 应用程序入口
              │   └─> main()                <-- 用户自定义的 main 函数
              └─> z_idle_thread()           <-- 空闲线程循环
3.2 线程管理与调度
z_thread_new() (kernel/thread.c)
  └─> z_thread_alloc()       <-- 分配线程控制块 (TCB) 和栈空间
  └─> z_thread_init()        <-- 初始化线程上下文 (PC, SP, LR)
  └─> z_ready_thread()       <-- 将线程放入就绪队列
      └─> z_sched_resched()  <-- 触发调度
​
调度器核心 (kernel/sched.c)
  └─> z_sched()              <-- 主调度函数
      ├─> z_sched_choose()   <-- 选择下一个最高优先级线程
      └─> context_switch()   <-- 执行上下文切换 (汇编)
3.3 设备驱动模型
device_get_binding(const char *name) (device.c)
  └─> device_find(name)
      └─> z_device_next()    <-- 遍历设备列表
​
设备驱动初始化 (通过宏)
  ├─> DEVICE_DT_DEFINE(dt_id, init_fn, ...)  <-- 定义驱动实例
  │   └─> z_device_register(init_fn, ...)    <-- 注册驱动
  │       └─> device_init()                  <-- 在启动时调用 init_fn
  └─> #include <device.h>  <-- 使用统一 API (如 `device_is_ready()`)
3.4 中断处理
中断向量表 (arch/arm/core/cortex_m/vector_table.c)
  └─> _isr_wrapper (汇编)    <-- 中断入口
      └─> z_isr_install()    <-- 中断服务函数注册
          └─> z_isr_entry()  <-- 执行具体的中断处理函数 (IRQ Handler)
3.5 线程同步 (互斥锁)
k_mutex_init(&mutex) (mutex.c)
  └─> z_mutex_init()
​
k_mutex_lock(&mutex, timeout) 
  ├─> if (mutex is free) -> lock, return
  └─> else -> z_pend_current_thread(&mutex.wait_q, timeout)  <-- 挂起当前线程
​
k_mutex_unlock(&mutex)
  └─> z_unpend_thread(&mutex.wait_q)  <-- 唤醒等待队列中最优线程
      └─> z_sched_resched()
3.6 加密子系统 (Crypto API)
crypto_begin_session() (crypto_api.c)
  └─> crypto_begin_session_dev(dev) <-- 从设备驱动获取会话
  └─> crypto_process_op()    <-- 执行加密操作 (AES, SHA, RSA)
      └─> device->driver_api->crypto_op()  <-- 调用具体加密引擎驱动

总结

  1. 模块化:通过 Kconfig 和 CMake,Zephyr 实现了高度的模块化和可裁剪性,为不同资源限制的设备提供了灵活的构建方案。

  2. 标准接口:内核、驱动、子系统通过统一的 API 进行抽象,极大地降低了跨平台移植的难度。

  3. 功能丰富:Zephyr 不仅是一个 RTOS,还提供了完整的网络、蓝牙、USB 和文件系统解决方案,是一个功能完备的物联网操作系统平台。

  4. 工具链完善:West、DeviceTree、Kconfig 等工具链确保了开发流程的标准化和高效性。

第二部分 Zephyr 官方示例:blinky (LED 闪烁)

1. 文件结构

samples/basic/blinky/
├── src/
│   └── main.c               # 主程序源码
├── boards/                  # 板级覆盖配置(可选)
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr blinky 示例程序
 *
 * 该程序演示如何通过 Zephyr 设备驱动模型(Device Driver Model)
 * 控制 GPIO 输出引脚,实现 LED 周期性闪烁。
 *
 * 依赖的硬件资源:
 * - 一个 GPIO 输出引脚(通过设备树指定)
 * - 系统定时器(用于延时)
 */
​
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
​
/**
 * @def LED0_NODE
 * @brief 从设备树中获取 LED0 的节点标识符
 *
 * 通过 devicetree 宏 `DT_ALIAS` 获取节点,该节点通常由
 * 板级设备树文件(.dts)定义。如果未定义,将引发编译错误。
 */
#define LED0_NODE DT_ALIAS(led0)
​
/**
 * @def STACK_SIZE
 * @brief 线程栈大小(字节)
 *
 * 本示例创建一个独立线程来控制 LED 闪烁,此处定义其栈大小。
 */
#define STACK_SIZE 1024
​
/**
 * @def SLEEP_TIME_MS
 * @brief 闪烁间隔时间(毫秒)
 *
 * LED 亮灭的持续时间为 1000 毫秒(1 秒)。
 */
#define SLEEP_TIME_MS 1000
​
/**
 * @brief 线程主函数
 *
 * 该函数在系统启动后由内核调度执行。其核心逻辑为:
 * 1. 获取 LED 设备的 GPIO 引脚句柄。
 * 2. 将引脚配置为输出模式。
 * 3. 进入无限循环:点亮 LED -> 延时 -> 熄灭 LED -> 延时。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void blink_thread(void *arg1, void *arg2, void *arg3)
{
    const struct device *dev;
    int ret;
​
    /* 1. 获取 GPIO 设备实例 */
    dev = DEVICE_DT_GET(DT_GPIO_CTLR(LED0_NODE, gpios));
    if (!device_is_ready(dev)) {
        printk("Error: Device %s is not ready\n", dev->name);
        return;
    }
​
    /* 2. 获取 GPIO 引脚编号 */
    gpio_pin_t pin = DT_GPIO_PIN(LED0_NODE, gpios);
    gpio_flags_t flags = DT_GPIO_FLAGS(LED0_NODE, gpios);
​
    /* 3. 配置 GPIO 引脚为输出,并初始化为低电平(熄灭) */
    ret = gpio_pin_configure(dev, pin, GPIO_OUTPUT_INACTIVE | flags);
    if (ret < 0) {
        printk("Error %d: failed to configure pin %d\n", ret, pin);
        return;
    }
​
    /* 4. 主循环:LED 闪烁 */
    while (1) {
        /* 4.1 点亮 LED(设置引脚为高电平) */
        gpio_pin_set(dev, pin, 1);
        printk("LED on\n");
        k_msleep(SLEEP_TIME_MS);  /* 延时 1000 毫秒 */
​
        /* 4.2 熄灭 LED(设置引脚为低电平) */
        gpio_pin_set(dev, pin, 0);
        printk("LED off\n");
        k_msleep(SLEEP_TIME_MS);  /* 延时 1000 毫秒 */
    }
}
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。系统初始化完成后会调用该函数。
 *
 * 在此示例中,`main()` 负责创建并启动一个独立线程来执行 LED 闪烁逻辑,
 * 而主线程本身可以执行其他任务(此处让主线程退出)。
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    printk("Zephyr Blinky Example\n");
​
    /* 创建并启动闪烁线程 */
    k_thread_create(&blink_thread_data, blink_stack,
                    STACK_SIZE, blink_thread,
                    NULL, NULL, NULL,
                    5, 0, K_NO_WAIT);
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 执行设备驱动初始化(`device_init()`)
    │      - 注册并初始化 GPIO 设备驱动
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印 "Zephyr Blinky Example"
    │
    │ 2. 创建并启动 `blink_thread` 线程
    │    - 指定栈空间 (stack_size = 1024)
    │    - 指定优先级 (priority = 5)
    │    - 线程函数为 `blink_thread`
    │
    │ 3. 主线程退出(结束)
    │
    ▼
[blink_thread 线程]
    │
    │ 1. 获取 GPIO 设备实例
    │    - 通过设备树节点 LED0_NODE 获取控制器设备 (`dev`)
    │    - 检查设备是否就绪 (`device_is_ready()`)
    │        └─ 若未就绪,打印错误并返回
    │
    │ 2. 获取 GPIO 引脚编号和标志位
    │    - `pin = DT_GPIO_PIN(LED0_NODE, gpios)`
    │    - `flags = DT_GPIO_FLAGS(LED0_NODE, gpios)`
    │
    │ 3. 配置 GPIO 引脚为输出,初始状态为低电平(熄灭)
    │    - `gpio_pin_configure(dev, pin, GPIO_OUTPUT_INACTIVE | flags)`
    │        └─ 若配置失败,打印错误并返回
    │
    │ 4. 进入无限循环
    │    │
    │    ├── 4.1 点亮 LED
    │    │    │   - `gpio_pin_set(dev, pin, 1)`
    │    │    │   - 打印 "LED on"
    │    │    │   - 调用 `k_msleep(1000)` 等待 1 秒
    │    │    │
    │    └── 4.2 熄灭 LED
    │            - `gpio_pin_set(dev, pin, 0)`
    │            - 打印 "LED off"
    │            - 调用 `k_msleep(1000)` 等待 1 秒
    │
    ▼
[循环继续...]

4. 关键 API 注解

API 功能 注解
DEVICE_DT_GET(node_id) 从设备树节点获取设备句柄 /**
  • @def DEVICE_DT_GET(node_id)

  • @brief 获取设备树节点对应的设备结构体指针

  • @param node_id 设备树节点标识符

  • @return 指向该设备驱动实例的指针 */ | | device_is_ready(dev) | 检查设备驱动是否已初始化就绪 | /**

  • @fn bool device_is_ready(const struct device *dev)

  • @brief 检查设备是否已就绪

  • @param dev 设备指针

  • @return true 就绪,false 未就绪 */ | | gpio_pin_configure(dev, pin, flags) | 配置 GPIO 引脚模式 | /**

  • @fn int gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)

  • @brief 配置单个 GPIO 引脚

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @param flags 配置标志(如 GPIO_OUTPUT、GPIO_INPUT)

  • @return 0 成功,负值错误码 */ | | gpio_pin_set(dev, pin, value) | 设置 GPIO 引脚电平 | /**

  • @fn int gpio_pin_set(const struct device *dev, gpio_pin_t pin, int value)

  • @brief 设置 GPIO 引脚电平

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @param value 0(低电平)或 1(高电平)

  • @return 0 成功,负值错误码 */ | | k_msleep(ms) | 线程延时(毫秒级) | /**

  • @fn void k_msleep(int32_t ms)

  • @brief 当前线程延时指定的毫秒数

  • @param ms 延时毫秒数(正数) */ |

5. 项目配置(prj.conf)

# 启用 GPIO 驱动
CONFIG_GPIO=y
# 启用标准输出
CONFIG_PRINTK=y
# 启用设备树
CONFIG_DT_DEVICE_PTR=y

6. 总结

  • 核心流程:设备获取 → 引脚配置 → 无限循环闪烁。

  • Zephyr 特色:使用设备树和 Kconfig 实现硬件抽象,驱动 API 统一。

  • 调试方式:通过 printk 输出日志,通过 k_msleep 控制时序。

  • 扩展方向:可替换为 PWM 控制亮度,或使用多个 LED 实现流水灯。

第三部分 Zephyr 官方示例:button(按键中断检测)

1. 文件结构

samples/basic/button/
├── src/
│   └── main.c               # 主程序源码
├── boards/                  # 板级覆盖配置(可选)
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr button 示例程序
 *
 * 该程序演示如何通过 Zephyr 设备驱动模型配置 GPIO 输入引脚,
 * 并注册中断回调函数处理按键事件。
 *
 * 依赖的硬件资源:
 * - 一个 GPIO 输入引脚(通过设备树指定,通常对应板载按钮)
 * - 一个 GPIO 输出引脚(用于控制 LED,可选)
 * - 系统中断控制器
 */
​
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
​
/**
 * @def LED0_NODE
 * @brief 从设备树中获取 LED0 的节点标识符
 *
 * 用于指示按键状态(按键按下时 LED 亮/灭)。
 */
#define LED0_NODE DT_ALIAS(led0)
​
/**
 * @def BUTTON0_NODE
 * @brief 从设备树中获取按钮的节点标识符
 *
 * 通过 devicetree 宏 `DT_ALIAS` 获取,通常在板级 .dts 中定义,
 * 如 `button0` 或 `btn0`。
 */
#define BUTTON0_NODE DT_ALIAS(button0)
​
/**
 * @def STACK_SIZE
 * @brief 线程栈大小(字节)
 *
 * 本示例创建一个独立线程来处理按键事件后的逻辑(如打印消息)。
 */
#define STACK_SIZE 1024
​
/**
 * @def SLEEP_TIME_MS
 * @brief 主循环延时(毫秒)
 */
#define SLEEP_TIME_MS 1000
​
/**
 * @struct button_state
 * @brief 按键状态结构体
 *
 * 用于存储按键引脚状态,并实现简单的消抖逻辑。
 */
struct button_state {
    const struct device *dev;   /**< GPIO 设备指针 */
    gpio_pin_t pin;             /**< 引脚编号 */
    gpio_flags_t flags;         /**< 引脚配置标志 */
    bool pressed;               /**< 当前是否按下 */
    bool last_pressed;          /**< 上次按下状态(用于消抖) */
    uint32_t press_count;       /**< 按下计数 */
};
​
/**
 * @brief 按键中断回调函数
 *
 * 该函数在按键事件触发时被系统调用,运行在中断上下文(ISR)中。
 * 因此,函数内部应尽量简短,避免阻塞操作。
 *
 * @param dev       GPIO 设备指针
 * @param pin       触发中断的引脚编号
 * @param user_data 用户数据(指向 struct button_state)
 */
void button_isr(const struct device *dev, gpio_pin_t pin, void *user_data)
{
    struct button_state *state = (struct button_state *)user_data;
    int val;
​
    /* 1. 读取当前引脚电平 */
    val = gpio_pin_get(dev, pin);
    if (val < 0) {
        printk("Error reading pin %d\n", pin);
        return;
    }
​
    /* 2. 更新状态 */
    state->last_pressed = state->pressed;
    state->pressed = (val == 1);  /* 假设高电平表示按下 */
​
    /* 3. 简单消抖逻辑:仅在状态变化时记录 */
    if (state->pressed != state->last_pressed) {
        if (state->pressed) {
            state->press_count++;
            printk("Button pressed! count = %d\n", state->press_count);
        } else {
            printk("Button released\n");
        }
    }
}
​
/**
 * @brief 按键处理线程
 *
 * 该线程负责监控按键状态,并根据按键事件执行相应的业务逻辑。
 * 在此示例中,线程用于演示如何从 ISR 向线程传递信息。
 *
 * @param arg1 线程参数(指向 struct button_state)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void button_thread(void *arg1, void *arg2, void *arg3)
{
    struct button_state *state = (struct button_state *)arg1;
    uint32_t last_count = 0;
​
    while (1) {
        /* 检查按键计数是否变化 */
        if (state->press_count != last_count) {
            /* 处理按键事件:例如点亮 LED */
            const struct device *led_dev = DEVICE_DT_GET(DT_GPIO_CTLR(LED0_NODE, gpios));
            if (device_is_ready(led_dev)) {
                gpio_pin_t led_pin = DT_GPIO_PIN(LED0_NODE, gpios);
                /* 按键计数为奇数时点亮 LED,偶数时熄灭 */
                gpio_pin_set(led_dev, led_pin, state->press_count % 2);
            }
            last_count = state->press_count;
        }
​
        /* 等待下一个周期 */
        k_msleep(SLEEP_TIME_MS);
    }
}
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责:
 * 1. 初始化 GPIO 输入引脚(按键)
 * 2. 注册中断回调函数
 * 3. 创建处理线程
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    const struct device *dev;
    struct button_state *state;
    int ret;
​
    printk("Zephyr Button Example\n");
​
    /* 1. 获取按键设备实例 */
    dev = DEVICE_DT_GET(DT_GPIO_CTLR(BUTTON0_NODE, gpios));
    if (!device_is_ready(dev)) {
        printk("Error: Device %s is not ready\n", dev->name);
        return;
    }
​
    /* 2. 获取引脚编号和标志位 */
    gpio_pin_t pin = DT_GPIO_PIN(BUTTON0_NODE, gpios);
    gpio_flags_t flags = DT_GPIO_FLAGS(BUTTON0_NODE, gpios);
​
    /* 3. 分配按键状态结构体 */
    state = (struct button_state *)k_malloc(sizeof(struct button_state));
    if (!state) {
        printk("Error: Failed to allocate button state\n");
        return;
    }
    state->dev = dev;
    state->pin = pin;
    state->flags = flags;
    state->pressed = false;
    state->last_pressed = false;
    state->press_count = 0;
​
    /* 4. 配置 GPIO 引脚为输入模式 */
    ret = gpio_pin_configure(dev, pin, GPIO_INPUT | flags);
    if (ret < 0) {
        printk("Error %d: failed to configure pin %d\n", ret, pin);
        goto cleanup;
    }
​
    /* 5. 注册中断回调函数 */
    ret = gpio_pin_interrupt_configure(dev, pin, GPIO_INT_EDGE_BOTH);
    if (ret < 0) {
        printk("Error %d: failed to configure interrupt on pin %d\n", ret, pin);
        goto cleanup;
    }
​
    ret = gpio_add_callback(dev, (gpio_callback_t)button_isr, state);
    if (ret < 0) {
        printk("Error %d: failed to add callback\n", ret);
        goto cleanup;
    }
​
    /* 6. 创建处理线程 */
    k_thread_create(&button_thread_data, button_stack,
                    STACK_SIZE, button_thread,
                    state, NULL, NULL,
                    5, 0, K_NO_WAIT);
​
    printk("Button example ready. Press the button!\n");
    return;
​
cleanup:
    k_free(state);
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 执行设备驱动初始化(`device_init()`)
    │      - 注册并初始化 GPIO 设备驱动
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印 "Zephyr Button Example"
    │
    │ 2. 获取按键 GPIO 设备实例
    │    - 通过设备树节点 BUTTON0_NODE 获取控制器设备 (`dev`)
    │    - 检查设备是否就绪 (`device_is_ready()`)
    │        └─ 若未就绪,打印错误并返回
    │
    │ 3. 获取 GPIO 引脚编号和标志位
    │    - `pin = DT_GPIO_PIN(BUTTON0_NODE, gpios)`
    │    - `flags = DT_GPIO_FLAGS(BUTTON0_NODE, gpios)`
    │
    │ 4. 分配按键状态结构体 (`struct button_state`)
    │    - 初始化状态字段
    │
    │ 5. 配置 GPIO 引脚为输入模式
    │    - `gpio_pin_configure(dev, pin, GPIO_INPUT | flags)`
    │        └─ 若配置失败,跳转到 cleanup 释放内存
    │
    │ 6. 配置中断触发方式(双边沿触发)
    │    - `gpio_pin_interrupt_configure(dev, pin, GPIO_INT_EDGE_BOTH)`
    │        └─ 若配置失败,跳转到 cleanup
    │
    │ 7. 注册中断回调函数
    │    - `gpio_add_callback(dev, (gpio_callback_t)button_isr, state)`
    │        └─ 注册后,按键事件会触发 `button_isr()`
    │
    │ 8. 创建处理线程 (`button_thread`)
    │    - 指定栈空间 (stack_size = 1024)
    │    - 指定优先级 (priority = 5)
    │    - 传入状态结构体指针
    │
    │ 9. 主线程退出(结束)
    │    - 打印 "Button example ready. Press the button!"
    │
    ▼
[中断触发流程]
    │
    │ 硬件中断触发(按键按下或松开)
    │
    ▼
[button_isr() 中断处理函数]
    │
    │ 1. 读取当前引脚电平
    │    - `val = gpio_pin_get(dev, pin)`
    │        └─ 若读取失败,打印错误并返回
    │
    │ 2. 更新状态结构体
    │    - 记录上次状态 (`last_pressed = pressed`)
    │    - 更新当前状态 (`pressed = (val == 1)`)
    │
    │ 3. 状态变化检测(简单消抖)
    │    - 若 `pressed != last_pressed`,表示状态发生变化
    │        ├─ 若 `pressed == true`: 按键按下
    │        │    - 计数 `press_count++`
    │        │    - 打印 "Button pressed! count = X"
    │        └─ 若 `pressed == false`: 按键松开
    │             - 打印 "Button released"
    │
    ▼
[button_thread 处理线程(周期执行)]
    │
    │ 1. 检查按键计数是否变化
    │    - `if (state->press_count != last_count)`
    │        └─ 若变化,执行处理逻辑
    │            ├─ 获取 LED 设备实例
    │            ├─ 检查设备就绪状态
    │            └─ 根据计数奇偶性设置 LED 状态
    │                - 奇数:点亮 LED
    │                - 偶数:熄灭 LED
    │
    │ 2. 更新上次计数
    │    - `last_count = state->press_count`
    │
    │ 3. 等待下一个周期
    │    - `k_msleep(SLEEP_TIME_MS)`
    │
    ▼
[循环继续...]

4. 关键 API 注解

API 功能 注解
gpio_pin_configure(dev, pin, flags) 配置 GPIO 引脚模式 /**
  • @fn int gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)

  • @brief 配置单个 GPIO 引脚

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @param flags 配置标志(如 GPIO_INPUT, GPIO_OUTPUT, GPIO_PULL_UP)

  • @return 0 成功,负值错误码 */ | | gpio_pin_interrupt_configure(dev, pin, flags) | 配置 GPIO 中断触发方式 | /**

  • @fn int gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)

  • @brief 配置 GPIO 引脚中断触发方式

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @param flags 中断触发标志(如 GPIO_INT_EDGE_RISING, GPIO_INT_EDGE_FALLING, GPIO_INT_EDGE_BOTH)

  • @return 0 成功,负值错误码 */ | | gpio_add_callback(dev, callback, user_data) | 注册中断回调函数 | /**

  • @fn int gpio_add_callback(const struct device *dev, gpio_callback_t callback, void *user_data)

  • @brief 注册 GPIO 中断回调函数

  • @param dev GPIO 设备指针

  • @param callback 回调函数指针

  • @param user_data 传递给回调的用户数据

  • @return 0 成功,负值错误码 */ | | gpio_pin_get(dev, pin) | 读取 GPIO 引脚电平 | /**

  • @fn int gpio_pin_get(const struct device *dev, gpio_pin_t pin)

  • @brief 读取 GPIO 引脚电平

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @return 0 低电平,1 高电平,负值错误码 */ | | gpio_pin_set(dev, pin, value) | 设置 GPIO 引脚电平 | /**

  • @fn int gpio_pin_set(const struct device *dev, gpio_pin_t pin, int value)

  • @brief 设置 GPIO 引脚电平

  • @param dev GPIO 设备指针

  • @param pin 引脚编号

  • @param value 0(低电平)或 1(高电平)

  • @return 0 成功,负值错误码 */ |

5. 项目配置(prj.conf)

# 启用 GPIO 驱动
CONFIG_GPIO=y
# 启用标准输出
CONFIG_PRINTK=y
# 启用设备树
CONFIG_DT_DEVICE_PTR=y
# 启用动态内存分配(用于 button_state 结构体)
CONFIG_MEM_SLAB=y
CONFIG_KERNEL_MEM_POOL=y
# 增加主线程栈大小(避免溢出)
CONFIG_MAIN_STACK_SIZE=2048

6. 板级覆盖配置(可选)

如果需要为特定开发板配置不同的按键引脚,可以在 boards/ 目录下创建覆盖文件:

boards/esp32_devkitc.conf

# ESP32 开发板使用 GPIO 2 作为 LED 输出
CONFIG_GPIO=y
# 按键引脚为 GPIO 0
# 设备树自动处理引脚配置

7. 总结

  • 核心流程:设备获取 → 中断配置 → 注册回调 → 线程处理。

  • Zephyr 特色

    • 中断上下文与线程上下文分离(ISR 只做最小处理,业务逻辑放在线程中)。

    • 通过 gpio_add_callback 实现回调注册,支持多个中断共享同一个设备。

    • 使用 k_msleep 实现线程周期调度,避免忙等待。

  • 调试方式:通过 printk 输出中断触发日志和按键计数,验证中断是否正确触发。

  • 扩展方向

    • 实现长按/短按检测(通过记录按键持续时间)。

    • 加入软件消抖(通过定时器中断或线程轮询)。

    • 使用信号量或消息队列代替全局变量,实现更安全的线程间通信。

这个示例展示了典型的中断驱动编程模式,是 Zephyr 开发中非常核心的编程范式。

第四部分 Zephyr 官方示例:thread_sync(线程同步)

1. 文件结构

samples/basic/thread_sync/
├── src/
│   └── main.c               # 主程序源码
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr 线程同步示例程序
 *
 * 该程序演示了 Zephyr 内核提供的多种线程同步机制:
 * - 信号量(Semaphore):用于线程间事件通知
 * - 互斥锁(Mutex):用于临界区保护
 * - 消息队列(Message Queue):用于线程间数据传递
 * - 事件(Event):用于位事件通知
 * - 条件变量(Condition Variable):用于等待特定条件满足
 *
 * 该程序创建了多个线程,演示这些同步机制的实际应用场景。
 */
​
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
​
/* ============================================================ */
/* 1. 信号量示例(Semaphore)                                   */
/* ============================================================ */
​
/**
 * @def SEMAPHORE_MAX_COUNT
 * @brief 信号量的最大计数
 */
#define SEMAPHORE_MAX_COUNT 5
​
/**
 * @def SEMAPHORE_INIT_COUNT
 * @brief 信号量的初始计数
 */
#define SEMAPHORE_INIT_COUNT 1
​
/**
 * @brief 信号量对象定义
 *
 * 使用 K_SEM_DEFINE 宏静态定义信号量,该宏在编译时初始化信号量。
 *
 * 信号量名称:sem 示例
 * 初始计数:1(表示有一个资源可用)
 * 最大计数:5(表示最多可累积 5 个资源)
 */
K_SEM_DEFINE(sem, SEMAPHORE_INIT_COUNT, SEMAPHORE_MAX_COUNT);
​
/**
 * @brief 信号量生产者线程
 *
 * 该线程模拟生产者,定期释放(give)信号量。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void sem_producer_thread(void *arg1, void *arg2, void *arg3)
{
    int count = 0;
​
    while (1) {
        k_sleep(K_SECONDS(1));  /* 每 1 秒生产一次 */
​
        /* 释放信号量:增加计数值 */
        k_sem_give(&sem);
        count++;
        printk("[Semaphore] Producer: give semaphore (count = %d)\n", count);
    }
}
​
/**
 * @brief 信号量消费者线程
 *
 * 该线程模拟消费者,等待信号量并使用(take)资源。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void sem_consumer_thread(void *arg1, void *arg2, void *arg3)
{
    int count = 0;
​
    while (1) {
        /* 获取信号量:若计数值为 0,线程会挂起等待 */
        k_sem_take(&sem, K_FOREVER);
        count++;
        printk("[Semaphore] Consumer: take semaphore (count = %d)\n", count);
​
        /* 处理资源(模拟) */
        k_sleep(K_MSEC(500));
    }
}
​
/* ============================================================ */
/* 2. 互斥锁示例(Mutex)                                       */
/* ============================================================ */
​
/**
 * @brief 互斥锁对象定义
 *
 * 使用 K_MUTEX_DEFINE 宏静态定义互斥锁。
 *
 * 互斥锁名称:mutex
 * 用于保护共享资源(如全局计数器)
 */
K_MUTEX_DEFINE(mutex);
​
/**
 * @brief 共享计数器(由互斥锁保护)
 */
int shared_counter = 0;
​
/**
 * @brief 互斥锁线程 1
 *
 * 该线程使用互斥锁保护共享计数器的递增操作。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void mutex_thread1(void *arg1, void *arg2, void *arg3)
{
    while (1) {
        /* 获取互斥锁(若被其他线程持有,当前线程会挂起等待) */
        k_mutex_lock(&mutex, K_FOREVER);
​
        /* 临界区:操作共享资源 */
        shared_counter++;
        printk("[Mutex] Thread1: shared_counter = %d\n", shared_counter);
​
        /* 释放互斥锁 */
        k_mutex_unlock(&mutex);
​
        k_sleep(K_MSEC(500));
    }
}
​
/**
 * @brief 互斥锁线程 2
 *
 * 该线程也使用互斥锁保护共享计数器的递增操作。
 * 与 thread1 形成竞争关系,测试互斥锁的互斥性。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void mutex_thread2(void *arg1, void *arg2, void *arg3)
{
    while (1) {
        /* 获取互斥锁 */
        k_mutex_lock(&mutex, K_FOREVER);
​
        /* 临界区:操作共享资源 */
        shared_counter++;
        printk("[Mutex] Thread2: shared_counter = %d\n", shared_counter);
​
        /* 释放互斥锁 */
        k_mutex_unlock(&mutex);
​
        k_sleep(K_MSEC(700));
    }
}
​
/* ============================================================ */
/* 3. 消息队列示例(Message Queue)                             */
/* ============================================================ */
​
/**
 * @def MSG_QUEUE_SIZE
 * @brief 消息队列容量(消息数量)
 */
#define MSG_QUEUE_SIZE 10
​
/**
 * @def MSG_DATA_SIZE
 * @brief 每条消息的数据大小(字节)
 */
#define MSG_DATA_SIZE 32
​
/**
 * @brief 消息队列对象定义
 *
 * 使用 K_MSGQ_DEFINE 宏静态定义消息队列。
 *
 * 消息队列名称:msgq
 * 每条消息大小:32 字节
 * 队列容量:10 条消息
 * 对齐方式:4 字节对齐
 */
K_MSGQ_DEFINE(msgq, MSG_DATA_SIZE, MSG_QUEUE_SIZE, 4);
​
/**
 * @brief 消息结构体定义
 */
struct message {
    uint32_t id;
    uint32_t value;
    char text[24];
};
​
/**
 * @brief 消息生产者线程
 *
 * 该线程定期生成消息并发送到消息队列。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void msg_producer_thread(void *arg1, void *arg2, void *arg3)
{
    struct message msg;
    int id = 0;
​
    while (1) {
        /* 构造消息 */
        msg.id = id++;
        msg.value = id * 100;
        snprintf(msg.text, sizeof(msg.text), "Message #%d", msg.id);
​
        /* 发送消息到队列(若队列满,线程会挂起等待) */
        k_msgq_put(&msgq, &msg, K_FOREVER);
        printk("[MsgQueue] Producer: sent message id=%d, value=%d\n",
               msg.id, msg.value);
​
        k_sleep(K_SECONDS(1));
    }
}
​
/**
 * @brief 消息消费者线程
 *
 * 该线程从消息队列中接收消息并处理。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void msg_consumer_thread(void *arg1, void *arg2, void *arg3)
{
    struct message msg;
​
    while (1) {
        /* 从队列接收消息(若队列空,线程会挂起等待) */
        k_msgq_get(&msgq, &msg, K_FOREVER);
        printk("[MsgQueue] Consumer: received message id=%d, value=%d, text='%s'\n",
               msg.id, msg.value, msg.text);
​
        /* 处理消息(模拟) */
        k_sleep(K_MSEC(500));
    }
}
​
/* ============================================================ */
/* 4. 事件示例(Event)                                         */
/* ============================================================ */
​
/**
 * @def EVENT_BIT_0
 * @brief 事件位 0
 */
#define EVENT_BIT_0 (1 << 0)
​
/**
 * @def EVENT_BIT_1
 * @brief 事件位 1
 */
#define EVENT_BIT_1 (1 << 1)
​
/**
 * @brief 事件对象定义
 *
 * 使用 K_EVENT_DEFINE 宏静态定义事件对象。
 */
K_EVENT_DEFINE(event);
​
/**
 * @brief 事件发送者线程
 *
 * 该线程定期设置事件位。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void event_sender_thread(void *arg1, void *arg2, void *arg3)
{
    uint32_t events = 0;
​
    while (1) {
        /* 交替设置不同的事件位 */
        events = (events == EVENT_BIT_0) ? EVENT_BIT_1 : EVENT_BIT_0;
        k_event_post(&event, events);
        printk("[Event] Sender: posted event 0x%08x\n", events);
​
        k_sleep(K_SECONDS(2));
    }
}
​
/**
 * @brief 事件接收者线程
 *
 * 该线程等待特定事件位被设置。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void event_receiver_thread(void *arg1, void *arg2, void *arg3)
{
    uint32_t received;
​
    while (1) {
        /* 等待事件(同时等待多个事件位) */
        received = k_event_wait(&event, EVENT_BIT_0 | EVENT_BIT_1, false, K_FOREVER);
        printk("[Event] Receiver: received event 0x%08x\n", received);
​
        /* 处理事件 */
        if (received & EVENT_BIT_0) {
            printk("[Event] Receiver: processing BIT_0 event\n");
        }
        if (received & EVENT_BIT_1) {
            printk("[Event] Receiver: processing BIT_1 event\n");
        }
    }
}
​
/* ============================================================ */
/* 5. 条件变量示例(Condition Variable)                        */
/* ============================================================ */
​
/**
 * @brief 条件变量示例使用的共享状态
 */
struct cv_state {
    int data;               /**< 共享数据 */
    int ready;              /**< 数据就绪标志 */
};
​
/**
 * @brief 静态互斥锁(用于保护条件变量)
 */
K_MUTEX_DEFINE(cv_mutex);
​
/**
 * @brief 静态条件变量
 *
 * 使用 K_COND_DEFINE 宏静态定义条件变量。
 */
K_COND_DEFINE(cv);
​
/**
 * @brief 条件变量等待者线程
 *
 * 该线程等待条件变量被触发(数据就绪)。
 *
 * @param arg1 线程参数(指向 struct cv_state)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void cv_wait_thread(void *arg1, void *arg2, void *arg3)
{
    struct cv_state *state = (struct cv_state *)arg1;
​
    while (1) {
        /* 获取互斥锁 */
        k_mutex_lock(&cv_mutex, K_FOREVER);
​
        /* 等待条件变量(释放互斥锁,被唤醒后重新获得锁) */
        while (state->ready == 0) {
            k_cond_wait(&cv, &cv_mutex, K_FOREVER);
        }
​
        /* 处理数据 */
        printk("[CondVar] Waiter: data = %d\n", state->data);
        state->ready = 0;  /* 标记已处理 */
​
        /* 释放互斥锁 */
        k_mutex_unlock(&cv_mutex);
    }
}
​
/**
 * @brief 条件变量发送者线程
 *
 * 该线程设置数据并触发条件变量。
 *
 * @param arg1 线程参数(指向 struct cv_state)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void cv_signal_thread(void *arg1, void *arg2, void *arg3)
{
    struct cv_state *state = (struct cv_state *)arg1;
    int count = 0;
​
    while (1) {
        k_sleep(K_SECONDS(3));
​
        /* 获取互斥锁 */
        k_mutex_lock(&cv_mutex, K_FOREVER);
​
        /* 设置数据 */
        state->data = ++count;
        state->ready = 1;
        printk("[CondVar] Signal: data set to %d\n", state->data);
​
        /* 通知等待者(唤醒至少一个) */
        k_cond_signal(&cv);
​
        /* 释放互斥锁 */
        k_mutex_unlock(&cv_mutex);
    }
}
​
/* ============================================================ */
/* 线程栈与线程句柄定义                                         */
/* ============================================================ */
​
#define STACK_SIZE 1024
​
K_THREAD_STACK_DEFINE(sem_producer_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(sem_consumer_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(mutex_thread1_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(mutex_thread2_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(msg_producer_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(msg_consumer_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(event_sender_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(event_receiver_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(cv_wait_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(cv_signal_stack, STACK_SIZE);
​
/* 线程句柄(可选,用于外部控制) */
struct k_thread sem_producer_thread_data;
struct k_thread sem_consumer_thread_data;
struct k_thread mutex_thread1_data;
struct k_thread mutex_thread2_data;
struct k_thread msg_producer_thread_data;
struct k_thread msg_consumer_thread_data;
struct k_thread event_sender_thread_data;
struct k_thread event_receiver_thread_data;
struct k_thread cv_wait_thread_data;
struct k_thread cv_signal_thread_data;
​
/* ============================================================ */
/* 系统主入口                                                   */
/* ============================================================ */
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责创建所有演示线程。
 * 每个同步机制对应的线程对(生产者-消费者)被分别创建并启动。
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    printk("Zephyr Thread Synchronization Example\n");
    printk("========================================\n");
    printk("This example demonstrates:\n");
    printk("  - Semaphore (信号量)\n");
    printk("  - Mutex (互斥锁)\n");
    printk("  - Message Queue (消息队列)\n");
    printk("  - Event (事件)\n");
    printk("  - Condition Variable (条件变量)\n");
    printk("========================================\n");
​
    /* 1. 创建信号量示例线程 */
    k_thread_create(&sem_producer_thread_data, sem_producer_stack,
                    STACK_SIZE, sem_producer_thread,
                    NULL, NULL, NULL, 5, 0, K_NO_WAIT);
    k_thread_create(&sem_consumer_thread_data, sem_consumer_stack,
                    STACK_SIZE, sem_consumer_thread,
                    NULL, NULL, NULL, 4, 0, K_NO_WAIT);
​
    /* 2. 创建互斥锁示例线程 */
    k_thread_create(&mutex_thread1_data, mutex_thread1_stack,
                    STACK_SIZE, mutex_thread1,
                    NULL, NULL, NULL, 3, 0, K_NO_WAIT);
    k_thread_create(&mutex_thread2_data, mutex_thread2_stack,
                    STACK_SIZE, mutex_thread2,
                    NULL, NULL, NULL, 3, 0, K_NO_WAIT);
​
    /* 3. 创建消息队列示例线程 */
    k_thread_create(&msg_producer_thread_data, msg_producer_stack,
                    STACK_SIZE, msg_producer_thread,
                    NULL, NULL, NULL, 4, 0, K_NO_WAIT);
    k_thread_create(&msg_consumer_thread_data, msg_consumer_stack,
                    STACK_SIZE, msg_consumer_thread,
                    NULL, NULL, NULL, 3, 0, K_NO_WAIT);
​
    /* 4. 创建事件示例线程 */
    k_thread_create(&event_sender_thread_data, event_sender_stack,
                    STACK_SIZE, event_sender_thread,
                    NULL, NULL, NULL, 4, 0, K_NO_WAIT);
    k_thread_create(&event_receiver_thread_data, event_receiver_stack,
                    STACK_SIZE, event_receiver_thread,
                    NULL, NULL, NULL, 3, 0, K_NO_WAIT);
​
    /* 5. 创建条件变量示例线程 */
    static struct cv_state cv_state = {0};
    k_thread_create(&cv_wait_thread_data, cv_wait_stack,
                    STACK_SIZE, cv_wait_thread,
                    &cv_state, NULL, NULL, 3, 0, K_NO_WAIT);
    k_thread_create(&cv_signal_thread_data, cv_signal_stack,
                    STACK_SIZE, cv_signal_thread,
                    &cv_state, NULL, NULL, 4, 0, K_NO_WAIT);
​
    printk("All synchronization threads have been started.\n");
    printk("Check the console output for execution traces.\n");
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 执行设备驱动初始化(`device_init()`)
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印标题信息
    │
    │ 2. 创建信号量示例线程对
    │    ├─ 生产者线程 (`sem_producer_thread`)   优先级 5
    │    └─ 消费者线程 (`sem_consumer_thread`)   优先级 4
    │
    │ 3. 创建互斥锁示例线程对
    │    ├─ 线程1 (`mutex_thread1`)   优先级 3
    │    └─ 线程2 (`mutex_thread2`)   优先级 3
    │
    │ 4. 创建消息队列示例线程对
    │    ├─ 生产者线程 (`msg_producer_thread`)   优先级 4
    │    └─ 消费者线程 (`msg_consumer_thread`)   优先级 3
    │
    │ 5. 创建事件示例线程对
    │    ├─ 发送者线程 (`event_sender_thread`)   优先级 4
    │    └─ 接收者线程 (`event_receiver_thread`)   优先级 3
    │
    │ 6. 创建条件变量示例线程对
    │    ├─ 等待者线程 (`cv_wait_thread`)   优先级 3
    │    └─ 信号者线程 (`cv_signal_thread`)   优先级 4
    │
    │ 7. 打印启动完成信息
    │
    ▼
[线程并发执行]
​
+--------------------------------------------------+
| 信号量示例 (Semaphore)                            |
+--------------------------------------------------+
│
│ [生产者线程]            [消费者线程]
│                         │
│ 1. 休眠 1 秒           │ 1. 等待信号量
│    │                   │    (k_sem_take)
│    ▼                   │    │
│ 2. 释放信号量           │    ▼
│    (k_sem_give)        │ 2. 获取信号量成功
│    │                   │    │
│    ▼                   │    ▼
│ 3. 打印日志             │ 3. 处理资源 (500ms)
│    │                   │    │
│    └─────> (循环)      │    └─────> (循环)
│                         │
│ 信号量计数: 初始 1,生产者释放增加,消费者获取减少
│ 当计数为 0 时,消费者线程会挂起等待
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 互斥锁示例 (Mutex)                                |
+--------------------------------------------------+
│
│ [线程1]               [线程2]
│    │                     │
│ 1. 获取互斥锁         1. 获取互斥锁
│    (k_mutex_lock)        (k_mutex_lock)
│    │                     │
│    ▼                     ▼
│ 2. 进入临界区         2. 进入临界区
│    │                     │
│    ▼                     ▼
│ 3. 操作 shared_counter 3. 操作 shared_counter
│    │                     │
│    ▼                     ▼
│ 4. 释放互斥锁         4. 释放互斥锁
│    (k_mutex_unlock)      (k_mutex_unlock)
│    │                     │
│    ▼                     ▼
│ 5. 休眠 500ms         5. 休眠 700ms
│    │                     │
│    └─────> (循环)       └─────> (循环)
│
│ 互斥锁保证同一时间只有一个线程可以访问共享资源
│ 两个线程竞争执行,但 shared_counter 的递增操作是原子性的
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 消息队列示例 (Message Queue)                      |
+--------------------------------------------------+
│
│ [生产者线程]            [消费者线程]
│    │                     │
│ 1. 构造消息            1. 等待消息
│    │                     │
│    ▼                     ▼
│ 2. 发送消息到队列      2. 从队列接收消息
│    (k_msgq_put)           (k_msgq_get)
│    │                     │
│    ▼                     ▼
│ 3. 打印日志            3. 打印消息内容
│    │                     │
│    ▼                     ▼
│ 4. 休眠 1 秒           4. 处理消息 (500ms)
│    │                     │
│    └─────> (循环)       └─────> (循环)
│
│ 消息队列容量为 10,当队列满时生产者会挂起
│ 当队列空时消费者会挂起
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 事件示例 (Event)                                  |
+--------------------------------------------------+
│
│ [发送者线程]            [接收者线程]
│    │                     │
│ 1. 休眠 2 秒           1. 等待事件
│    │                     (k_event_wait)
│    ▼                     │
│ 2. 发布事件位            ▼
│    (k_event_post)       2. 收到事件
│    │                     │
│    ▼                     ▼
│ 3. 打印日志            3. 处理事件位
│    │                     │
│    └─────> (循环)        └─────> (循环)
│
│ 事件位:BIT_0 (1<<0), BIT_1 (1<<1)
│ 发送者交替发布不同的事件位
│ 接收者同时等待 BIT_0 和 BIT_1
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 条件变量示例 (Condition Variable)                 |
+--------------------------------------------------+
│
│ [等待者线程]            [信号者线程]
│    │                     │
│ 1. 获取互斥锁           1. 休眠 3 秒
│    (k_mutex_lock)         │
│    │                     ▼
│    ▼                    2. 获取互斥锁
│ 2. 检查条件               (k_mutex_lock)
│    (state->ready == 0)   │
│    │                     ▼
│    ▼                    3. 设置数据
│ 3. 等待条件变量           (state->data = ++count)
│    (k_cond_wait)          │
│    │                     ▼
│    ▼                    4. 设置就绪标志
│    │                     (state->ready = 1)
│    │                     │
│    │                     ▼
│    │                    5. 通知等待者
│    │                     (k_cond_signal)
│    │                     │
│    │                     ▼
│    │                    6. 释放互斥锁
│    │                     (k_mutex_unlock)
│    │                     │
│    ▼                     │
│ 4. 被唤醒,检查条件      └─────> (循环)
│    │                     │
│    ▼                     │
│ 5. 处理数据               │
│    │                     │
│    ▼                     │
│ 6. 释放互斥锁             │
│    (k_mutex_unlock)        │
│    │                     │
│    └─────> (循环)         │
│
│ 条件变量用于等待某个条件变为真
│ 必须在互斥锁的保护下使用
│ k_cond_wait 会释放互斥锁,被唤醒后重新获取
│
+--------------------------------------------------+

4. 关键 API 注解

API 功能 注解
K_SEM_DEFINE(name, init_count, max_count) 静态定义信号量 /**
  • @def K_SEM_DEFINE(name, init_count, max_count)

  • @brief 静态初始化信号量

  • @param name 信号量名称

  • @param init_count 初始计数

  • @param max_count 最大计数 */ | | k_sem_give(sem) | 释放信号量 | /**

  • @fn void k_sem_give(struct k_sem *sem)

  • @brief 释放信号量,增加计数值

  • @param sem 信号量指针 */ | | k_sem_take(sem, timeout) | 获取信号量 | /**

  • @fn int k_sem_take(struct k_sem *sem, k_timeout_t timeout)

  • @brief 获取信号量,减少计数值

  • @param sem 信号量指针

  • @param timeout 超时时间 (K_FOREVER 表示永久等待)

  • @return 0 成功,-EBUSY 超时 */ | | K_MUTEX_DEFINE(name) | 静态定义互斥锁 | /**

  • @def K_MUTEX_DEFINE(name)

  • @brief 静态初始化互斥锁

  • @param name 互斥锁名称 */ | | k_mutex_lock(mutex, timeout) | 获取互斥锁 | /**

  • @fn int k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout)

  • @brief 获取互斥锁

  • @param mutex 互斥锁指针

  • @param timeout 超时时间

  • @return 0 成功,-EBUSY 超时 */ | | k_mutex_unlock(mutex) | 释放互斥锁 | /**

  • @fn void k_mutex_unlock(struct k_mutex *mutex)

  • @brief 释放互斥锁

  • @param mutex 互斥锁指针 */ | | K_MSGQ_DEFINE(name, msg_size, max_msgs, align) | 静态定义消息队列 | /**

  • @def K_MSGQ_DEFINE(name, msg_size, max_msgs, align)

  • @brief 静态初始化消息队列

  • @param name 队列名称

  • @param msg_size 每条消息大小(字节)

  • @param max_msgs 队列最大消息数

  • @param align 对齐方式 */ | | k_msgq_put(msgq, data, timeout) | 发送消息到队列 | /**

  • @fn int k_msgq_put(struct k_msgq *msgq, const void *data, k_timeout_t timeout)

  • @brief 发送消息到队列

  • @param msgq 消息队列指针

  • @param data 消息数据指针

  • @param timeout 超时时间

  • @return 0 成功,-ENOMEM 队列满 */ | | k_msgq_get(msgq, data, timeout) | 从队列接收消息 | /**

  • @fn int k_msgq_get(struct k_msgq *msgq, void *data, k_timeout_t timeout)

  • @brief 从队列接收消息

  • @param msgq 消息队列指针

  • @param data 接收缓冲区指针

  • @param timeout 超时时间

  • @return 0 成功,-ENOMSG 队列空 */ | | K_EVENT_DEFINE(name) | 静态定义事件对象 | /**

  • @def K_EVENT_DEFINE(name)

  • @brief 静态初始化事件对象

  • @param name 事件名称 */ | | k_event_post(event, events) | 发布事件位 | /**

  • @fn void k_event_post(struct k_event *event, uint32_t events)

  • @brief 发布事件位

  • @param event 事件指针

  • @param events 要设置的事件位掩码 */ | | k_event_wait(event, events, reset, timeout) | 等待事件位 | /**

  • @fn uint32_t k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout)

  • @brief 等待事件位

  • @param event 事件指针

  • @param events 要等待的事件位掩码

  • @param reset 是否清除事件位

  • @param timeout 超时时间

  • @return 实际触发的事件位 */ | | K_COND_DEFINE(name) | 静态定义条件变量 | /**

  • @def K_COND_DEFINE(name)

  • @brief 静态初始化条件变量

  • @param name 条件变量名称 */ | | k_cond_wait(cond, mutex, timeout) | 等待条件变量 | /**

  • @fn int k_cond_wait(struct k_cond *cond, struct k_mutex *mutex, k_timeout_t timeout)

  • @brief 等待条件变量

  • @param cond 条件变量指针

  • @param mutex 互斥锁指针

  • @param timeout 超时时间

  • @return 0 成功,-EBUSY 超时 */ | | k_cond_signal(cond) | 通知条件变量 | /**

  • @fn void k_cond_signal(struct k_cond *cond)

  • @brief 通知条件变量(唤醒至少一个等待者)

  • @param cond 条件变量指针 */ |

5. 项目配置(prj.conf)

# 启用所有同步机制
CONFIG_SEMAPHORE=y
CONFIG_MUTEX=y
CONFIG_MSGQ=y
CONFIG_EVENTS=y
CONFIG_COND=y
​
# 启用标准输出
CONFIG_PRINTK=y
​
# 设置内核对象调试(可选)
CONFIG_KERNEL_LOG_LEVEL_INFO=y
CONFIG_KERNEL_LOG_LEVEL_DBG=y

6. 总结

  • 核心流程

    • 信号量:生产者释放(give),消费者获取(take),适用于资源计数和事件通知。

    • 互斥锁:线程在临界区前后加锁解锁,适用于保护共享资源。

    • 消息队列:生产者放入(put),消费者取出(get),适用于线程间数据传递。

    • 事件:发送者发布事件位,接收者等待事件位,适用于多事件通知场景。

    • 条件变量:等待者等待条件(cond_wait),信号者通知条件(cond_signal),适用于复杂条件同步。

  • Zephyr 特色

    • 所有同步对象都可以静态初始化(K_*_DEFINE 宏),避免运行时内存分配。

    • 支持 K_FOREVERK_NO_WAIT 等超时选项,灵活控制挂起行为。

    • 统一的内核对象 API 设计,学习曲线低。

  • 调试方式

    • 通过 printk 输出线程执行日志,观察同步行为。

    • 可以使用 k_thread_priority_get() 检查线程优先级。

    • 可以使用 k_object_get() 打印内核对象信息。

  • 扩展方向

    • 使用 k_sem_reset() 重置信号量计数。

    • 使用 k_cond_broadcast() 唤醒所有等待者(条件变量)。

    • 使用 k_event_wait_all() 等待所有事件位同时触发。

    • 使用 k_msgq_purge() 清空消息队列。

这个示例完整展示了 Zephyr 内核提供的所有线程同步机制,实际开发中可以根据具体需求选择最适合的同步方式。

第五部分 Zephyr 官方示例:thread_management(线程管理)

1. 文件结构

samples/basic/thread_management/
├── src/
│   └── main.c               # 主程序源码
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码(带 Doxygen 注解)

/**
 * @file main.c
 * @brief Zephyr 线程管理示例程序
 *
 * 该程序演示了 Zephyr 内核中线程管理的核心功能:
 * - 线程创建(静态与动态)
 * - 线程优先级设置与调整
 * - 线程挂起与恢复
 * - 线程退出与回收
 * - 线程状态监控
 * - 内核对象调试
 */
​
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/kernel_structs.h>  /* 用于线程状态枚举 */
​
/* ============================================================ */
/* 1. 线程栈与线程句柄定义                                      */
/* ============================================================ */
​
#define STACK_SIZE 1024
​
/**
 * @def THREAD_PRIO_LOW
 * @brief 低优先级线程优先级 (数值越大优先级越低)
 */
#define THREAD_PRIO_LOW  7
​
/**
 * @def THREAD_PRIO_MID
 * @brief 中优先级线程优先级
 */
#define THREAD_PRIO_MID  5
​
/**
 * @def THREAD_PRIO_HIGH
 * @brief 高优先级线程优先级
 */
#define THREAD_PRIO_HIGH 3
​
/**
 * @brief 静态线程栈定义
 *
 * 使用 K_THREAD_STACK_DEFINE 宏静态定义线程栈,
 * 并指定栈大小。该宏会处理架构相关的栈对齐。
 */
K_THREAD_STACK_DEFINE(static_thread_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(thread1_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(thread2_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(thread3_stack, STACK_SIZE);
​
/**
 * @brief 线程句柄定义
 *
 * 用于引用线程对象,进行挂起、恢复、优先级调整等操作。
 */
struct k_thread static_thread_data;
struct k_thread thread1_data;
struct k_thread thread2_data;
struct k_thread thread3_data;
​
/* ============================================================ */
/* 2. 线程函数定义                                              */
/* ============================================================ */
​
/**
 * @brief 静态线程函数
 *
 * 演示使用 K_THREAD_DEFINE 宏创建的静态线程。
 * 该线程会定期打印消息并演示线程状态。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void static_thread_fn(void *arg1, void *arg2, void *arg3)
{
    printk("[StaticThread] Started, priority = %d\n",
           k_thread_priority_get(k_current_get()));
​
    while (1) {
        printk("[StaticThread] Running...\n");
        k_sleep(K_SECONDS(2));
    }
}
​
/**
 * @brief 线程1函数:演示优先级调整
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void thread1_fn(void *arg1, void *arg2, void *arg3)
{
    int priority;
​
    printk("[Thread1] Started, initial priority = %d\n",
           k_thread_priority_get(k_current_get()));
​
    /* 演示优先级调整 */
    k_sleep(K_SECONDS(1));
    priority = THREAD_PRIO_MID - 1;
    printk("[Thread1] Changing priority to %d\n", priority);
    k_thread_priority_set(k_current_get(), priority);
​
    /* 验证新优先级 */
    priority = k_thread_priority_get(k_current_get());
    printk("[Thread1] New priority = %d\n", priority);
​
    while (1) {
        printk("[Thread1] Running at priority %d\n", priority);
        k_sleep(K_SECONDS(3));
    }
}
​
/**
 * @brief 线程2函数:演示线程挂起与恢复
 *
 * @param arg1 线程参数(指向控制变量)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void thread2_fn(void *arg1, void *arg2, void *arg3)
{
    int *control = (int *)arg1;
    int count = 0;
​
    printk("[Thread2] Started, will suspend and resume repeatedly\n");
​
    while (1) {
        count++;
        printk("[Thread2] Iteration %d\n", count);
​
        /* 检查是否需要挂起 */
        if (*control == 1) {
            printk("[Thread2] Suspending itself\n");
            k_thread_suspend(k_current_get());
            printk("[Thread2] Resumed\n");
        }
​
        k_sleep(K_SECONDS(1));
    }
}
​
/**
 * @brief 线程3函数:演示线程退出
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void thread3_fn(void *arg1, void *arg2, void *arg3)
{
    int count = 0;
​
    printk("[Thread3] Started, will exit after 3 iterations\n");
​
    while (1) {
        count++;
        printk("[Thread3] Iteration %d\n", count);
​
        if (count >= 3) {
            printk("[Thread3] Exiting now\n");
            /* 退出当前线程 */
            k_thread_abort(k_current_get());
        }
​
        k_sleep(K_SECONDS(1));
    }
}
​
/* ============================================================ */
/* 3. 静态线程定义 (使用 K_THREAD_DEFINE)                       */
/* ============================================================ */
​
/**
 * @brief 使用 K_THREAD_DEFINE 宏静态定义线程
 *
 * 该宏会在编译时创建线程对象和栈,并自动加入系统线程列表。
 * 线程名称为 static_thread,优先级 5,栈大小 1024。
 */
K_THREAD_DEFINE(static_thread_tid, STACK_SIZE,
                static_thread_fn, NULL, NULL, NULL,
                THREAD_PRIO_MID, 0, 0);
​
/* ============================================================ */
/* 4. 系统主入口                                               */
/* ============================================================ */
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责:
 * 1. 创建多个动态线程并演示线程管理功能
 * 2. 演示线程优先级调整、挂起、恢复、退出
 * 3. 演示内核对象调试功能
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    int control = 0;
    struct k_thread *thread;
​
    printk("Zephyr Thread Management Example\n");
    printk("================================\n");
​
    /* 1. 静态线程已经自动创建,我们只需等待它运行 */
    printk("[Main] Static thread already created by K_THREAD_DEFINE\n");
​
    /* 2. 创建线程1:演示优先级调整 */
    k_thread_create(&thread1_data, thread1_stack, STACK_SIZE,
                    thread1_fn, NULL, NULL, NULL,
                    THREAD_PRIO_LOW, 0, K_NO_WAIT);
    printk("[Main] Thread1 created with priority %d\n", THREAD_PRIO_LOW);
​
    /* 3. 创建线程2:演示挂起与恢复 */
    k_thread_create(&thread2_data, thread2_stack, STACK_SIZE,
                    thread2_fn, &control, NULL, NULL,
                    THREAD_PRIO_LOW, 0, K_NO_WAIT);
    printk("[Main] Thread2 created, control variable set to 0\n");
​
    /* 4. 创建线程3:演示线程退出 */
    k_thread_create(&thread3_data, thread3_stack, STACK_SIZE,
                    thread3_fn, NULL, NULL, NULL,
                    THREAD_PRIO_LOW, 0, K_NO_WAIT);
    printk("[Main] Thread3 created\n");
​
    /* 5. 演示线程管理操作(在主线程中执行) */
    k_sleep(K_SECONDS(3));
​
    /* 5.1 演示线程优先级调整(修改已创建的线程) */
    printk("[Main] Changing thread1 priority to %d\n", THREAD_PRIO_MID);
    k_thread_priority_set(&thread1_data, THREAD_PRIO_MID);
​
    /* 5.2 演示线程挂起 */
    printk("[Main] Suspending thread2\n");
    control = 1;  /* 触发线程2自己挂起 */
    k_sleep(K_MSEC(500));
    printk("[Main] Resuming thread2\n");
    control = 0;
    k_thread_resume(&thread2_data);
​
    /* 5.3 演示线程退出等待 */
    printk("[Main] Waiting for thread3 to exit\n");
    k_sleep(K_SECONDS(5));  /* 等待线程3自行退出 */
​
    /* 5.4 演示内核对象调试 */
    printk("[Main] Dumping kernel object info:\n");
    k_object_dump((void *)&static_thread_tid, K_OBJ_THREAD, NULL, NULL);
​
    printk("[Main] Main thread finished demo, will sleep forever\n");
​
    while (1) {
        k_sleep(K_SECONDS(10));
    }
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印标题信息
    │
    │ 2. 静态线程已经通过 K_THREAD_DEFINE 自动创建
    │    - 线程名: static_thread
    │    - 优先级: THREAD_PRIO_MID (5)
    │    - 栈大小: 1024
    │    - 线程函数: static_thread_fn
    │
    │ 3. 创建动态线程1 (thread1_fn)
    │    - 优先级: THREAD_PRIO_LOW (7)
    │    - 用于演示优先级调整
    │
    │ 4. 创建动态线程2 (thread2_fn)
    │    - 优先级: THREAD_PRIO_LOW (7)
    │    - 参数: control 变量指针
    │    - 用于演示挂起与恢复
    │
    │ 5. 创建动态线程3 (thread3_fn)
    │    - 优先级: THREAD_PRIO_LOW (7)
    │    - 用于演示线程退出
    │
    │ 6. 等待 3 秒(让各线程执行一段时间)
    │
    │ 7. 演示线程管理操作
    │    ├─ 7.1 调整线程1优先级为 THREAD_PRIO_MID (5)
    │    ├─ 7.2 触发线程2挂起:
    │    │    - 设置 control = 1
    │    │    - 线程2检测到控制值后自我挂起
    │    │    - 500ms 后恢复线程2 (control = 0)
    │    ├─ 7.3 等待线程3退出 (5秒)
    │    │    - 线程3执行3次后自行调用 k_thread_abort()
    │    └─ 7.4 打印内核对象调试信息
    │
    │ 8. 主线程进入无限循环 (休眠10秒)
    │
    ▼
[线程并发执行]
​
+--------------------------------------------------+
| 静态线程 (static_thread_fn)                       |
| 优先级: 5 (THREAD_PRIO_MID)                       |
+--------------------------------------------------+
│
│ 1. 启动后打印当前优先级
│
│ 2. 进入无限循环:
│    ├─ 打印 "[StaticThread] Running..."
│    └─ 休眠 2 秒
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 线程1 (thread1_fn)                                |
| 初始优先级: 7 (THREAD_PRIO_LOW)                  |
+--------------------------------------------------+
│
│ 1. 启动后打印初始优先级 (7)
│
│ 2. 休眠 1 秒
│
│ 3. 在 main 线程中被调整优先级为 5 (THREAD_PRIO_MID)
│    - 打印优先级变化
│    - 验证新优先级 (5)
│
│ 4. 进入无限循环:
│    ├─ 打印当前优先级 (5)
│    └─ 休眠 3 秒
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 线程2 (thread2_fn)                                |
| 优先级: 7 (THREAD_PRIO_LOW)                      |
+--------------------------------------------------+
│
│ 1. 启动后打印消息
│
│ 2. 进入循环:
│    ├─ 打印当前迭代次数
│    ├─ 检查 control 变量
│    │    └─ 若 control == 1:
│    │         ├─ 打印 "Suspending itself"
│    │         ├─ 调用 k_thread_suspend()
│    │         └─ 被恢复后打印 "Resumed"
│    ├─ 休眠 1 秒
│    └─ (循环)
│
│ 在 main 线程中:
│    - control 被设置为 1 → 线程2自我挂起
│    - 500ms 后 control 被设置为 0
│    - 调用 k_thread_resume() 恢复线程2
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 线程3 (thread3_fn)                                |
| 优先级: 7 (THREAD_PRIO_LOW)                      |
+--------------------------------------------------+
│
│ 1. 启动后打印 "will exit after 3 iterations"
│
│ 2. 进入循环:
│    ├─ 迭代计数递增
│    ├─ 打印当前迭代次数
│    ├─ 若计数 >= 3:
│    │    └─ 打印 "Exiting now" + k_thread_abort()
│    ├─ 休眠 1 秒
│    └─ (循环)
│
│ 3. 执行 3 次迭代后自动退出
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 主线程 (main) 的线程管理操作                      |
+--------------------------------------------------+
│
│ 时间线:
│   0s: 创建所有线程
│   3s: 开始管理操作
│       3.5s: 线程2挂起
│       4.0s: 线程2恢复
│       8.0s: 线程3退出 (3次迭代后)
│       8.5s: 打印内核对象调试信息
│   之后: 主线程休眠10秒循环
│
+--------------------------------------------------+

4. 关键 API 注解

API 功能 注解
K_THREAD_DEFINE(name, stack_size, fn, arg1, arg2, arg3, prio, options, delay) 静态定义线程 /**
  • @def K_THREAD_DEFINE(name, stack_size, fn, arg1, arg2, arg3, prio, options, delay)

  • @brief 静态创建并定义线程

  • @param name 线程名称

  • @param stack_size 栈大小

  • @param fn 线程函数指针

  • @param arg1-arg3 线程参数

  • @param prio 优先级

  • @param options 选项 (如 K_ESSENTIAL)

  • @param delay 延迟启动时间 */ | | k_thread_create(thread, stack, stack_size, fn, arg1, arg2, arg3, prio, options, delay) | 动态创建线程 | /**

  • @fn k_tid_t k_thread_create(struct k_thread *thread, k_thread_stack_t *stack, size_t stack_size, k_thread_entry_t entry, void *p1, void *p2, void *p3, int prio, uint32_t options, k_timeout_t delay)

  • @brief 动态创建线程

  • @param thread 线程句柄指针

  • @param stack 栈指针

  • @param stack_size 栈大小

  • @param entry 线程函数

  • @param p1-p3 线程参数

  • @param prio 优先级

  • @param options 选项

  • @param delay 延迟启动时间

  • @return 线程ID */ | | k_thread_priority_get(thread) | 获取线程优先级 | /**

  • @fn int k_thread_priority_get(struct k_thread *thread)

  • @brief 获取线程优先级

  • @param thread 线程句柄

  • @return 优先级数值 */ | | k_thread_priority_set(thread, prio) | 设置线程优先级 | /**

  • @fn void k_thread_priority_set(struct k_thread *thread, int prio)

  • @brief 设置线程优先级

  • @param thread 线程句柄

  • @param prio 新优先级 */ | | k_thread_suspend(thread) | 挂起线程 | /**

  • @fn void k_thread_suspend(struct k_thread *thread)

  • @brief 挂起指定线程

  • @param thread 线程句柄 */ | | k_thread_resume(thread) | 恢复线程 | /**

  • @fn void k_thread_resume(struct k_thread *thread)

  • @brief 恢复挂起的线程

  • @param thread 线程句柄 */ | | k_thread_abort(thread) | 终止线程 | /**

  • @fn void k_thread_abort(struct k_thread *thread)

  • @brief 强制终止线程

  • @param thread 线程句柄 */ | | k_object_dump(obj, type, stdout_func, parent) | 调试打印内核对象信息 | /**

  • @fn void k_object_dump(void obj, enum k_objects type, void (stdout_func)(void *arg), void *parent)

  • @brief 打印内核对象信息(用于调试)

  • @param obj 对象指针

  • @param type 对象类型 (K_OBJ_THREAD, K_OBJ_MUTEX 等)

  • @param stdout_func 输出函数(NULL 则使用默认)

  • @param parent 父对象指针 */ |

5. 项目配置(prj.conf)

# 启用线程管理相关功能
CONFIG_THREAD_APIS=y
CONFIG_THREAD_MONITOR=y
CONFIG_THREAD_NAME=y
CONFIG_THREAD_CUSTOM_DATA=y
​
# 启用内核对象调试
CONFIG_KERNEL_OBJ_DEBUG=y
CONFIG_KERNEL_OBJ_DEBUG_TLS=y
​
# 启用线程优先级调整
CONFIG_PRIORITY_CEILING=y
​
# 启用标准输出
CONFIG_PRINTK=y
​
# 设置线程栈大小(全局默认)
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_IDLE_STACK_SIZE=1024

6. 总结

  • 核心流程

    • 静态线程:通过 K_THREAD_DEFINE 宏在编译时创建,自动加入调度。

    • 动态线程:通过 k_thread_create 函数在运行时创建,需要手动管理栈和句柄。

    • 优先级管理:使用 k_thread_priority_set 调整线程优先级。

    • 挂起与恢复:使用 k_thread_suspendk_thread_resume 控制线程执行。

    • 线程退出:使用 k_thread_abort 强制终止线程。

    • 内核对象调试:使用 k_object_dump 打印内核对象信息。

  • Zephyr 特色

    • 静态线程创建(K_THREAD_DEFINE)减少运行时开销,适用于常驻线程。

    • 线程优先级数值越小优先级越高(与 Linux 相反)。

    • 支持 K_ESSENTIAL 选项,创建不可被杀死的系统线程。

    • 线程栈通过 K_THREAD_STACK_DEFINE 宏定义,处理架构相关的对齐。

  • 调试方式

    • 通过 printk 输出线程状态变化。

    • 使用 k_object_dump 查看线程信息。

    • 使用 k_thread_state_str 获取线程状态字符串(需启用 THREAD_MONITOR)。

  • 扩展方向

    • 使用 k_thread_start 延迟启动线程。

    • 使用 k_thread_join 等待线程结束。

    • 使用 k_thread_custom_data_set 存储线程私有数据。

    • 使用 k_thread_name_set 为线程命名。

这个示例完整展示了 Zephyr 线程管理的核心功能,通过静态线程和动态线程的灵活组合,可以实现复杂的线程调度策略。

第六部分 Zephyr 官方示例:timer(定时器与时间管理)

1. 文件结构

samples/basic/timer/
├── src/
│   └── main.c               # 主程序源码
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr 定时器示例程序
 *
 * 该程序演示了 Zephyr 内核中定时器的核心功能:
 * - 定时器的创建与初始化
 * - 定时器的启动(单次与周期性)
 * - 定时器的停止与重置
 * - 定时器回调函数的编写
 * - 时间管理(系统滴答、时间戳、延时)
 * - 高精度定时器(HRT)的使用
 */
​
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/time64.h>
#include <zephyr/device.h>
#include <zephyr/drivers/timer/rtc.h>
#include <zephyr/drivers/timer/arm_arch_timer.h>
#include <zephyr/timing/timing.h>
#include <zephyr/kernel.h>
​
/* ============================================================ */
/* 1. 定时器对象定义                                             */
/* ============================================================ */
​
/**
 * @def TIMER_PERIOD_MS
 * @brief 定时器周期(毫秒)
 *
 * 定义定时器的触发周期。
 */
#define TIMER_PERIOD_MS 500
​
/**
 * @def TIMER_DELAY_MS
 * @brief 定时器延迟启动时间(毫秒)
 */
#define TIMER_DELAY_MS 1000
​
/**
 * @brief 定时器对象定义
 *
 * 使用 K_TIMER_DEFINE 宏静态定义定时器对象。
 *
 * 定时器名称:timer1
 * 用于演示周期性定时器功能
 */
K_TIMER_DEFINE(timer1);
​
/**
 * @brief 另一个定时器对象定义
 *
 * 用于演示单次定时器功能。
 */
K_TIMER_DEFINE(timer2);
​
/**
 * @brief 高精度定时器对象定义(可选)
 *
 * 用于演示高精度定时器功能(需硬件支持)。
 */
K_TIMER_DEFINE(hrt_timer);
​
/* ============================================================ */
/* 2. 定时器回调函数定义                                         */
/* ============================================================ */
​
/**
 * @brief 定时器1的回调函数(周期性定时器)
 *
 * 该函数在定时器触发时被调用,运行在系统工作队列上下文。
 * 函数应当快速执行,避免阻塞。
 *
 * @param timer 定时器指针
 */
void timer1_callback(struct k_timer *timer)
{
    static int count = 0;
    uint64_t now = k_uptime_get();
​
    count++;
    printk("[Timer1] Periodic timer triggered (count = %d, time = %llu ms)\n",
           count, now);
​
    /* 每 5 次触发时,演示停止定时器 */
    if (count == 5) {
        printk("[Timer1] Stopping timer after 5 triggers\n");
        k_timer_stop(timer);
    }
}
​
/**
 * @brief 定时器2的回调函数(单次定时器)
 *
 * 该函数在单次定时器触发时被调用。
 *
 * @param timer 定时器指针
 */
void timer2_callback(struct k_timer *timer)
{
    uint64_t now = k_uptime_get();
    printk("[Timer2] One-shot timer triggered at %llu ms\n", now);
​
    /* 演示重置定时器 */
    printk("[Timer2] Restarting timer for another 3 seconds\n");
    k_timer_start(timer, K_SECONDS(3), K_NO_WAIT);
}
​
/**
 * @brief 高精度定时器回调函数
 *
 * 该函数演示高精度定时器的使用(需要硬件支持)。
 *
 * @param timer 定时器指针
 */
void hrt_timer_callback(struct k_timer *timer)
{
    static int count = 0;
    count++;
    printk("[HRT] High-resolution timer trigger %d\n", count);
}
​
/* ============================================================ */
/* 3. 定时器超时处理函数(通过工作队列)                         */
/* ============================================================ */
​
/**
 * @brief 定时器超时处理工作项
 *
 * 定时器触发时除了调用回调函数,还可以通过工作队列执行更复杂的操作。
 * 使用 K_WORK_DELAYABLE_DEFINE 定义一个可延迟工作项。
 */
K_WORK_DELAYABLE_DEFINE(timer_work, timer_work_handler);
​
/**
 * @brief 工作项处理函数
 *
 * 该函数由工作队列执行,可以执行较长时间的操作。
 *
 * @param work 工作项指针
 */
void timer_work_handler(struct k_work *work)
{
    printk("[Work] Delayed work executed at %llu ms\n", k_uptime_get());
}
​
/* ============================================================ */
/* 4. 线程函数定义                                               */
/* ============================================================ */
​
#define STACK_SIZE 1024
​
/**
 * @brief 定时器管理线程
 *
 * 该线程负责演示定时器的动态管理操作。
 *
 * @param arg1 线程参数(未使用)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void timer_thread(void *arg1, void *arg2, void *arg3)
{
    uint64_t start_time;
    uint64_t current_time;
    int count = 0;
​
    printk("[Thread] Timer management thread started\n");
​
    /* 1. 演示 k_uptime_get() 获取系统启动时间 */
    start_time = k_uptime_get();
    printk("[Thread] System uptime at start: %llu ms\n", start_time);
​
    while (1) {
        count++;
        current_time = k_uptime_get();
        printk("[Thread] Iteration %d, uptime = %llu ms\n", count, current_time);
​
        /* 2. 演示时间差值计算 */
        if (count == 3) {
            uint64_t elapsed = current_time - start_time;
            printk("[Thread] Elapsed time since start: %llu ms\n", elapsed);
        }
​
        /* 3. 演示定时器状态检查 */
        if (count == 4) {
            bool is_timer1_pending = k_timer_is_pending(&timer1);
            bool is_timer2_pending = k_timer_is_pending(&timer2);
            printk("[Thread] Timer1 pending: %d, Timer2 pending: %d\n",
                   is_timer1_pending, is_timer2_pending);
        }
​
        /* 4. 演示剩余时间获取 */
        if (count == 5) {
            uint64_t remaining1 = k_timer_remaining_get(&timer1);
            uint64_t remaining2 = k_timer_remaining_get(&timer2);
            printk("[Thread] Timer1 remaining: %llu ms, Timer2 remaining: %llu ms\n",
                   remaining1, remaining2);
        }
​
        k_sleep(K_SECONDS(2));
    }
}
​
/* 线程栈与句柄 */
K_THREAD_STACK_DEFINE(timer_thread_stack, STACK_SIZE);
struct k_thread timer_thread_data;
​
/* ============================================================ */
/* 5. 系统主入口                                                 */
/* ============================================================ */
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责:
 * 1. 初始化并启动定时器
 * 2. 启动管理线程
 * 3. 演示定时器的高级操作
 * 4. 演示时间管理功能
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    uint64_t start_time;
    uint64_t ticks_per_ms;
    int ret;
​
    printk("Zephyr Timer Example\n");
    printk("====================\n");
​
    /* 1. 获取系统滴答频率 */
    ticks_per_ms = CONFIG_SYS_CLOCK_TICKS_PER_SEC / 1000;
    printk("[Main] System tick rate: %llu ticks per ms\n", ticks_per_ms);
​
    /* 2. 启动周期性定时器 timer1 */
    printk("[Main] Starting periodic timer1 with period %d ms, delay %d ms\n",
           TIMER_PERIOD_MS, TIMER_DELAY_MS);
    k_timer_init(&timer1, timer1_callback, NULL);
    k_timer_start(&timer1, K_MSEC(TIMER_DELAY_MS), K_MSEC(TIMER_PERIOD_MS));
​
    /* 3. 启动单次定时器 timer2 */
    printk("[Main] Starting one-shot timer2 with delay 3 seconds\n");
    k_timer_init(&timer2, timer2_callback, NULL);
    k_timer_start(&timer2, K_SECONDS(3), K_NO_WAIT);
​
    /* 4. 演示使用工作队列处理定时器超时 */
    printk("[Main] Starting delayed work (will execute after 2 seconds)\n");
    k_work_schedule(&timer_work, K_SECONDS(2));
​
    /* 5. 演示定时器状态查询 */
    printk("[Main] Timer1 pending: %d\n", k_timer_is_pending(&timer1));
    printk("[Main] Timer2 pending: %d\n", k_timer_is_pending(&timer2));
​
    /* 6. 演示获取定时器剩余时间 */
    printk("[Main] Timer1 remaining: %llu ms\n",
           k_timer_remaining_get(&timer1) / 1000);
    printk("[Main] Timer2 remaining: %llu ms\n",
           k_timer_remaining_get(&timer2) / 1000);
​
    /* 7. 演示时间戳获取 */
    start_time = k_uptime_get();
    printk("[Main] Current uptime: %llu ms\n", start_time);
​
    /* 8. 创建并启动管理线程 */
    k_thread_create(&timer_thread_data, timer_thread_stack, STACK_SIZE,
                    timer_thread, NULL, NULL, NULL,
                    5, 0, K_NO_WAIT);
​
    printk("[Main] All timers started, management thread running\n");
​
    /* 9. 演示高精度定时器(如果硬件支持) */
#ifdef CONFIG_HW_TIMER
    printk("[Main] High-resolution timer available\n");
    k_timer_init(&hrt_timer, hrt_timer_callback, NULL);
    k_timer_start(&hrt_timer, K_USEC(100), K_USEC(100));
#else
    printk("[Main] High-resolution timer not available (HW_TIMER not configured)\n");
#endif
​
    /* 10. 主线程进入循环,演示定时器同步等待 */
    printk("[Main] Main thread sleeping for 8 seconds...\n");
    k_sleep(K_SECONDS(8));
​
    /* 11. 演示定时器停止 */
    printk("[Main] Stopping timer1\n");
    k_timer_stop(&timer1);
​
    printk("[Main] Stopping timer2\n");
    k_timer_stop(&timer2);
​
    printk("[Main] Demonstrating timer synchronization (waiting for timer1 to stop)\n");
    k_timer_stop_sync(&timer1);
    printk("[Main] Timer1 stopped synchronously\n");
​
    printk("[Main] Main thread finished demo, will sleep forever\n");
​
    while (1) {
        k_sleep(K_SECONDS(10));
    }
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 初始化系统滴答定时器
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印标题信息
    │
    │ 2. 获取系统滴答频率
    │    - `CONFIG_SYS_CLOCK_TICKS_PER_SEC` 配置值
    │    - 计算每毫秒滴答数
    │
    │ 3. 启动周期性定时器 timer1
    │    - 使用 `k_timer_init` 初始化
    │    - 使用 `k_timer_start` 启动
    │    - 延迟启动: 1000ms
    │    - 周期: 500ms
    │    - 回调函数: timer1_callback
    │
    │ 4. 启动单次定时器 timer2
    │    - 使用 `k_timer_init` 初始化
    │    - 使用 `k_timer_start` 启动
    │    - 延迟: 3000ms (3秒)
    │    - 回调函数: timer2_callback
    │
    │ 5. 演示工作队列处理定时器超时
    │    - `k_work_schedule(&timer_work, K_SECONDS(2))`
    │    - 2秒后执行 timer_work_handler
    │
    │ 6. 演示定时器状态查询
    │    - `k_timer_is_pending(&timer1)` 检查是否在等待
    │    - `k_timer_is_pending(&timer2)` 检查是否在等待
    │
    │ 7. 演示获取定时器剩余时间
    │    - `k_timer_remaining_get(&timer1) / 1000`
    │    - `k_timer_remaining_get(&timer2) / 1000`
    │
    │ 8. 演示时间戳获取
    │    - `k_uptime_get()` 获取系统运行时间
    │
    │ 9. 创建管理线程 (timer_thread)
    │    - 优先级: 5
    │    - 栈大小: 1024
    │    - 线程函数: timer_thread
    │
    │ 10. 演示高精度定时器 (如果硬件支持)
    │     - 初始化 hrt_timer
    │     - 启动:周期 100us (微秒)
    │
    │ 11. 主线程休眠 8 秒
    │     - 等待定时器触发和线程执行
    │
    │ 12. 演示定时器停止
    │     - 停止 timer1 (`k_timer_stop`)
    │     - 停止 timer2 (`k_timer_stop`)
    │
    │ 13. 演示定时器同步停止
    │     - `k_timer_stop_sync(&timer1)` 等待定时器完全停止
    │
    │ 14. 主线程进入无限循环 (休眠10秒)
    │
    ▼
[定时器执行流程]
​
+--------------------------------------------------+
| 周期性定时器 (timer1)                             |
| 延迟: 1000ms, 周期: 500ms                        |
+--------------------------------------------------+
│
│ 0ms:   timer1 启动,开始倒计时
│
│ 1000ms: timer1 首次触发
│         调用 timer1_callback()
│         - 打印触发计数 (count = 1)
│         - 记录当前时间
│
│ 1500ms: timer1 第二次触发
│         - count = 2
│         - 打印信息
│
│ 2000ms: timer1 第三次触发
│         - count = 3
│         - 打印信息
│
│ 2500ms: timer1 第四次触发
│         - count = 4
│         - 打印信息
│
│ 3000ms: timer1 第五次触发
│         - count = 5
│         - 打印信息
│         - 执行 k_timer_stop(timer1) 停止自身
│
│ [timer1 停止] 不再触发
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 单次定时器 (timer2)                               |
| 延迟: 3000ms                                     |
+--------------------------------------------------+
│
│ 0ms:   timer2 启动,开始倒计时
│
│ 3000ms: timer2 首次触发
│         调用 timer2_callback()
│         - 打印触发时间和信息
│         - 重新启动定时器: K_SECONDS(3)
│         (变成周期性)
│
│ 6000ms: timer2 第二次触发
│         - 打印 "Restarting timer"
│         - 再次重启
│
│ 9000ms: timer2 第三次触发
│         - 打印 "Restarting timer"
│         - 再次重启
│
│ (继续周期触发直到被主线程停止)
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 工作队列定时器 (timer_work)                       |
| 延迟: 2000ms                                     |
+--------------------------------------------------+
│
│ 0ms:   k_work_schedule() 调用
│
│ 2000ms: 工作项触发
│         调用 timer_work_handler()
│         - 打印 "Delayed work executed at X ms"
│
│ (单次执行后结束)
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 管理线程 (timer_thread)                           |
| 周期: 2000ms                                     |
+--------------------------------------------------+
│
│ 启动后打印 "Timer management thread started"
│
│ 第一次迭代 (2秒):
│   - 打印当前迭代次数和运行时间
│
│ 第二次迭代 (4秒):
│   - 打印迭代次数和运行时间
│
│ 第三次迭代 (6秒):
│   - 打印迭代次数和运行时间
│   - 计算并打印启动以来经过的时间
│
│ 第四次迭代 (8秒):
│   - 打印迭代次数和运行时间
│   - 检查 timer1 和 timer2 的挂起状态
│
│ 第五次迭代 (10秒):
│   - 打印迭代次数和运行时间
│   - 获取 timer1 和 timer2 的剩余时间
│
│ (主线程停止后,线程继续执行)
│
+--------------------------------------------------+
​
+--------------------------------------------------+
| 高精度定时器 (hrt_timer)                         |
| 周期: 100us (微秒)                              |
+--------------------------------------------------+
│
│ 启动后每 100us 触发一次
│ 触发时调用 hrt_timer_callback()
│ - 打印 "High-resolution timer trigger X"
│
│ (频率较高,打印信息可能很快)
│
+--------------------------------------------------+

4. 关键 API 注解

API 功能 注解
K_TIMER_DEFINE(name) 静态定义定时器 /**
  • @def K_TIMER_DEFINE(name)

  • @brief 静态初始化定时器

  • @param name 定时器名称 */ | | k_timer_init(timer, callback, user_data) | 初始化定时器 | /**

  • @fn void k_timer_init(struct k_timer *timer, k_timer_handler_t callback, void *user_data)

  • @brief 初始化定时器

  • @param timer 定时器指针

  • @param callback 回调函数(可空)

  • @param user_data 用户数据 */ | | k_timer_start(timer, duration, period) | 启动定时器 | /**

  • @fn void k_timer_start(struct k_timer *timer, k_timeout_t duration, k_timeout_t period)

  • @brief 启动定时器

  • @param timer 定时器指针

  • @param duration 首次延迟

  • @param period 周期(K_NO_WAIT 表示单次) */ | | k_timer_stop(timer) | 停止定时器 | /**

  • @fn void k_timer_stop(struct k_timer *timer)

  • @brief 停止定时器

  • @param timer 定时器指针 */ | | k_timer_stop_sync(timer) | 同步停止定时器 | /**

  • @fn void k_timer_stop_sync(struct k_timer *timer)

  • @brief 同步停止定时器(等待定时器完全停止)

  • @param timer 定时器指针 */ | | k_timer_is_pending(timer) | 检查定时器是否在等待 | /**

  • @fn int k_timer_is_pending(struct k_timer *timer)

  • @brief 检查定时器是否已启动并在等待

  • @param timer 定时器指针

  • @return 1 等待中,0 未等待 */ | | k_timer_remaining_get(timer) | 获取定时器剩余时间 | /**

  • @fn k_ticks_t k_timer_remaining_get(struct k_timer *timer)

  • @brief 获取定时器剩余时间(滴答数)

  • @param timer 定时器指针

  • @return 剩余滴答数 */ | | k_uptime_get() | 获取系统运行时间 | /**

  • @fn int64_t k_uptime_get(void)

  • @brief 获取系统运行时间(毫秒)

  • @return 运行时间(毫秒) */ | | k_sleep(timeout) | 线程延时 | /**

  • @fn int k_sleep(k_timeout_t timeout)

  • @brief 当前线程延时指定的时间

  • @param timeout 延时时间

  • @return 0 正常,-EINTR 被中断 */ | | k_work_schedule(work, delay) | 调度延迟工作 | /**

  • @fn int k_work_schedule(struct k_work_delayable *work, k_timeout_t delay)

  • @brief 调度延迟工作项

  • @param work 工作项指针

  • @param delay 延迟时间

  • @return 0 成功,负数错误码 */ |

5. 项目配置(prj.conf)

# 启用定时器相关功能
CONFIG_TIMER=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
​
# 启用高精度定时器(如果硬件支持)
CONFIG_HW_TIMER=y
​
# 启用工作队列
CONFIG_WORK=y
​
# 启用系统时间管理
CONFIG_UTIME=y
CONFIG_UPTIME=y
​
# 启用标准输出
CONFIG_PRINTK=y
​
# 设置线程栈大小
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_IDLE_STACK_SIZE=1024

6. 总结

  • 核心流程

    • 定时器创建:使用 K_TIMER_DEFINEk_timer_init 创建定时器。

    • 定时器启动:使用 k_timer_start 启动,支持单次或周期性触发。

    • 定时器停止:使用 k_timer_stopk_timer_stop_sync 停止。

    • 定时器状态查询:使用 k_timer_is_pendingk_timer_remaining_get 检查状态。

    • 时间管理:使用 k_uptime_get 获取系统运行时间。

    • 工作队列集成:使用 k_work_schedule 在定时器触发时执行延迟工作。

  • Zephyr 特色

    • 定时器运行在系统工作队列上下文,回调函数应快速执行。

    • 支持微秒级高精度定时器(需要硬件支持)。

    • 提供 k_timer_stop_sync 用于同步停止定时器,确保定时器完全停止。

    • 定时器对象可静态分配(K_TIMER_DEFINE),减少运行时开销。

  • 调试方式

    • 通过 printk 输出定时器触发信息。

    • 使用 k_timer_is_pending 检查定时器状态。

    • 使用 k_timer_remaining_get 查看剩余时间。

  • 扩展方向

    • 使用 k_timer_user_data_setk_timer_user_data_get 管理用户数据。

    • 使用 k_timer_expiration_remaining 获取下次触发剩余时间。

    • 使用 k_timer_stop_sync 确保定时器完全停止。

    • 在定时器回调中启动其他定时器或工作项。

这个示例完整展示了 Zephyr 定时器和时间管理的核心功能,是开发周期性任务、超时控制和实时应用的基础。

第七部分 Zephyr 官方示例:fatal(致命错误处理)

1. 文件结构

samples/basic/fatal/
├── src/
│   └── main.c               # 主程序源码
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr 致命错误处理示例程序
 *
 * 该程序演示了 Zephyr 系统如何处理各种致命错误:
 * - 断言失败(ASSERT)
 * - 未捕获的异常(如除以零、非法指令)
 * - 堆栈溢出检测
 * - 内核错误(k_panic)
 * - 自定义致命错误处理程序
 * - 错误信息的记录与输出
 */
​
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/fatal.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/toolchain.h>
​
/* ============================================================ */
/* 1. 致命错误处理程序                                           */
/* ============================================================ */
​
/**
 * @brief 自定义致命错误处理程序
 *
 * 该函数在发生致命错误时被调用,可以用于记录错误信息、
 * 保存现场、触发恢复机制或复位系统。
 *
 * @param reason 错误原因(枚举值)
 * @param esf    异常栈帧指针(包含 CPU 寄存器状态)
 */
void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf)
{
    printk("!!! Fatal error handler triggered !!!\n");
    printk("  Reason: ");
​
    switch (reason) {
    case K_ERR_CPU_EXCEPTION:
        printk("CPU Exception\n");
        break;
    case K_ERR_STACK_CHK_FAIL:
        printk("Stack check failure (overflow)\n");
        break;
    case K_ERR_KERNEL_PANIC:
        printk("Kernel panic\n");
        break;
    case K_ERR_ASSERTION:
        printk("Assertion failure\n");
        break;
    case K_ERR_OOPS:
        printk("OOPS\n");
        break;
    case K_ERR_VM_FAULT:
        printk("VM fault (memory access violation)\n");
        break;
    default:
        printk("Unknown (0x%x)\n", reason);
        break;
    }
​
    /* 输出寄存器状态(如果有) */
    if (esf != NULL) {
        printk("  CPU Registers:\n");
        /* 架构相关:ARM Cortex-M 寄存器输出示例 */
        #if defined(CONFIG_CPU_CORTEX_M)
        printk("    R0: 0x%08x\n", esf->basics.r0);
        printk("    R1: 0x%08x\n", esf->basics.r1);
        printk("    SP: 0x%08x\n", esf->basics.sp);
        printk("    LR: 0x%08x\n", esf->basics.lr);
        printk("    PC: 0x%08x\n", esf->basics.pc);
        printk("    xPSR: 0x%08x\n", esf->basics.xpsr);
        #endif
    }
​
    /* 可在此处执行恢复或复位 */
    printk("  System will halt.\n");
    while (1) {
        __asm__ volatile ("wfi");
    }
}
​
/* ============================================================ */
/* 2. 触发致命错误的函数定义                                     */
/* ============================================================ */
​
/**
 * @brief 触发断言失败
 *
 * 演示如何触发 `K_ERR_ASSERTION` 类型的致命错误。
 */
void trigger_assertion(void)
{
    printk("Going to trigger an assertion...\n");
    /* 使用 __ASSERT 宏触发断言失败 */
    __ASSERT(0, "This is a deliberate assertion failure");
}
​
/**
 * @brief 触发异常(除以零)
 *
 * 演示如何触发 `K_ERR_CPU_EXCEPTION` 类型的致命错误。
 */
void trigger_exception(void)
{
    volatile int a = 1;
    volatile int b = 0;
    volatile int c;
​
    printk("Going to trigger an exception (divide by zero)...\n");
    /* 除以零操作,在大多数架构上触发 CPU 异常 */
    c = a / b;
    printk("This line should never be reached. c = %d\n", c);
}
​
/**
 * @brief 触发堆栈溢出
 *
 * 演示如何触发 `K_ERR_STACK_CHK_FAIL` 类型的致命错误。
 * 通过递归函数消耗大量栈空间。
 */
void trigger_stack_overflow(void)
{
    static int recursion_count = 0;
    char buffer[256];  /* 在栈上分配大量内存 */
​
    printk("Stack overflow recursion: %d\n", recursion_count++);
    /* 递归调用,每次消耗更多栈空间 */
    trigger_stack_overflow();
}
​
/**
 * @brief 触发内核恐慌
 *
 * 演示如何触发 `K_ERR_KERNEL_PANIC` 类型的致命错误。
 */
void trigger_panic(void)
{
    printk("Going to trigger kernel panic...\n");
    k_panic();
}
​
/**
 * @brief 触发 OOPS
 *
 * 演示如何触发 `K_ERR_OOPS` 类型的致命错误。
 */
void trigger_oops(void)
{
    printk("Going to trigger OOPS...\n");
    k_oops();
}
​
/**
 * @brief 触发内存访问违规(VM Fault)
 *
 * 演示如何触发 `K_ERR_VM_FAULT` 类型的致命错误。
 * 访问无效内存地址。
 */
void trigger_vm_fault(void)
{
    uint32_t *bad_addr = (uint32_t *)0xFFFFFFFF;
    uint32_t value;
​
    printk("Going to trigger VM fault...\n");
    /* 访问无效内存地址 */
    value = *bad_addr;
    printk("This line should never be reached. value = 0x%08x\n", value);
}
​
/* ============================================================ */
/* 3. 线程函数定义                                               */
/* ============================================================ */
​
#define STACK_SIZE 1024
​
/**
 * @brief 演示线程函数(触发各种致命错误)
 *
 * 根据参数执行不同的错误触发函数。
 *
 * @param arg1 错误类型选择 (0-5)
 * @param arg2 线程参数(未使用)
 * @param arg3 线程参数(未使用)
 */
void fatal_thread(void *arg1, void *arg2, void *arg3)
{
    int error_type = (int)(uintptr_t)arg1;
​
    printk("Fatal thread started, error type = %d\n", error_type);
​
    k_sleep(K_SECONDS(1));
​
    switch (error_type) {
    case 0:
        trigger_assertion();
        break;
    case 1:
        trigger_exception();
        break;
    case 2:
        trigger_stack_overflow();
        break;
    case 3:
        trigger_panic();
        break;
    case 4:
        trigger_oops();
        break;
    case 5:
        trigger_vm_fault();
        break;
    default:
        printk("Unknown error type\n");
        break;
    }
​
    /* 如果错误被处理,程序不应执行到这里 */
    printk("Error handled? This line should not be reached.\n");
}
​
/* 线程栈与句柄 */
K_THREAD_STACK_DEFINE(fatal_thread_stack, STACK_SIZE);
struct k_thread fatal_thread_data;
​
/* ============================================================ */
/* 4. 系统主入口                                                 */
/* ============================================================ */
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责:
 * 1. 注册自定义致命错误处理程序
 * 2. 创建线程并演示不同类型的致命错误
 * 3. 展示错误信息的输出
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    int error_type = 0;
​
    printk("Zephyr Fatal Error Handling Example\n");
    printk("=====================================\n");
​
    /* 1. 注册自定义致命错误处理程序(覆盖默认处理程序) */
    printk("[Main] Registering custom fatal error handler\n");
    k_sys_fatal_error_handler_register(NULL);  /* 使用自定义处理程序 */
​
    /* 2. 演示不同错误类型(通过修改 error_type 值) */
    printk("[Main] Select error type (0-5):\n");
    printk("  0: Assertion\n");
    printk("  1: CPU Exception\n");
    printk("  2: Stack overflow\n");
    printk("  3: Kernel panic\n");
    printk("  4: OOPS\n");
    printk("  5: VM Fault\n");
    printk("[Main] Creating thread with error type %d\n", error_type);
​
    /* 3. 创建错误触发线程 */
    k_thread_create(&fatal_thread_data, fatal_thread_stack, STACK_SIZE,
                    fatal_thread, (void *)(uintptr_t)error_type, NULL, NULL,
                    5, 0, K_NO_WAIT);
​
    printk("[Main] Thread created. Waiting for fatal error...\n");
​
    /* 4. 主线程进入循环(等待错误发生) */
    while (1) {
        k_sleep(K_SECONDS(5));
        printk("[Main] System still running (error not yet triggered)\n");
    }
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印标题信息
    │
    │ 2. 注册自定义致命错误处理程序
    │    - `k_sys_fatal_error_handler_register(NULL)`
    │    - 默认处理程序被替换为 `k_sys_fatal_error_handler`
    │
    │ 3. 打印错误类型选择菜单
    │
    │ 4. 创建错误触发线程 (fatal_thread)
    │    - 参数: error_type = 0(默认)
    │    - 优先级: 5
    │    - 栈大小: 1024
    │
    │ 5. 进入无限循环,等待错误发生
    │    - 每5秒打印状态信息
    │
    ▼
[错误触发线程 fatal_thread()]
    │
    │ 1. 打印启动信息和错误类型
    │
    │ 2. 休眠 1 秒
    │
    │ 3. 根据 error_type 触发对应错误:
    │
    │    ├── [error_type == 0] 触发断言失败
    │    │    - `trigger_assertion()`
    │    │    - 调用 `__ASSERT(0, "...")`
    │    │    - 触发 K_ERR_ASSERTION
    │    │
    │    ├── [error_type == 1] 触发 CPU 异常 (除以零)
    │    │    - `trigger_exception()`
    │    │    - 执行 `a / b` (b == 0)
    │    │    - 触发 K_ERR_CPU_EXCEPTION
    │    │
    │    ├── [error_type == 2] 触发堆栈溢出
    │    │    - `trigger_stack_overflow()`
    │    │    - 递归调用,每次分配 256 字节栈空间
    │    │    - 触发 K_ERR_STACK_CHK_FAIL
    │    │
    │    ├── [error_type == 3] 触发内核恐慌
    │    │    - `trigger_panic()`
    │    │    - 调用 `k_panic()`
    │    │    - 触发 K_ERR_KERNEL_PANIC
    │    │
    │    ├── [error_type == 4] 触发 OOPS
    │    │    - `trigger_oops()`
    │    │    - 调用 `k_oops()`
    │    │    - 触发 K_ERR_OOPS
    │    │
    │    └── [error_type == 5] 触发 VM Fault
    │         - `trigger_vm_fault()`
    │         - 访问无效地址 0xFFFFFFFF
    │         - 触发 K_ERR_VM_FAULT
    │
    ▼
[内核捕获致命错误]
    │
    │ 1. 内核检测到错误,暂停当前线程
    │
    │ 2. 调用注册的致命错误处理程序
    │    - `k_sys_fatal_error_handler(reason, esf)`
    │
    ▼
[致命错误处理程序 k_sys_fatal_error_handler()]
    │
    │ 1. 打印 "Fatal error handler triggered"
    │
    │ 2. 根据 reason 输出错误类型
    │
    │ 3. 输出 CPU 寄存器状态(如果 esf 非空)
    │    - R0, R1, SP, LR, PC, xPSR (ARM Cortex-M)
    │
    │ 4. 打印 "System will halt."
    │
    │ 5. 进入无限循环 (wfi)
    │
    ▼
[系统停止]
    │
    │ 系统进入停滞状态,等待复位或调试器介入
    │
    │ [可以通过看门狗复位或外部复位恢复]
    │
    ▼
[结束]

4. 关键 API 注解

API 功能 注解
k_sys_fatal_error_handler_register(handler) 注册致命错误处理程序 /**
  • @fn void k_sys_fatal_error_handler_register(k_sys_fatal_error_handler_t handler)

  • @brief 注册致命错误处理程序

  • @param handler 处理程序指针(NULL 表示使用默认处理程序) */ | | k_panic() | 触发内核恐慌 | /**

  • @fn void k_panic(void)

  • @brief 触发内核恐慌,系统停止运行 */ | | k_oops() | 触发 OOPS(轻量级错误) | /**

  • @fn void k_oops(void)

  • @brief 触发 OOPS 错误,用于调试 */ | | __ASSERT(expr, msg) | 断言宏 | /**

  • @def __ASSERT(expr, msg)

  • @brief 断言检查,失败时触发致命错误

  • @param expr 条件表达式

  • @param msg 错误消息 */ | | K_ERR_* | 错误原因枚举 | /**

  • @enum k_fatal_error_reason

  • @brief 致命错误原因枚举

  • @var K_ERR_CPU_EXCEPTION CPU 异常

  • @var K_ERR_STACK_CHK_FAIL 堆栈检测失败

  • @var K_ERR_KERNEL_PANIC 内核恐慌

  • @var K_ERR_ASSERTION 断言失败

  • @var K_ERR_OOPS OOPS

  • @var K_ERR_VM_FAULT 虚拟内存错误 */ |

5. 项目配置(prj.conf)

# 启用致命错误处理
CONFIG_FATAL=y
CONFIG_FAULT_DUMP=y
CONFIG_FAULT_DUMP_STACK=y
​
# 启用断言支持
CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2
CONFIG_ASSERT_DEBUG=y
​
# 启用堆栈溢出检测
CONFIG_STACK_SENTINEL=y
CONFIG_STACK_CANARIES=y
​
# 启用异常处理
CONFIG_EXCEPTION_STACK_SIZE=4096
​
# 启用调试符号
CONFIG_DEBUG=y
​
# 启用标准输出
CONFIG_PRINTK=y
​
# 设置线程栈大小
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_IDLE_STACK_SIZE=1024

6. 总结

  • 核心流程

    • 错误触发:通过断言、异常、堆栈溢出、内核恐慌等方式触发错误。

    • 错误捕获:内核检测到错误后暂停当前线程,调用注册的错误处理程序。

    • 错误处理:处理程序记录错误信息、输出寄存器状态、执行恢复或停止系统。

    • 系统停止:默认行为是停止系统,等待复位。

  • Zephyr 特色

    • 通过 k_sys_fatal_error_handler_register 自定义处理程序,支持高级错误恢复。

    • 提供多种错误类型区分(K_ERR_* 枚举),便于针对性处理。

    • 支持 stack sentinelstack canaries 堆栈溢出检测。

    • 支持 k_panick_oops 两种严重级别的错误。

  • 调试方式

    • 通过自定义处理程序输出错误信息。

    • 使用 fault_dump 输出异常栈帧和寄存器状态。

    • 使用调试器(JTAG)捕获错误后的现场。

    • 通过 assert 宏在开发时检测逻辑错误。

  • 扩展方向

    • 实现错误恢复机制(如重启系统、回滚到安全模式)。

    • 记录错误日志到持久化存储。

    • 通过看门狗定时器自动复位。

    • 实现远程错误报告。

这个示例展示了 Zephyr 系统如何捕捉和处理致命错误,是开发安全可靠嵌入式系统的基础。通过自定义错误处理程序,可以实现故障恢复、日志记录和系统监控。

第八部分 Zephyr 官方示例:fs(文件系统)

1. 文件结构

samples/subsys/fs/
├── src/
│   └── main.c               # 主程序源码
├── boards/                  # 板级覆盖配置(可选)
├── prj.conf                 # 项目配置
├── CMakeLists.txt           # 构建脚本
└── README.rst               # 说明文档

2. 主程序源码

/**
 * @file main.c
 * @brief Zephyr 文件系统示例程序
 *
 * 该程序演示了 Zephyr 文件系统的核心功能:
 * - 文件系统挂载(FatFS / LittleFS)
 * - 文件创建与写入
 * - 文件读取
 * - 文件截断与重命名
 * - 目录操作(创建、读取、删除)
 * - 文件状态查询
 * - 错误处理
 */
​
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/fs/fat_fs.h>
#include <zephyr/sys/printk.h>
#include <zephyr/storage/disk_access.h>
​
/* ============================================================ */
/* 1. 文件系统配置与挂载点定义                                   */
/* ============================================================ */
​
/**
 * @def FS_MOUNT_POINT
 * @brief 文件系统挂载点路径
 */
#define FS_MOUNT_POINT "/littlefs"
​
/**
 * @def FS_TEST_FILE
 * @brief 测试文件路径
 */
#define FS_TEST_FILE FS_MOUNT_POINT "/test.txt"
​
/**
 * @def FS_TEST_DIR
 * @brief 测试目录路径
 */
#define FS_TEST_DIR FS_MOUNT_POINT "/test_dir"
​
/**
 * @def FS_TEST_FILE_IN_DIR
 * @brief 目录中的测试文件路径
 */
#define FS_TEST_FILE_IN_DIR FS_TEST_DIR "/file.txt"
​
/**
 * @def FS_TEST_BACKUP_FILE
 * @brief 备份文件路径
 */
#define FS_TEST_BACKUP_FILE FS_MOUNT_POINT "/backup.txt"
​
/**
 * @brief LittleFS 文件系统配置
 *
 * LittleFS 是专门为嵌入式系统设计的小型文件系统,
 * 具有低内存占用和掉电保护特性。
 */
static struct fs_mount_t littlefs_fs = {
    .type = FS_LITTLEFS,
    .fs_data = NULL,
    .storage_dev = (void *)FIXED_PARTITION_ID(littlefs_storage),
    .mount_point = FS_MOUNT_POINT,
};
​
/* ============================================================ */
/* 2. 文件系统操作函数                                           */
/* ============================================================ */
​
/**
 * @brief 创建并写入测试文件
 *
 * @return 0 成功,负值错误码
 */
static int test_file_write(void)
{
    struct fs_file_t file;
    int ret;
    const char *data = "Hello, Zephyr File System!\n";
    const char *data2 = "This is the second line.\n";
​
    printk("[FS] Creating and writing file: %s\n", FS_TEST_FILE);
​
    /* 1. 打开文件(创建或覆盖) */
    fs_file_t_init(&file);
    ret = fs_open(&file, FS_TEST_FILE, FS_O_CREATE | FS_O_WRONLY);
    if (ret < 0) {
        printk("[FS] Failed to open file: %d\n", ret);
        return ret;
    }
​
    /* 2. 写入数据 */
    ret = fs_write(&file, data, strlen(data));
    if (ret < 0) {
        printk("[FS] Failed to write to file: %d\n", ret);
        fs_close(&file);
        return ret;
    }
    printk("[FS] Written %d bytes\n", ret);
​
    /* 写入第二行数据 */
    ret = fs_write(&file, data2, strlen(data2));
    if (ret < 0) {
        printk("[FS] Failed to write to file: %d\n", ret);
        fs_close(&file);
        return ret;
    }
    printk("[FS] Written %d bytes\n", ret);
​
    /* 3. 关闭文件 */
    fs_close(&file);
    printk("[FS] File written successfully\n");
​
    return 0;
}
​
/**
 * @brief 读取测试文件内容
 *
 * @return 0 成功,负值错误码
 */
static int test_file_read(void)
{
    struct fs_file_t file;
    int ret;
    char buffer[256];
​
    printk("[FS] Reading file: %s\n", FS_TEST_FILE);
​
    /* 1. 打开文件(只读模式) */
    fs_file_t_init(&file);
    ret = fs_open(&file, FS_TEST_FILE, FS_O_RDONLY);
    if (ret < 0) {
        printk("[FS] Failed to open file: %d\n", ret);
        return ret;
    }
​
    /* 2. 读取数据 */
    ret = fs_read(&file, buffer, sizeof(buffer) - 1);
    if (ret < 0) {
        printk("[FS] Failed to read file: %d\n", ret);
        fs_close(&file);
        return ret;
    }
​
    /* 3. 添加字符串终结符并打印 */
    buffer[ret] = '\0';
    printk("[FS] Read %d bytes:\n%s\n", ret, buffer);
​
    /* 4. 关闭文件 */
    fs_close(&file);
​
    return 0;
}
​
/**
 * @brief 测试文件状态
 *
 * @return 0 成功,负值错误码
 */
static int test_file_stat(void)
{
    struct fs_stat_t stat;
    int ret;
​
    printk("[FS] Getting file status: %s\n", FS_TEST_FILE);
​
    /* 1. 获取文件状态 */
    ret = fs_stat(FS_TEST_FILE, &stat);
    if (ret < 0) {
        printk("[FS] Failed to stat file: %d\n", ret);
        return ret;
    }
​
    /* 2. 打印文件信息 */
    printk("[FS] File size: %zu bytes\n", stat.size);
    printk("[FS] File type: %s\n", (stat.type == FS_DIR_ENTRY_FILE) ? "File" : "Directory");
    printk("[FS] Access time: %lld\n", stat.atime);
    printk("[FS] Modify time: %lld\n", stat.mtime);
​
    return 0;
}
​
/**
 * @brief 创建测试目录
 *
 * @return 0 成功,负值错误码
 */
static int test_dir_create(void)
{
    int ret;
​
    printk("[FS] Creating directory: %s\n", FS_TEST_DIR);
​
    /* 1. 创建目录 */
    ret = fs_mkdir(FS_TEST_DIR);
    if (ret < 0) {
        printk("[FS] Failed to create directory: %d\n", ret);
        return ret;
    }
​
    printk("[FS] Directory created successfully\n");
​
    /* 2. 在目录中创建一个文件 */
    struct fs_file_t file;
    const char *data = "This file is inside a directory.\n";
​
    fs_file_t_init(&file);
    ret = fs_open(&file, FS_TEST_FILE_IN_DIR, FS_O_CREATE | FS_O_WRONLY);
    if (ret < 0) {
        printk("[FS] Failed to open file: %d\n", ret);
        return ret;
    }
​
    ret = fs_write(&file, data, strlen(data));
    if (ret < 0) {
        printk("[FS] Failed to write to file: %d\n", ret);
        fs_close(&file);
        return ret;
    }
​
    fs_close(&file);
    printk("[FS] File in directory written successfully\n");
​
    return 0;
}
​
/**
 * @brief 列出目录内容
 *
 * @return 0 成功,负值错误码
 */
static int test_dir_list(void)
{
    struct fs_dir_t dir;
    struct fs_dirent entry;
    int ret;
​
    printk("[FS] Listing directory: %s\n", FS_MOUNT_POINT);
​
    /* 1. 打开目录 */
    fs_dir_t_init(&dir);
    ret = fs_opendir(&dir, FS_MOUNT_POINT);
    if (ret < 0) {
        printk("[FS] Failed to open directory: %d\n", ret);
        return ret;
    }
​
    /* 2. 遍历目录项 */
    printk("[FS] Directory contents:\n");
    while (1) {
        ret = fs_readdir(&dir, &entry);
        if (ret < 0) {
            printk("[FS] Failed to read directory: %d\n", ret);
            break;
        }
        if (ret == 0) {
            break;  /* 没有更多项 */
        }
​
        printk("[FS]   %s %s (%zu bytes)\n",
               (entry.type == FS_DIR_ENTRY_DIR) ? "DIR" : "FILE",
               entry.name, entry.size);
    }
​
    /* 3. 关闭目录 */
    fs_closedir(&dir);
​
    return 0;
}
​
/**
 * @brief 重命名文件
 *
 * @return 0 成功,负值错误码
 */
static int test_file_rename(void)
{
    int ret;
​
    printk("[FS] Renaming file: %s -> %s\n", FS_TEST_FILE, FS_TEST_BACKUP_FILE);
​
    /* 1. 重命名文件 */
    ret = fs_rename(FS_TEST_FILE, FS_TEST_BACKUP_FILE);
    if (ret < 0) {
        printk("[FS] Failed to rename file: %d\n", ret);
        return ret;
    }
​
    printk("[FS] File renamed successfully\n");
​
    return 0;
}
​
/**
 * @brief 删除文件
 *
 * @return 0 成功,负值错误码
 */
static int test_file_delete(void)
{
    int ret;
​
    printk("[FS] Deleting file: %s\n", FS_TEST_BACKUP_FILE);
​
    /* 1. 删除文件 */
    ret = fs_unlink(FS_TEST_BACKUP_FILE);
    if (ret < 0) {
        printk("[FS] Failed to delete file: %d\n", ret);
        return ret;
    }
​
    printk("[FS] File deleted successfully\n");
​
    return 0;
}
​
/**
 * @brief 删除目录(递归)
 *
 * @return 0 成功,负值错误码
 */
static int test_dir_delete(void)
{
    int ret;
​
    printk("[FS] Deleting directory: %s\n", FS_TEST_DIR);
​
    /* 1. 删除目录中的文件 */
    ret = fs_unlink(FS_TEST_FILE_IN_DIR);
    if (ret < 0) {
        printk("[FS] Failed to delete file in directory: %d\n", ret);
        return ret;
    }
​
    /* 2. 删除目录 */
    ret = fs_rmdir(FS_TEST_DIR);
    if (ret < 0) {
        printk("[FS] Failed to delete directory: %d\n", ret);
        return ret;
    }
​
    printk("[FS] Directory deleted successfully\n");
​
    return 0;
}
​
/**
 * @brief 测试文件截断
 *
 * @return 0 成功,负值错误码
 */
static int test_file_truncate(void)
{
    struct fs_file_t file;
    int ret;
​
    printk("[FS] Truncating file: %s\n", FS_TEST_FILE);
​
    /* 1. 打开文件 */
    fs_file_t_init(&file);
    ret = fs_open(&file, FS_TEST_FILE, FS_O_WRONLY);
    if (ret < 0) {
        printk("[FS] Failed to open file: %d\n", ret);
        return ret;
    }
​
    /* 2. 截断到 10 字节 */
    ret = fs_truncate(&file, 10);
    if (ret < 0) {
        printk("[FS] Failed to truncate file: %d\n", ret);
        fs_close(&file);
        return ret;
    }
​
    printk("[FS] File truncated to 10 bytes\n");
​
    /* 3. 关闭文件 */
    fs_close(&file);
​
    return 0;
}
​
/* ============================================================ */
/* 3. 文件系统挂载与格式化                                       */
/* ============================================================ */
​
/**
 * @brief 格式化文件系统
 *
 * @param mount_point 挂载点
 * @return 0 成功,负值错误码
 */
static int test_fs_format(const char *mount_point)
{
    int ret;
​
    printk("[FS] Formatting file system: %s\n", mount_point);
​
    ret = fs_mkfs(FS_LITTLEFS, mount_point);
    if (ret < 0) {
        printk("[FS] Failed to format file system: %d\n", ret);
        return ret;
    }
​
    printk("[FS] File system formatted successfully\n");
    return 0;
}
​
/**
 * @brief 挂载文件系统
 *
 * @return 0 成功,负值错误码
 */
static int test_fs_mount(void)
{
    int ret;
​
    printk("[FS] Mounting file system: %s\n", littlefs_fs.mount_point);
​
    /* 1. 挂载文件系统 */
    ret = fs_mount(&littlefs_fs);
    if (ret < 0) {
        printk("[FS] Failed to mount file system: %d\n", ret);
        return ret;
    }
​
    printk("[FS] File system mounted successfully\n");
    return 0;
}
​
/**
 * @brief 卸载文件系统
 *
 * @return 0 成功,负值错误码
 */
static int test_fs_unmount(void)
{
    int ret;
​
    printk("[FS] Unmounting file system: %s\n", littlefs_fs.mount_point);
​
    /* 1. 卸载文件系统 */
    ret = fs_unmount(&littlefs_fs);
    if (ret < 0) {
        printk("[FS] Failed to unmount file system: %d\n", ret);
        return ret;
    }
​
    printk("[FS] File system unmounted successfully\n");
    return 0;
}
​
/* ============================================================ */
/* 4. 系统主入口                                                 */
/* ============================================================ */
​
/**
 * @brief 系统主入口
 *
 * Zephyr 应用的标准入口点。
 *
 * 在此示例中,`main()` 负责:
 * 1. 格式化文件系统
 * 2. 挂载文件系统
 * 3. 执行各种文件系统操作
 * 4. 卸载文件系统
 *
 * @return 无返回值(实际不会返回)
 */
void main(void)
{
    int ret;
​
    printk("Zephyr File System Example\n");
    printk("===========================\n");
​
    /* 1. 格式化文件系统(首次运行需要) */
    ret = test_fs_format(littlefs_fs.mount_point);
    if (ret < 0) {
        printk("[Main] Failed to format file system\n");
        return;
    }
​
    /* 2. 挂载文件系统 */
    ret = test_fs_mount();
    if (ret < 0) {
        printk("[Main] Failed to mount file system\n");
        return;
    }
​
    /* 3. 执行文件操作 */
    printk("[Main] ===== File Operations =====\n");
​
    /* 3.1 创建并写入文件 */
    ret = test_file_write();
    if (ret < 0) {
        printk("[Main] File write failed\n");
        goto unmount;
    }
​
    /* 3.2 读取文件 */
    ret = test_file_read();
    if (ret < 0) {
        printk("[Main] File read failed\n");
        goto unmount;
    }
​
    /* 3.3 获取文件状态 */
    ret = test_file_stat();
    if (ret < 0) {
        printk("[Main] File stat failed\n");
        goto unmount;
    }
​
    /* 3.4 创建目录并写入文件 */
    ret = test_dir_create();
    if (ret < 0) {
        printk("[Main] Directory create failed\n");
        goto unmount;
    }
​
    /* 3.5 列出目录内容 */
    ret = test_dir_list();
    if (ret < 0) {
        printk("[Main] Directory list failed\n");
        goto unmount;
    }
​
    /* 3.6 重命名文件 */
    ret = test_file_rename();
    if (ret < 0) {
        printk("[Main] File rename failed\n");
        goto unmount;
    }
​
    /* 3.7 删除文件 */
    ret = test_file_delete();
    if (ret < 0) {
        printk("[Main] File delete failed\n");
        goto unmount;
    }
​
    /* 3.8 删除目录 */
    ret = test_dir_delete();
    if (ret < 0) {
        printk("[Main] Directory delete failed\n");
        goto unmount;
    }
​
    /* 3.9 截断文件 */
    ret = test_file_truncate();
    if (ret < 0) {
        printk("[Main] File truncate failed\n");
        goto unmount;
    }
​
    /* 4. 卸载文件系统 */
unmount:
    ret = test_fs_unmount();
    if (ret < 0) {
        printk("[Main] Failed to unmount file system\n");
    }
​
    printk("[Main] File system demo completed\n");
}

3. 文字程序流程图

[系统启动]
    │
    │ 1. Zephyr 内核初始化
    │    - 初始化中断控制器、定时器、调度器
    │    - 初始化存储设备驱动
    │    - 创建主线程(`main()`)
    │
    ▼
[主线程 main()]
    │
    │ 1. 打印标题信息
    │
    │ 2. 格式化文件系统
    │    - `test_fs_format("/littlefs")`
    │    - 调用 `fs_mkfs(FS_LITTLEFS, mount_point)`
    │
    │ 3. 挂载文件系统
    │    - `test_fs_mount()`
    │    - 调用 `fs_mount(&littlefs_fs)`
    │
    │ 4. 执行文件操作序列
    │    ├── 4.1 创建并写入文件
    │    │    - `test_file_write()`
    │    │    - 打开文件 (FS_O_CREATE | FS_O_WRONLY)
    │    │    - 写入 "Hello, Zephyr File System!\n"
    │    │    - 写入 "This is the second line.\n"
    │    │    - 关闭文件
    │    │
    │    ├── 4.2 读取文件
    │    │    - `test_file_read()`
    │    │    - 打开文件 (FS_O_RDONLY)
    │    │    - 读取并打印内容
    │    │    - 关闭文件
    │    │
    │    ├── 4.3 获取文件状态
    │    │    - `test_file_stat()`
    │    │    - 调用 `fs_stat()` 获取文件信息
    │    │    - 打印大小、类型、时间戳
    │    │
    │    ├── 4.4 创建目录并写入文件
    │    │    - `test_dir_create()`
    │    │    - 调用 `fs_mkdir()` 创建目录
    │    │    - 在目录中创建并写入文件
    │    │    - 关闭文件
    │    │
    │    ├── 4.5 列出目录内容
    │    │    - `test_dir_list()`
    │    │    - 打开目录 `fs_opendir()`
    │    │    - 遍历并打印目录项
    │    │    - 关闭目录
    │    │
    │    ├── 4.6 重命名文件
    │    │    - `test_file_rename()`
    │    │    - 调用 `fs_rename()` 重命名文件
    │    │
    │    ├── 4.7 删除文件
    │    │    - `test_file_delete()`
    │    │    - 调用 `fs_unlink()` 删除文件
    │    │
    │    ├── 4.8 删除目录
    │    │    - `test_dir_delete()`
    │    │    - 先删除目录中的文件
    │    │    - 调用 `fs_rmdir()` 删除目录
    │    │
    │    └── 4.9 截断文件
    │         - `test_file_truncate()`
    │         - 打开文件 (FS_O_WRONLY)
    │         - 调用 `fs_truncate()` 截断到 10 字节
    │         - 关闭文件
    │
    │ 5. 卸载文件系统
    │    - `test_fs_unmount()`
    │    - 调用 `fs_unmount(&littlefs_fs)`
    │
    │ 6. 打印完成信息
    │
    ▼
[结束]

4. 关键 API 注解

API 功能 注解
fs_mkfs(type, mount_point) 格式化文件系统 /**
  • @fn int fs_mkfs(enum fs_type type, const char *mount_point)

  • @brief 格式化文件系统

  • @param type 文件系统类型

  • @param mount_point 挂载点路径

  • @return 0 成功,负值错误码 */ | | fs_mount(mount) | 挂载文件系统 | /**

  • @fn int fs_mount(struct fs_mount_t *mount)

  • @brief 挂载文件系统

  • @param mount 挂载点配置

  • @return 0 成功,负值错误码 */ | | fs_unmount(mount) | 卸载文件系统 | /**

  • @fn int fs_unmount(struct fs_mount_t *mount)

  • @brief 卸载文件系统

  • @param mount 挂载点配置

  • @return 0 成功,负值错误码 */ | | fs_file_t_init(file) | 初始化文件对象 | /**

  • @fn void fs_file_t_init(struct fs_file_t *file)

  • @brief 初始化文件对象

  • @param file 文件对象指针 */ | | fs_open(file, path, flags) | 打开文件 | /**

  • @fn int fs_open(struct fs_file_t *file, const char *path, fs_flags_t flags)

  • @brief 打开文件

  • @param file 文件对象指针

  • @param path 文件路径

  • @param flags 访问标志

  • @return 0 成功,负值错误码 */ | | fs_close(file) | 关闭文件 | /**

  • @fn int fs_close(struct fs_file_t *file)

  • @brief 关闭文件

  • @param file 文件对象指针

  • @return 0 成功,负值错误码 */ | | fs_write(file, data, len) | 写入文件 | /**

  • @fn ssize_t fs_write(struct fs_file_t *file, const void *data, size_t len)

  • @brief 写入文件

  • @param file 文件对象指针

  • @param data 数据指针

  • @param len 数据长度

  • @return 写入字节数,负值错误码 */ | | fs_read(file, data, len) | 读取文件 | /**

  • @fn ssize_t fs_read(struct fs_file_t *file, void *data, size_t len)

  • @brief 读取文件

  • @param file 文件对象指针

  • @param data 缓冲区指针

  • @param len 读取长度

  • @return 读取字节数,负值错误码 */ | | fs_stat(path, stat) | 获取文件状态 | /**

  • @fn int fs_stat(const char *path, struct fs_stat_t *stat)

  • @brief 获取文件状态

  • @param path 文件路径

  • @param stat 状态结构指针

  • @return 0 成功,负值错误码 */ | | fs_mkdir(path) | 创建目录 | /**

  • @fn int fs_mkdir(const char *path)

  • @brief 创建目录

  • @param path 目录路径

  • @return 0 成功,负值错误码 */ | | fs_opendir(dir, path) | 打开目录 | /**

  • @fn int fs_opendir(struct fs_dir_t *dir, const char *path)

  • @brief 打开目录

  • @param dir 目录对象指针

  • @param path 目录路径

  • @return 0 成功,负值错误码 */ | | fs_readdir(dir, entry) | 读取目录项 | /**

  • @fn int fs_readdir(struct fs_dir_t *dir, struct fs_dirent *entry)

  • @brief 读取目录项

  • @param dir 目录对象指针

  • @param entry 目录项结构指针

  • @return 1 有项,0 无项,负值错误码 */ | | fs_closedir(dir) | 关闭目录 | /**

  • @fn int fs_closedir(struct fs_dir_t *dir)

  • @brief 关闭目录

  • @param dir 目录对象指针

  • @return 0 成功,负值错误码 */ | | fs_rename(old, new) | 重命名文件 | /**

  • @fn int fs_rename(const char *old_path, const char *new_path)

  • @brief 重命名文件

  • @param old_path 源路径

  • @param new_path 目标路径

  • @return 0 成功,负值错误码 */ | | fs_unlink(path) | 删除文件 | /**

  • @fn int fs_unlink(const char *path)

  • @brief 删除文件

  • @param path 文件路径

  • @return 0 成功,负值错误码 */ | | fs_rmdir(path) | 删除目录 | /**

  • @fn int fs_rmdir(const char *path)

  • @brief 删除目录

  • @param path 目录路径

  • @return 0 成功,负值错误码 */ | | fs_truncate(file, size) | 截断文件 | /**

  • @fn int fs_truncate(struct fs_file_t *file, off_t size)

  • @brief 截断文件

  • @param file 文件对象指针

  • @param size 新文件大小

  • @return 0 成功,负值错误码 */ |

5. 项目配置(prj.conf)

# 启用 LittleFS 文件系统
CONFIG_FILE_SYSTEM=y
CONFIG_FS_LITTLEFS=y
CONFIG_LITTLEFS_USE_SHELL=y
​
# 启用 FatFS(可选)
# CONFIG_FS_FATFS=y
# CONFIG_FATFS_USE_SHELL=y
​
# 启用文件系统操作
CONFIG_FS_API=y
CONFIG_FS_OPEN_FILE=y
CONFIG_FS_OPEN_DIR=y
​
# 启用存储设备
CONFIG_DISK_ACCESS=y
CONFIG_DISK_ACCESS_FLASH=y
​
# 启用调试输出
CONFIG_PRINTK=y
​
# 启用 Shell 命令(用于手动操作)
CONFIG_SHELL=y
CONFIG_FS_SHELL=y

6. 总结

  • 核心流程

    • 挂载:格式化文件系统后,通过 fs_mount 挂载。

    • 文件操作:打开/关闭、读写、截断、重命名、删除。

    • 目录操作:创建、遍历、删除。

    • 状态查询:获取文件大小、类型、时间戳。

    • 卸载:操作完成后通过 fs_unmount 卸载。

  • Zephyr 特色

    • 统一的文件系统 API(支持 LittleFS、FatFS 等)。

    • fs_file_tfs_dir_t 对象需要初始化。

    • FS_O_* 标志与标准文件系统兼容(CREATE、WRONLY、RDONLY)。

    • 支持通过 Shell 手动操作文件系统。

  • 调试方式

    • 通过 printk 输出操作日志。

    • 使用 fs_stat 检查文件状态。

    • 使用 fs_readdir 遍历目录查看文件列表。

    • 通过 Shell 命令手动操作文件系统。

  • 扩展方向

    • 使用 FatFS 替代 LittleFS,支持 SD 卡。

    • 实现文件系统的加密存储。

    • 使用 fs_rename 实现原子更新。

    • 使用 fs_truncate 实现日志轮转。

    • 使用 fs_sync 强制刷新数据到存储设备。

这个示例完整展示了 Zephyr 文件系统的核心功能,是开发需要存储数据的嵌入式应用的基础。通过文件系统,可以实现日志记录、配置存储、固件更新等高级功能。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐