Zephyr 操作系统侧重于资源受限的嵌入式设备
第一部分 Zephyr (西风)操作系统
一、Zephyr 操作系统核心特点
-
高度模块化与可配置性:通过 Kconfig 系统(类似于 Linux 内核的配置方式)和设备树(Devicetree),开发者可以精细裁剪功能,构建出极小或功能丰富的系统。
-
多架构支持:支持 ARM (Cortex-M/A/R)、x86、RISC-V、ARC、Xtensa 等多种架构,并提供了统一的 API 接口。
-
强大的设备驱动模型 (Device Driver Model):基于设备树的驱动框架,实现了驱动与硬件的解耦,增加了代码的可移植性和复用性。
-
内置实时内核:提供了抢占式多任务调度、线程同步(互斥锁、信号量、消息队列)和中断管理。
-
内存保护与安全:
-
用户空间:支持将线程分为用户模式和内核模式,防止用户线程破坏系统核心。
-
MPU/MMU 支持:对支持 MPU 的 Cortex-M 或 MMU 的 Cortex-A/R 处理器提供硬件内存保护。
-
-
网络协议栈:集成了 Zephyr 原生 TCP/IP 栈 (Z-NET),并支持蓝牙、802.15.4、LoRaWAN、以太网等多种有线/无线通信协议。
-
可移植中间件:支持大量中间件,如 LWM2M、MQTT、CoAP、USB 堆栈、文件系统(FatFS, LittleFS)和加密子系统(Crypto API)。
-
调试与测试:内置了丰富的调试工具(
ztest测试框架、trace、fatal 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() <-- 调用具体加密引擎驱动
总结
-
模块化:通过 Kconfig 和 CMake,Zephyr 实现了高度的模块化和可裁剪性,为不同资源限制的设备提供了灵活的构建方案。
-
标准接口:内核、驱动、子系统通过统一的 API 进行抽象,极大地降低了跨平台移植的难度。
-
功能丰富:Zephyr 不仅是一个 RTOS,还提供了完整的网络、蓝牙、USB 和文件系统解决方案,是一个功能完备的物联网操作系统平台。
-
工具链完善: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_FOREVER和K_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_suspend和k_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_DEFINE或k_timer_init创建定时器。 -
定时器启动:使用
k_timer_start启动,支持单次或周期性触发。 -
定时器停止:使用
k_timer_stop或k_timer_stop_sync停止。 -
定时器状态查询:使用
k_timer_is_pending和k_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_set和k_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 sentinel和stack canaries堆栈溢出检测。 -
支持
k_panic和k_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_t和fs_dir_t对象需要初始化。 -
FS_O_*标志与标准文件系统兼容(CREATE、WRONLY、RDONLY)。 -
支持通过 Shell 手动操作文件系统。
-
-
调试方式:
-
通过
printk输出操作日志。 -
使用
fs_stat检查文件状态。 -
使用
fs_readdir遍历目录查看文件列表。 -
通过 Shell 命令手动操作文件系统。
-
-
扩展方向:
-
使用 FatFS 替代 LittleFS,支持 SD 卡。
-
实现文件系统的加密存储。
-
使用
fs_rename实现原子更新。 -
使用
fs_truncate实现日志轮转。 -
使用
fs_sync强制刷新数据到存储设备。
-
这个示例完整展示了 Zephyr 文件系统的核心功能,是开发需要存储数据的嵌入式应用的基础。通过文件系统,可以实现日志记录、配置存储、固件更新等高级功能。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)