Zephyr 嵌入式开发指南:环境搭建、Kconfig 配置与 FreeRTOS 选型
Zephyr 不是 Linux 发行版,也不是一个单纯的 RTOS 内核,而是面向 MCU 和资源受限设备的开源实时操作系统。很多嵌入式工程师第一次遇到 Zephyr,通常是产品里同时需要 BLE、传感器、OTA 和低功耗,而用裸机或者 FreeRTOS 加厂商 SDK 的路线越写越难维护。要判断 2026 年嵌入式项目是否选 Zephyr,不能只看内核调度器,还要看环境搭建、配置体系、驱动模型、协议栈生态和长期维护成本。建议按这个顺序评估:先搭建环境,再理解 Kconfig,接着跑通最小示例,最后结合 FreeRTOS 做选型判断。
1. Zephyr 到底是什么,它解决哪类嵌入式问题
1.1 先区分“RTOS 内核”和“OS 平台”
嵌入式实时操作系统这个概念,在 FreeRTOS 那里通常指的是一个内核库:任务调度、队列、信号量、互斥量,再加上内存管理,大约几千行代码。Zephyr 走的是另一条路线。它把内核、驱动框架、文件系统、网络协议栈、蓝牙协议栈、低功耗管理、固件升级机制做成一个整体,用户拿到的是完整操作系统,而不仅仅是调度器。
“完整操作系统”听起来很重,但 Zephyr 的裁剪能力也来自这套体系。Kconfig 决定哪些模块编译进固件,Devicetree 决定哪些硬件设备被描述和驱动,两者配合可以让最终镜像保持在一个 MCU 能接受的规模。它并不是把 Linux 搬到 MCU 上,而是把 Linux 生态里被验证过的“配置、构建、设备描述、日志、调试”方法,按 MCU 场景重新实现了一遍。
1.2 Zephyr 的三大组成:内核、子系统、构建工具链
Zephyr 项目可以拆成三层看。
第一层是内核。它提供线程、时钟、信号量、互斥量、消息队列、事件、FIFO/LIFO、内存管理、同步机制。这部分和 FreeRTOS 的定位接近,但 API 风格更统一,命名上与 POSIX 有一定呼应。Zephyr 的线程模型支持协作式、抢占式和时间片调度,线程优先级数值越小优先级越高,这一点和很多 RTOS 相反,需要习惯。
第二层是子系统。这是 Zephyr 和轻量 RTOS 拉开差距的地方。内置子系统包括蓝牙、802.15.4、Thread、Zigbee、6LoWPAN、Wi-Fi(受驱动支持情况影响)、USB 设备栈、传感器子系统、显示、存储、加密、shell、日志、设置存储、设备电源管理、OTA 升级等。对产品来说,这些不是可有可无的组件,而是决定开发周期的主要部分。例如,一个需要 BLE 连接、传感器采样、空中升级的穿戴设备,用 Zephyr 可以直接在这些子系统的框架上做业务,不需要每个模块都从芯片寄存器开始写。
第三层是构建和环境工具链。Zephyr 官方推荐使用 west 作为多仓库工具,用 CMake 和 Ninja 组织构建,用 Kconfig 管理软件配置,用 Devicetree 描述板级硬件。这套组合对从单片机转到 Linux 工具链的开发者来说比较熟悉,对只用过 IDE 的嵌入式工程师来说,前两周会明显感到门槛。但一旦过了这个门槛,批量配置、CI 构建、多板卡管理会比打开 IDE 点按钮更可控。
1.3 哪些场景不适合用 Zephyr
Zephyr 不是万能方案。如果产品只需要一个 8 位 MCU、两三个任务、几十 KB Flash,Zephyr 的收益并不明显,裁剪成本反而高。FreeRTOS 或者裸机在这种场景下更直接。若项目由厂商 SDK 深度绑定,例如某些音频、无线 SoC,厂商的例程只在自有框架里跑,那么先把 FreeRTOS 版本跑通再评估 Zephyr,比直接推翻重来更稳妥。
在 2026 年的选型语境里,Zephyr 适合那些资源有一定余量、功能模块多、产品生命周期长、需要持续迭代的 MCU 项目。资源极限并不是它想解决的问题,软件生态和统一抽象才是。如果团队里没有人熟悉 CMake、Kconfig、Git 和命令行,第一批任务里应该加入“工具链学习”,否则项目进度会卡在构建阶段。
2. 搭建 Zephyr 开发环境:从 west 到 SDK
搭建 Zephyr 环境,核心目标是让
west build
能跑起来。west 是 Zephyr 官方推荐的元工具,它负责三件事:初始化多仓库工作区、调用 CMake/Ninja 构建、调用烧录器写入固件。安装顺序是:Python 环境、west、Zephyr SDK、初始化工程、编译 hello_world。
2.1 环境要求与推荐组合
Zephyr 的构建系统对主机环境有一定要求。以下是常见组合,实际版本以官方文档为准:
| 组件 | 推荐要求 | 说明 |
|---|---|---|
| 操作系统 | Linux / macOS / Windows WSL2 | Windows 原生也可以,但 USB 和路径问题较多 |
| Python | 3.10 或更新 | 安装 west 的基础环境 |
| CMake | 3.20 或更新 | Zephyr 构建系统依赖 |
| Ninja | 1.10 或更新 | 默认构建器,速度比 Make 快 |
| west | 最新稳定版 | 多仓库管理和构建入口 |
| 工具链 | Zephyr SDK 或厂商工具链 | 交叉编译需要 |
| 网络 | 能正常访问 GitHub 和 Zephyr 官方源 |
west update
会拉取多个仓库
|
如果主机上已经装过 Python,建议先检查版本:
python3 --version
cmake --version
ninja --version
版本不符合要求时,后续构建会出现难以理解的错误。这里不要跳过,宁可先安装好再开始。
2.2 安装 west 并确认版本
west 是一个 Python 包,直接通过 pip 安装。为了避免污染系统全局 Python,推荐加
--user
参数:
python3 -m pip install --user west
安装完成后检查版本:
python3 -m west --version
如果提示
west: command not found
,说明
~/.local/bin
不在 PATH 中。可以执行:
export PATH=$HOME/.local/bin:$PATH
并把这一行写入
~/.bashrc
或
~/.zshrc
。在 Windows WSL2 中,这个路径同样是常见的坑。
2.3 初始化 Zephyr 工作区
Zephyr 不是单一 git 仓库,而是一组仓库的集合。west 通过 manifest 文件统一管理这些仓库。初始化命令如下:
mkdir -p ~/zephyrproject
cd ~/zephyrproject
west init
west update
west init
只会在当前目录创建
.west
和 manifest 配置,不会拉取全部代码。真正把所有仓库下载下来的是
west update
。这一步耗时取决于网络,下载内容包括 Zephyr 主仓库、模块仓库和工具链相关的辅助仓库。
如果之前已经初始化过,进入工作区后不需要重复
west init
。只要在
zephyrproject
目录下,west 就能自动定位 Zephyr 根目录。
2.4 安装 Zephyr SDK
Zephyr SDK 包含交叉编译器、调试器、QEMU 模拟器以及目标平台支持文件。推荐从 Zephyr 官方 GitHub releases 页面下载对应主机平台的 SDK 包。下载完成后解压并运行安装脚本:
cd ~
tar xf zephyr-sdk-<version>.tar.xz
cd zephyr-sdk-<version>
./setup.sh -t all
安装脚本会把工具链路径注册好。为了确保构建系统能找到 SDK,还需要设置环境变量:
export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
export ZEPHYR_SDK_INSTALL_DIR=$HOME/zephyr-sdk-<version>
建议把这两行写入
~/.bashrc
。使用 SDK 工具链可以避免“本机 GCC 能不能编 ARM 代码”的问题。如果使用厂商工具链,例如 ARM GCC,也可以设置
ZEPHYR_TOOLCHAIN_VARIANT=external
后单独指定路径,但新手阶段优先使用官方 SDK。
2.5 用 native_posix 验证环境是否可用
环境是否真的配置好了,最好的办法是编译一个不需要硬件的示例。Zephyr 提供
native_posix
目标,它把 Zephyr 构建成一个可在 PC 上直接运行的本地程序,非常适合验证工具链和构建系统。
cd ~/zephyrproject
west build -b native_posix samples/hello_world
./build/zephyr/zephyr.exe
预期输出:
Hello World! native_posix
能看到这行输出,说明 Python、west、CMake、Ninja、Zephyr 源码和 SDK 基本都正确。之后接真实开发板时,只需要把
-b native_posix
换成具体板卡名,例如
nrf52840dk/nrf52840
或
stm32f746g_disco
。
2.6 Windows、Docker 和 CI 环境的差异
Windows 下比较推荐的路径是 WSL2。在 WSL2 里编译 Linux 原生工具链,路径和权限问题少很多。烧录时需要把 USB 设备从 Windows 映射进 WSL2,这依赖 usbipd 等工具,不同 Windows 版本有差异,先把编译跑通,再处理烧录。
Zephyr 也提供官方 Docker 镜像。如果团队需要统一构建环境,可以在 CI 里直接使用官方镜像,避免每个人手动装 SDK 出现版本漂移。Docker 方式下需要注意容器内挂载目录的写权限,以及容器内用户 UID 与宿主机是否一致,否则生成的文件在宿主机上可能无法直接修改。
3. Kconfig:Zephyr 配置体系的入口
Kconfig 是 Zephyr 里最需要耐心的一块。它来自 Linux 内核的配置体系,作用是管理“哪些软件功能被编入固件”。Zephyr 源码里到处是
config XXX
的定义,构建时根据用户配置生成最终的
.config
,再由构建脚本决定编译哪些文件。
3.1 Kconfig 与 Devicetree 的分工
很多新手混淆 Kconfig 和 Devicetree。简单来说:
- Kconfig 控制软件层面:要不要编译日志系统、shell、某个驱动、某个协议栈。
- Devicetree 描述硬件层面:某个 LED 接在哪个 GPIO 控制器上、哪个引脚、是否低电平点亮。
例如
CONFIG_GPIO=y
只表示 GPIO 驱动框架会被编译,并不表示某个引脚被配置为输出。引脚复用、电气属性、设备节点都在 Devicetree 里。应用代码通过 Devicetree API 拿到引脚编号,再调用 GPIO API 配置输出。
这两个体系的分工是 Zephyr 与很多传统 MCU 工程最大的差别。如果只改 Kconfig 不改 Devicetree,驱动可能没有设备实例;只改 Devicetree 不配置 Kconfig,对应驱动代码可能根本没编译进去。
3.2 配置来源和优先级
Zephyr 的配置是分层叠加的。构建时,系统会把以下来源按顺序合并:
| 配置来源 | 示例 | 优先级 |
|---|---|---|
| Kconfig 默认值 |
default y
| 低 |
| SoC 默认配置 |
arch/.../soc/.../Kconfig.defconfig
| 中低 |
| 板级默认配置 |
boards/<厂商>/<板卡>/<板卡>_defconfig
| 中 |
| 应用配置 |
prj.conf
,以及
boards/<板卡>.conf
| 高 |
| 额外配置片段 |
west build -- -DCONFIG_XXX=y
| 最高 |
实际使用中,大多数项目只需要在
prj.conf
里写应用配置。板级默认配置通常由 Zephyr 官方或板卡厂商维护。修改板级默认配置时要小心,因为会影响所有在该板卡上编译的应用。
3.3 Kconfig 常见语法
Kconfig 文件本身也可以阅读。一个简单的符号定义大致这样:
config MY_FEATURE
bool "Enable my feature"
default y
depends on GPIO
select LOG
help
This option enables my custom feature.
解释一下:
-
bool表示这是一个布尔开关,值是y或空。 -
default y表示没有其他覆盖时默认开启。 -
depends on GPIO表示只有在CONFIG_GPIO=y时,这个选项才可见、可被设置。 -
select LOG表示一旦开启MY_FEATURE,会强制开启LOG。select要慎用,它会绕过依赖检查,可能让一些配置组合看起来“莫名其妙被打开”。
如果看到
CONFIG_XXX=y
写进
prj.conf
后仍不生效,优先检查它是否有
depends on
条件不满足。
3.4 prj.conf 常用配置示例
一个典型的 Zephyr 应用
prj.conf
可能是这样:
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_SHELL=y
CONFIG_GPIO=y
CONFIG_MAIN_STACK_SIZE=2048
这些配置的含义:
| 配置项 | 作用 |
|---|---|
CONFIG_LOG=y
| 启用 Zephyr 日志子系统 |
CONFIG_LOG_MODE_IMMEDIATE=y
| 日志立即输出,不经过后台处理,调试阶段更方便 |
CONFIG_SHELL=y
| 启用 shell 子系统,可以通过串口执行调试命令 |
CONFIG_GPIO=y
| 编译 GPIO 驱动框架 |
CONFIG_MAIN_STACK_SIZE=2048
| 设置 main 线程栈大小,单位是字节 |
CONFIG_SHELL=y
这类配置在资源紧张时会有明显开销。调试阶段开着没问题,量产固件往往要关掉或限制为条件编译。
3.5 用 menuconfig 检查真实配置
写了很多配置之后,最直接的问题是:实际生效的配置到底是什么。Zephyr 提供 menuconfig 图形配置界面:
west build -b <board> <app>
west build -t menuconfig
在 menuconfig 中,可以用
/
搜索配置项,查看依赖关系、当前值和导致不能修改的原因。这里要注意:
menuconfig 保存的结果会写入
build/zephyr/.config,但这只是构建目录里的临时配置。如果删掉 build 目录重新构建,这些修改会丢失。
正确做法是,在 menuconfig 中确定可行配置后,把需要的项手动写回
prj.conf
。也可以使用
west build -t savedefconfig
从当前
.config
生成精简的 defconfig 内容,作为配置参考。不要习惯性直接改
.config
,否则下一次 clean 构建就会踩坑。
4. Zephyr vs FreeRTOS:2026 年嵌入式项目选型需要知道的差异
Zephyr 和 FreeRTOS 经常被放在一起比较,但它们其实不是一个量级的东西。FreeRTOS 是内核库,Zephyr 是完整操作系统平台。选型时不是看谁“更好”,而是看项目需要的软件复杂度在哪一层。
4.1 定位差异
| 维度 | FreeRTOS | Zephyr |
|---|---|---|
| 定位 | RTOS 内核库 | 完整 RTOS 平台 |
| 许可证 | MIT | Apache-2.0 |
| 代码规模 | 小,容易通读 | 大,需要按模块学习 |
| 构建系统 | 依赖厂商 SDK 或 IDE | west + CMake + Kconfig |
| 硬件描述 | 厂商自己管理 | Devicetree 统一描述 |
| 驱动模型 | 无标准,厂商各写各的 | 统一设备驱动模型 |
| 内置协议栈 | 基本不带 | 蓝牙、Thread、Zigbee、Wi-Fi 等 |
FreeRTOS 的优势是小和简单。一个传统 STM32 工程里,FreeRTOS 源码只占很小一部分,剩余的 HAL、BSP、应用逻辑由团队自己组织。Zephyr 则自带一套完整 BSP 和驱动体系,应用跑在它的抽象层上,前期学习成本高,但换板卡、升级驱动、扩展协议时节省大量工作。
4.2 内核 API 和 IPC 的差异
二者在调度器能力上都很成熟,都支持抢占式、协作式和时间片调度。差异主要体现在 API 风格和资源管理上。
Zephyr 创建线程的常见方式:
K_THREAD_DEFINE(my_tid, 1024,
my_thread, NULL, NULL, NULL,
2, 0, 0);
参数分别是线程名字、栈大小、入口函数、入口参数、优先级、选项、启动延迟。优先级 2 表示抢占式的普通优先级,数字越小优先级越高。
FreeRTOS 创建任务的典型写法:
xTaskCreate(my_task, "my_task", 1024, NULL, 1, NULL);
两者功能相似,但语义有区别。Zephyr 的优先级和 FreeRTOS 的优先级数值方向相反,FreeRTOS 数字越大优先级越高。如果从 FreeRTOS 迁移到 Zephyr,这类差异最容易隐藏 bug。
IPC 方面,Zephyr 提供信号量、互斥量、消息队列、邮箱、管道、FIFO/LIFO、事件对象。FreeRTOS 提供队列、二值信号量、计数信号量、互斥量、事件组、流缓冲区。选型时看团队习惯即可,不用过度纠结某个 API 的对比。
4.3 内存管理的差异
FreeRTOS 提供多种 heap 实现,例如
heap_1
到
heap_5
,每种的内存分配行为不同,使用简单但需要自己选择。Zephyr 默认更倾向于静态内存分配,线程栈、内核对象都在编译期定义,运行时动态分配需要显式配置堆。
静态分配的好处是内存使用可控、适合安全关键系统;缺点是灵活性低,不适合按需申请大块内存。Zephyr 也支持动态内存,例如
k_malloc
和
k_heap
,但配置门槛存在。对资源极小的 MCU,FreeRTOS 的轻量 heap 可能更顺滑。
4.4 协议栈、驱动和生态
这是 Zephyr 最明显的优势。FreeRTOS 本身不包含蓝牙协议栈,项目中通常需要用芯片厂商的 SDK,比如 Nordic 的 nRF5 SDK 自带 SoftDevice,乐鑫的 ESP-IDF 使用自己的协议栈。这些方案能跑,但厂商之间不通用,换芯片等于换开发方式。
Zephyr 把蓝牙、802.15.4、Thread、Zigbee、Wi-Fi、USB、传感器等作为标准子系统,应用层面向 Zephyr API 开发。换一个支持 Zephyr 的 SoC,很多驱动和应用代码可以复用。代价是 Zephyr 支持的 SoC 和板卡数量虽然多,但不同板卡的驱动完善程度参差不齐。评估时不能只看“支持这块板子”,要看具体外设驱动谁维护、有没有人在生产环境用过。
4.5 选型建议表
| 项目情况 | 更适合的方案 |
|---|---|
| 极低资源,8 位 MCU,任务很少 | FreeRTOS 或裸机 |
| 产品需要 BLE、OTA、多传感器 | Zephyr |
| 厂商 SDK 深度绑定,例程只在自由框架下 | FreeRTOS 加厂商 SDK |
| 产品生命周期长,后续可能换主控 | Zephyr |
| 团队只有单片机经验,对 Linux 工具链陌生 | 先 FreeRTOS,同时规划工具链学习 |
| 需求涉及蓝牙、Thread、Zigbee 等多协议 | Zephyr |
选型不是一锤子买卖。Zephyr 适合“软件复杂度高、迭代周期长”的项目,FreeRTOS 适合“快速可靠、小资源、团队已有经验”的项目。2026 年做选型时,重点不是看哪个系统功能多,而是看自己的团队和产品更能承受哪种开发模型。
5. 最小可运行示例:从 hello_world 到线程与 GPIO
环境配置看再多文档,不如亲手跑一个工程。这里从 hello_world 开始,再到一个包含线程、日志和 GPIO 的最小应用。
5.1 先跑通 hello_world
使用
native_posix
是最快的验证方式:
cd ~/zephyrproject
west build -b native_posix samples/hello_world
./build/zephyr/zephyr.exe
看到
Hello World! native_posix
后,再尝试真实板卡。以板卡名字替换
native_posix
,然后烧录:
west build -b <your_board> samples/hello_world
west flash
如果
west flash
找不到调试器,说明 runner 没有自动识别。可以先看板卡文档确定使用的是
jlink
、
openocd
、
pyocd
还是
dfu-util
,再手动指定:
west flash --runner <runner_name>
5.2 最小应用工程结构
在 workspace 里新建一个应用目录:
my_app/
├── CMakeLists.txt
├── prj.conf
└── src/
└── main.c
CMakeLists.txt 内容:
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_app)
target_sources(app PRIVATE src/main.c)
这段 CMake
更多推荐

所有评论(0)