
2026 年再聊嵌入式实时操作系统Zephyr 已经不算“新面孔”但很多从 FreeRTOS 或裸机开发转过来的朋友初次接触 west、Kconfig、设备树这套组合时依然会有明显的门槛感。这篇文章是 Higgsfield 原创系列2026的特别篇我把 Zephyr 的完整学习路径拆开来讲从环境搭建、核心配置系统、选型对比一直到一个可运行的多线程示例尽量让零基础读者也能跟着把工程跑起来。这篇文章适合以下读者正在做嵌入式项目选型的技术负责人从 FreeRTOS 迁移到 Zephyr 的应用开发者以及刚接触 Zephyr 但对 west 和 Kconfig 一脸茫然的新手。读完你会理解 Zephyr 为什么这样设计、如何搭建一套可用的开发环境、如何在真实项目中配置并编译一个多线程应用以及在 2026 年做嵌入式项目时Zephyr 和 FreeRTOS 到底应该怎么选。1. 背景Zephyr 是什么为什么 2026 年值得关注1.1 Zephyr 的定位Zephyr 是一个由 Linux Foundation 托管的开源嵌入式实时操作系统采用 Apache 2.0 许可证。它一开始的目标就不是做一个“小而美”的 RTOS 内核而是提供一套完整的物联网嵌入式软件平台包括线程调度、内存管理、设备驱动模型、电源管理、蓝牙、Wi-Fi、传感器、显示、文件系统和网络协议栈等子系统。用一句话概括Zephyr 更像是“面向 MCU 的 Linux-like 系统”它不是简单的任务调度器而是一个模块化、可裁剪、跨架构的软件平台。它支持 ARM、RISC-V、x86、Xtensa、ARC 等多种 CPU 架构这也是很多芯片厂商在 2026 年逐步把官方 SDK 转向 Zephyr 的原因之一。1.2 Zephyr 解决什么问题传统 RTOS 开发中工程师通常需要自己适配外设驱动项目换一颗 MCU 后驱动层可能要重写。Zephyr 通过统一的设备驱动模型和 Devicetree 硬件描述机制把“板级硬件差异”和“应用逻辑”尽可能分离。应用代码不必关心具体寄存器地址和管脚号而是通过设备树节点和 API 操作外设。Zephyr 还解决了“多仓库多组件”的版本管理问题。Zephyr 内核、硬件抽象层、第三方库、应用代码通过 west 工具统一管理manifest 文件锁定版本这在大型团队和产品化项目中非常实用。1.3 常见应用场景Zephyr 的典型应用场景包括智能穿戴设备、工业控制器、智能家居网关、BMS 电池管理、医疗设备、跟踪器、传感器采集节点等。它尤其适合需要蓝牙连接、低功耗管理、网络通信并且希望在不同芯片平台之间复用的产品。2. Zephyr 环境搭建从零开始Zephyr 环境搭建是新手的第一道坎。它不像 Keil 或 STM32CubeIDE 那样安装一个软件就能开发而是需要组合 Python、CMake、Ninja、west、交叉编译工具链等多个组件。下面以 Ubuntu 22.04 / 24.04 环境为例演示一套完整搭建流程。2.1 安装基础依赖先安装系统级依赖工具。Zephyr 构建依赖 CMake 和 Ninja设备树编译依赖 device-tree-compiler代码格式化依赖 gperf下载烧录依赖 dfu-util另外还需要 Python 工具链。sudo apt update sudo apt install -y git cmake ninja-build gperf \ ccache dfu-util device-tree-compiler wget \ python3-dev python3-pip python3-setuptools \ python3-tk python3-wheel xz-utils file make gcc这里有几个容易遗漏的包device-tree-compiler在编译设备树时必需gperf用于生成哈希表代码dfu-util用于 Nordic 等芯片的 DFU 烧录。如果编译时出现“dtc not found”或“gperf not found”多半是这些包没装。2.2 安装 west 与获取源码west 是 Zephyr 的官方多仓库管理工具使用 Python 编写。它会根据 manifest 文件拉取 Zephyr 内核、hal、第三方模块等指定版本的源码。pip3 install west west --version如果west命令找不到通常是因为用户级 Python 包目录未加入 PATH可以执行export PATH$HOME/.local/bin:$PATH然后创建工程目录并初始化mkdir zephyrproject cd zephyrproject west init west updatewest init默认使用官方 manifest 仓库。执行后目录中会出现.west目录和zephyr目录west update会继续拉取 west.yml 中声明的所有模块这一步骤取决于网络环境耗时可能较长。2.3 安装 Python 依赖进入 Zephyr 仓库目录安装 Zephyr 构建脚本所需的 Python 依赖cd zephyrproject pip3 install -r zephyr/scripts/requirements.txt这一步会让 west、pyelftools、pyserial 等工具版本统一。Zephyr 对 Python 版本有一定要求一般 Python 3.8 以上都可以正常工作。2.4 安装交叉编译工具链Zephyr 官方强烈推荐使用 Zephyr SDK它包含针对多种架构的 GCC 交叉编译器、binutils、newlib、QEMU 模拟器等工具。下载地址在 Zephyr 官网的 Download 页面需要选择与当前版本匹配的 SDK 包。解压后建议放在固定路径例如/opt下tar xvf zephyr-sdk-版本号.tar.xz mv zephyr-sdk-版本号 /opt/然后设置环境变量export ZEPHYR_SDK_INSTALL_DIR/opt/zephyr-sdk-版本号也可以把这一行写入~/.bashrc或~/.zshrc避免每次打开终端重新配置。如果不想下载完整 SDK某些编译目标也可以使用系统自带工具链但 Zephyr SDK 能保证版本一致减少兼容问题。2.5 验证环境是否可用用 qemu_x86 目标编译官方 hello_world 示例验证环境cd zephyr west build -b qemu_x86 samples/hello_world west build -t run如果能看到Hello World! qemu_x86输出说明 west、CMake、Ninja、工具链全部正常。这样一套 Zephyr 环境搭建就完成了。3. 核心设计Kconfig、Devicetree 与 west 构建体系Zephyr 和传统 RTOS 最大的区别在于它引入了 Linux 生态里常用的 Kconfig 和 Devicetree 两套机制。理解这两套机制基本上就理解了 Zephyr 的开发模型。3.1 Kconfig 配置系统Kconfig 是一种层级式配置系统Zephyr 用它管理内核特性、驱动开关和子系统配置。每个功能模块都会声明自己依赖哪些其他配置、默认值是什么、是否可被应用覆盖。应用层的配置入口是prj.conf里面每行一个配置项例如CONFIG_LOGy CONFIG_PRINTKy CONFIG_MAIN_STACK_SIZE4096CONFIG_LOGy表示开启日志子系统CONFIG_MAIN_STACK_SIZE4096表示主线程栈大小。不同板卡还有自己的默认配置通常存放在boards/架构/board/board_defconfig文件中。最终生效的配置是所有配置层级合并后的结果优先级从高到低大致是应用prj.conf- 板级 defconfig - 架构默认 Kconfig。初学者经常遇到“我明明在 prj.conf 写了 CONFIG_XXXy但编译后没有生效”的情况。原因往往是该配置依赖的其他配置没有打开或者配置符号名拼写错误。这时可以在构建目录中运行west build -t menuconfig会打开一个字符界面的图形化配置工具可以查看所有 Kconfig 符号的依赖关系和当前值。很多 IDE 或第三方插件也提供 Kconfig 的“工作台”Workbench可视化界面本质上就是把 Config Symbol 的修改写回配置文件理解这一点之后无论用什么工具都不会迷路。3.2 Devicetree 设备树设备树是一种描述硬件资源的机制。Zephyr 用它描述板载外设、GPIO 管脚、I2C/SPI 总线、中断号等硬件属性。设备树源文件后缀是.dts公共片段是.dtsi应用层可以通过.overlay文件对设备树进行追加或修改而不需要改动板级文件。举个例子要描述一个 GPIO LED/ { aliases { led0 led0; }; };这段 overlay 为板子增加了一个名为led0的 alias。应用代码通过DT_ALIAS(led0, gpios)获取 LED 的 GPIO 控制器和管脚号实现“硬件描述分离”。设备树虽然让新人觉得繁琐但它带来的好处非常明显应用代码不关心具体芯片引脚只关心逻辑设备换板子时只需要更换 board 目录应用层代码几乎不需要改动。3.3 west 与 CMake 构建流程Zephyr 的构建过程可以分成三层west 负责多仓库管理解析 manifest 文件拉取指定版本的依赖模块。CMake 负责生成构建脚本处理工具链选择、配置文件合并、设备树编译。Ninja 负责实际编译链接生成最终的固件文件。一个 Zephyr 应用目录通常包含app/ ├── CMakeLists.txt ├── prj.conf ├── boards/ │ └── board.overlay └── src/ └── main.cCMakeLists.txt是最基本的构建脚本cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(my_app) target_sources(app PRIVATE src/main.c)find_package(Zephyr)会加载 Zephyr 的构建系统target_sources指定应用源文件。编译时 west 将 CMake、Ninja、Kconfig、设备树等步骤串联起来west build -b board app生成的固件在build/zephyr/zephyr.bin或zephyr.hex中烧录命令是west flash3.4 线程与内核对象Zephyr 的线程模型与传统 RTOS 类似支持优先级抢占式调度。线程通过K_THREAD_DEFINE静态定义也可以使用k_thread_create动态创建。优先级数值越小优先级越高。线程之间通过内核对象通信包括信号量、互斥量、消息队列、管道、事件标志等。内核对象是 Zephyr 非常有特色的设计。比如信号量可以解决“中断到线程”的同步互斥量解决资源共享消息队列解决数据传递。在工程实践中合理使用内核对象比到处使用全局变量要安全得多。4. 2026 年嵌入式项目选型Zephyr vs FreeRTOS 深度对比Zephyr 环境搭建和基础概念讲完后很多开发者最关心的问题就是项目选型到底选 Zephyr 还是 FreeRTOS下面从多个维度做一个深度对比。对比维度ZephyrFreeRTOS许可证Apache-2.0MIT核心托管方Linux FoundationAWS 维护内核体积可裁剪但完整系统偏大极致轻量适合资源受限芯片硬件抽象设备树 统一设备驱动模型较弱依赖厂商 SDK 提供驱动配置方式Kconfig 设备树编译期配置强头文件和宏配置为主网络/蓝牙原生支持大量协议栈和子系统需要额外第三方组件多架构支持ARM / RISC-V / x86 / Xtensa 等主要面向 MCU架构支持广泛工具链CMake Ninja west现代但门槛高支持 IDE 和任意编译链上手快生态芯片厂商适配度持续提高生态成熟历史代码资源多学习曲线较陡平缓适用产品物联网、穿戴、工业、需要复杂连接的场景简单控制、资源极受限产品4.1 什么情况优先选 Zephyr如果你的产品需要蓝牙、Wi-Fi、低功耗连接、传感器管理、OTA 升级、多协议支持Zephyr 的子系统可以直接使用。Zephyr 在网络和蓝牙方面的积累明显强于 FreeRTOS 内核本身。团队如果重视代码在不同芯片平台之间的复用性Zephyr 的统一设备驱动模型能显著减少移植成本。2026 年很多芯片厂商已经在官方 SDK 中提供 Zephyr 支持Nordic、ST、NXP、乐鑫等都有对应移植。如果产品线覆盖多种供应商芯片工程师可以用同一套应用代码跑在不同硬件上这是 Zephyr 最大的长期价值。4.2 什么情况继续用 FreeRTOS如果产品只需要简单的任务调度、队列、信号量并且 Flash/RAM 非常紧张FreeRTOS 依然是优秀选择。FreeRTOS 的文档和 Demo 非常多团队招人成本低遇到问题时更容易找到参考代码。如果公司已经有大量基于 FreeRTOS 的存量代码和厂商 SDK 资产迁移到 Zephyr 的成本可能高于收益。4.3 选型建议选型不是看谁更“先进”而是看团队和产品需求。2026 年的趋势是新项目、复杂连接设备、长期维护产品Zephyr 的优势越来越明显存量项目、极简产品、快速迭代验证FreeRTOS 依然很稳。也可以采用混合策略比如先在小核芯片上用 FreeRTOS在主控 SoC 上用 Zephyr两类系统通过通信协议协同工作。5. 实战用 Zephyr 实现多线程 GPIO 控制前面理论讲得再多不如一个能跑起来的示例。下面我们用 Zephyr 创建一个应用包含两个线程一个线程控制板载 LED 闪烁另一个线程周期性打印计数日志。这个示例覆盖了 Kconfig、设备树 overlay、线程创建、GPIO API 和日志模块是 Zephyr 工程的最小完整闭环。5.1 创建项目结构假设应用放在zephyrproject目录下应用名为appzephyrproject/ └── app/ ├── CMakeLists.txt ├── prj.conf ├── boards/ │ └── nrf52840dk_nrf52840.overlay └── src/ └── main.c本文以 nRF52840 DK 为例。如果你使用其他板子请把boards目录下的文件名和west build的-b参数替换成实际板子名称。5.2 编写 prj.confCONFIG_LOGy CONFIG_PRINTKyCONFIG_LOGy打开日志子系统CONFIG_PRINTKy确保printk输出可用。日志模块是 Zephyr 推荐的输出方式支持分级打印和后台过滤比直接printk更适合工程开发。5.3 编写设备树 overlay/ { aliases { led0 led0; }; };这段 overlay 的作用是给应用层一个稳定的逻辑名称led0。很多官方板子已经定义了led0alias如果你的板子已有可以省略这个文件。如果编译时报“led0 not found”就需要根据板子的实际设备树节点补充 alias。5.4 编写 CMakeLists.txtcmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(my_zephyr_app) target_sources(app PRIVATE src/main.c)注意find_package(Zephyr)必须写在project()之前Zephyr 构建系统会在此时加载核心定义。5.5 编写 main.c#include zephyr/kernel.h #include zephyr/device.h #include zephyr/drivers/gpio.h #include zephyr/logging/log.h #include zephyr/dt-bindings/gpio/gpio.h LOG_MODULE_REGISTER(app, LOG_LEVEL_INF); #define LED_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led GPIO_DT_SPEC_GET(LED_NODE, gpios); static uint32_t counter 0; static void led_thread_entry(void *p1, void *p2, void *p3) { if (!gpio_is_ready_dt(led)) { LOG_ERR(LED GPIO not ready); return; } gpio_pin_configure_dt(led, GPIO_OUTPUT_ACTIVE); while (1) { gpio_pin_toggle_dt(led); k_sleep(K_MSEC(500)); } } static void log_thread_entry(void *p1, void *p2, void *p3) { while (1) { LOG_INF(uptime counter %u, counter); k_sleep(K_SECONDS(1)); } } K_THREAD_DEFINE(led_tid, 1024, led_thread_entry, NULL, NULL, NULL, 7, 0, 0); K_THREAD_DEFINE(log_tid, 1024, log_thread_entry, NULL, NULL, NULL, 6, 0, 0); int main(void) { LOG_INF(Zephyr multi-thread demo started); return 0; }代码做了四件事通过DT_ALIAS(led0)获取 LED 设备节点再用GPIO_DT_SPEC_GET生成struct gpio_dt_spec它封装了 GPIO 控制器和设备管脚号。led_thread_entry线程每 500ms 翻转一次 LED表示系统调度正常。log_thread_entry线程每秒钟打印一次递增计数方便通过串口观察。K_THREAD_DEFINE静态创建两个线程优先级分别设置为 7 和 6数值越小优先级越高所以日志线程会优先于 LED 线程执行。k_sleep是 Zephyr 推荐的线程延时方式它会主动让出 CPU而不是忙等。K_MSEC(500)是毫秒转 tick 的宏如果内核配置了 tickless 模式睡眠功耗会进一步降低。5.6 编译与烧录在zephyrproject目录下执行west build -b nrf52840dk_nrf52840 app west flash如果已经在app目录内可以简化为west build -b nrf52840dk_nrf52840 . west flashwest flash会调用对应的烧录工具。对于支持 DAPLink 或 J-Link 的板子插上 USB 后通常可以直接烧录。如果没有硬件也可以用 QEMU 目标验证west build -b qemu_x86 app west build -t run不过qemu_x86没有板载 LED需要根据模拟环境调整代码中的设备节点这里不再展开。5.7 预期结果烧录成功后串口终端会输出类似内容*** Booting Zephyr OS build zephyr-v3.x *** [00:00:00.000,000] inf app: Zephyr multi-thread demo started [00:00:01.000,000] inf app: uptime counter 0 [00:00:02.000,000] inf app: uptime counter 1 [00:00:03.000,000] inf app: uptime counter 2同时板载 LED 以 500ms 周期闪烁。看到这样的输出说明 Zephyr 环境、设备树、Kconfig、日志系统、线程调度全部正常工作。6. 常见问题与排查思路Zephyr 工程报错往往让新手无从下手下面整理几个高频问题每个问题都给出排查方向。问题现象常见原因解决思路west: command not foundPython 用户级 bin 目录未加入 PATH执行export PATH$HOME/.local/bin:$PATH编译报错CONFIG_XXX undeclaredKconfig 符号名写错或依赖未开启用west build -t menuconfig检查符号设备树报错led0 not found板级设备树没有该 alias编写 overlay 补充 alias改为实际 node labelZEPHYR_BASE未设置环境变量缺失在 zephyr 目录执行source zephyr-env.sh烧录失败permission denied调试器 USB 权限不足配置 udev 规则或把用户加入 dialout 组线程没有执行栈溢出或优先级配置不当增大 K_THREAD_DEFINE 栈大小检查优先级6.1 west 命令找不到这个问题的根本原因是 Python 安装的脚本目录没有加入 PATH。优先用python3 -m pip show west查看安装位置再手动导出 PATH。6.2 Kconfig 配置未生效Zephyr 的配置合并逻辑比较强prj.conf只是最上层配置。如果某个配置依赖其他配置需要在 menuconfig 中查看该符号变为灰色的原因通常是依赖条件不满足或选择了互斥选项。6.3 设备树 alias 缺失设备树报错时先看build/zephyr/zephyr.dts文件这是最终生成的设备树视图可以确认板级定义是否完整。大多数官方开发板的 LED 节点 label 是led0但也可能是green_led、blue_led等需要根据实际板卡调整 overlay。6.4 线程栈溢出Zephyr 提供了栈溢出检测机制打开CONFIG_THREAD_STACK_INFO或CONFIG_DEBUG_THREAD_INFO可以帮助定位问题。如果线程内使用了较大局部数组需要同步调大K_THREAD_DEFINE中的栈大小。7. 最佳实践与工程建议Zephyr 项目上了规模之后工程规范比代码技巧更重要。下面几条建议来自实际项目中的高频踩坑点。7.1 使用 west manifest 固定版本团队多人开发时统一 Zephyr 版本非常关键。建议在west.yml中显式锁定 Zephyr 内核和所有模块的 revision成员执行west update后能获得完全一致的代码状态避免“我这边能编译你那边报错”的版本漂移问题。7.2 分层管理配置文件不要把全部配置堆在prj.conf里。建议遵循以下分层prj.conf应用通用配置。boards/board.conf针对某块板子的特殊配置。board.overlay硬件属性差异化配置。条件编译场景使用prj_board.confZephyr 会按优先级加载。这样换方案、换板子时差异点一目了然。7.3 设备树优先避免硬编码管脚应用代码里尽量不要写死 GPIO 控制器和管脚号。用设备树属性描述管脚用途代码通过DT_ALIAS、GPIO_DT_SPEC_GET等 API 获取。这样当硬件改板、管脚重映射时只需要改 overlay不用动 C 代码。7.4 日志系统代替 printkprintk简单直接但不适合生产环境。建议使用 Zephyr 日志模块通过LOG_MODULE_REGISTER注册模块按 DEBUG / INFO / WARN / ERR 分级输出。日志模块支持运行时过滤还能减少对实时性的影响。7.5 线程设计要规划优先级Zephyr 是抢占式调度系统优先级规划需要全局统一。建议将周期短、实时性高的任务放到高优先级耗时操作放到低优先级线程中并通过信号量或消息队列异步处理。避免在中断处理函数里做耗时操作中断只负责标记事件并触发线程处理。7.6 注意内存和栈资源MCU 的 RAM 有限线程栈不是越大越好。先给合理初值通过栈溢出检测和内存统计工具定位峰值占用。Zephyr 提供了CONFIG_THREAD_ANALYZER等工具可以帮助统计各线程栈使用率。上线前记得关闭调试配置释放 Flash 和 RAM。7.7 注意日志外设和调试权限涉及安全、权限和产品配置时遵循最小权限原则。烧录工具和调试器配置需要确认开发板权限生产环境不要随意开启可能泄露信息的调试接口。8. 下一步学习路线看完这篇文章你已经掌握了 Zephyr 环境搭建、核心配置体系、选型对比和一个多线程示例。下一步可以按以下顺序深入阅读 Zephyr 官方文档中关于 Kconfig 和 Devicetree 的详细说明理解更多配置符号和设备树 API。尝试官方 samples 中的samples/hello_world、samples/philosophers、samples/boards观察不同示例的配置方式。如果项目涉及蓝牙学习samples/bluetooth下的 peripheral 和 central 示例了解 Zephyr 蓝牙协议栈的层次。尝试把现有 FreeRTOS 项目中的任务和队列用 Zephyr 线程和内核对象重写对比两种设计的差异。研究 Zephyr 的电源管理和节能模式这是低功耗产品的关键能力。如果这篇特别篇对你有帮助建议直接打开官方文档挑一个 sample 编译烧录再回来对照本文的配置逻辑理解会快很多。Zephyr 的路很长但值得走。