如果你最近开始接触 Zephyr我先给你打个预防针这个 RTOS 的环境搭建比 STM32 标准库那套“装个 Keil 然后点几下鼠标”要抽象不少。它把工具链、编译系统、设备树、板级支持包全部揉在了一套叫 west 的多仓库管理工作流里第一“Hello World”编译成功之前至少有 30% 的人会卡在某个依赖或者网络问题上。这其实不是坏事。Zephyr 的目标是做一个真正面向物联网场景的通用 RTOS 内核支持的架构从 Cortex-M、Cortex-A、RISC-V 到 x86 全覆盖板卡超过 700 款。如果你只用 Keil/STM32CubeIDE 这一条路基本体验不到这种“一套代码在不同芯片上随意切换”的爽感。环境搭建的复杂换来的就是后面移植和复用的便利。这篇文章我会把搭建过程按我实际动手的顺序完整拆开把每一步“为什么这么干”讲清楚从零开始直到在 QEMU 和控制台里看到 Hello World。适合刚入门 RTOS 的嵌入式新手也适合被各种教程绕晕后想一口气跑通的工程师。1. 我整理的 Zephyr 环境搭建整体思路1.1 为什么 Zephyr 的环境搭建让人头疼先说说痛点。你如果之前玩过 Linux 下的交叉编译看到 Zephyr 的第一反应大概率是“不就是 make make flash 吗”。但实际上 Zephyr 的构建链路是west 负责拉取和管理所有仓库代码CMake 负责生成构建脚本Ninja 负责真正编译编译器默认使用 Zephyr SDK 自带的交叉工具链板级描述靠 DTS设备树镜像的链接和内存布局又依赖各板卡/SoC 的特定配置。这一套组合拳下来任何一个环节版本不对、路径没配对最终都会在你执行west build的时候以不同形式的报错弹出来。很多新手第一次见CMake Error或者ninja: error就直接懵了其实问题不在编译本身而是前面环境没理顺。我的经验是不要急着敲命令先搞懂整个流程分哪几段。整体上就是三件事拉代码、装工具链、跑构建。拉代码负责把 Zephyr 核心、各种模块比如硬件抽象、文件系统、网络协议栈按清单全部抓到本地工具链负责给你一个能编译嵌入式代码的交叉编译器Zephyr 官方对 x86、ARM、RISC-V 都提供预编译好的 SDK构建则是把应用源码、内核、设备树、配置系统通过 CMake/Ninja 组合成一个可烧录的镜像。思路清楚了后面每一步出现问题你都知道该去哪里找原因。1.2 一条主线四件事我把整个搭建过程归纳成四件大事装系统依赖Zephyr 的构建脚本依赖一堆 Linux 工具。这是最枯燥但最不能省的一步。创建 Python 虚拟环境并安装 westwest 是 Zephyr 官方推荐的源码管理和构建入口相当于一个“总指挥”。初始化工作目录并拉取源码west init会创建一个 workspace里面包含 Zephyr 核心仓库以及 west 的 manifest 清单之后所有板卡支持、驱动代码都在这个目录下。安装 Zephyr SDKSDK 里包含编译器、调试器、QEMU 模拟器等一套嵌入式开发必备的工具。环境搭完Hello World 示例本质上就两句话west build -b qemu_x86 samples/hello_world然后west build -t run。但我强烈建议你不要只盯着这两句命令因为真实项目里你一定会遇到配置板卡、添加自定义驱动、切换工具链这些需求环境这层多做 20% 的功课后面能省 200% 的折腾时间。2. 动手前先选型Linux、Windows 还是 Docker2.1 三个平台怎么选我最早是直接在 Windows 上尝试装的。官方确实提供了 Windows 安装包和 PowerShell 脚本但实际体验是CMake、Ninja、Python、MSYS2 一样都不能少而且路径分隔符、系统环境变量、权限这些“小问题”会把你的耐心一点点消耗光。后来转到 WSL2Windows Subsystem for Linux体验立刻好了很多因为本质上你只是在 Windows 里跑一个 Ubuntu 虚拟机所有命令和路径都跟原生 Linux 一样。如果你是 Windows 用户我的建议排序是WSL2 Ubuntu 22.04最推荐基本等于开箱即用的 Linux 环境跟 Zephyr 官方文档完全对齐。Docker 容器用官方镜像zephyrprojectrtos/zephyr-build适合不想污染本机环境、或者团队需要统一构建环境的场景。原生 Windows能跑但需要跟着官方脚本一步步来遇到问题的概率最高。Linux 用户就不用纠结了直接用宿主机的 Ubuntu 或者 Debian安装完依赖直接开始。macOS 用户也可以跑 Zephyr但 SD 卡烧录、USB 驱动这类硬件相关操作可能没有 Linux 方便如果是长期开发建议还是弄个 Linux 环境。2.2 系统依赖清单与作用在 Ubuntu/Debian 上官方推荐的依赖安装命令是这样的我稍微合并了一下避免装多sudo apt update sudo apt install --no-install-recommends \ 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 gcc-multilib \ g-multilib libsdl2-dev libmagic1很多人看到这一长串就头大我按用途拆解一下方便你以后排查git拉取代码最基本工具不用解释。cmakeninja-buildZephyr 的构建系统。CMake 负责解析顶层CMakeLists.txt并生成构建规则Ninja 是实际执行编译的高效构建器可以理解为“比 make 更快更干净的构建工具”。gperf一个哈希生成工具。Zephyr 在编译时会用 gperf 为一些配置表、关键字表生成完美的哈希函数少了它一定会报错。device-tree-compiler编译 DTS 设备树文件。Zephyr 用设备树来描述硬件资源和引脚连接这个工具负责把 DTS 源文件编译成 dtb。dfu-util设备固件升级工具。后面烧录到真实开发板时很多板子支持 USB DFU 方式下载这个必须装。wget下载 SDK 用。python3相关包west 是 Python 写的很多 Zephyr 脚本也是 Python 脚本所以 Python 环境要完整。gcc/g系列编译一些宿主机的辅助工具。libsdl2-dev用 QEMU 跑带图形界面的模拟器时可能用到装上有备无患。提示如果你用的是 Fedora/Arch安装包名会略有不同。核心思路是一样的把上面这些工具的对应发行版包全部装上即可。3. 从零到 Hello World 的完整实操3.1 创建虚拟环境并安装 west我不建议在系统全局装 west。嵌入式项目经常要切换不同版本用 Python 虚拟环境是最省心的做法。实际操作如下mkdir -p ~/zephyrproject cd ~/zephyrproject python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install west装完以后验证一下west --version如果输出类似West version: v1.2.0就说明 west 装好了。这里有一个小细节虚拟环境激活只在当前终端窗口有效。你每次重新打开终端想用west命令时都需要先执行一次source ~/zephyrproject/.venv/bin/activate除非你把ZEPHYR_BASE和 west 的路径做好了持久化配置。我的习惯是把这两行写进~/.bashrc省得每次都手动敲。3.2 用 west init 拉取 Zephyr 主干还是在~/zephyrproject目录下执行west init -m https://github.com/zephyrproject-rtos/zephyr --mr main west updatewest init做了什么它会在当前目录生成一个.west目录和一个west.ymlmanifest 文件。这个 manifest 是 Zephyr 项目管理的核心它列出了一堆仓库的地址和版本zephyr 核心、picolibc、mcuboot、hal_espressif、hal_nordic 等等。west update就是按照这个清单把这些仓库全部拉到本地。这一步在网速不好的时候容易卡住因为仓库数量多、体积大。如果更新到一半断网或者失败不用慌直接重新执行west update即可它会断点续传。首次更新可能需要 10~20 分钟取决于你的网络情况。更新完以后cd zephyr进去可以看到一个完整的 Zephyr 源码树板卡支持列表在boards/目录下内核代码在kernel/下应用示例在samples/下。这里顺便解释一下为什么 Zephyr 要用 west 这样一层封装。Zephyr 不是单仓库它把 HAL、第三方库、工具脚本拆成了几十个独立仓库。如果用git clone一个个拉版本兼容性会是一场灾难。west 通过 manifest 保证了一套“组合拳”的确定性版本匹配这在工程上是很有价值的设计。3.3 安装并配置 Zephyr SDKSDK 是另一个大坑。我第一次装的时候选错版本导致后面编译出来一堆诡异错误。从 Zephyr 官网zephyrproject.org 的下载页面或者 GitHub Releases 找到最新的zephyr-sdk-xxx_linux-x86_64.tar.xz。写这篇文章时常用的版本是 0.16.x我建议你下载 release 版本而不是 nightly 版本。安装命令cd ~ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.3/zephyr-sdk-0.16.3_linux-x86_64.tar.xz tar -xf zephyr-sdk-0.16.3_linux-x86_64.tar.xz cd zephyr-sdk-0.16.3 ./setup.sh -t all -hsetup.sh会自动完成两件事把交叉工具链路径写进配置并安装若干依赖。参数-t all表示安装所有工具链目标架构如果你的板子是 ARM 就只需要-t arm-zephyr-eabi但我建议直接全装省得以后换板子再折腾。安装完还需要告诉 Zephyr 编译器在哪。打开~/.bashrc加上export ZEPHYR_TOOLCHAIN_VARIANTzephyr export ZEPHYR_SDK_INSTALL_DIR$HOME/zephyr-sdk-0.16.3然后source ~/.bashrc。注意ZEPHYR_TOOLCHAIN_VARIANT 一定要设置。如果不设置西风会默认尝试找系统自带的 GNU 工具链但系统自带的 gcc 是不能直接交叉编译嵌入式固件的。SDK 里的编译器才是正主。3.4 构建 Hello World 镜像并运行环境都准备好了现在开始真正的“仪式”——Hello World。Zephyr 自带的示例位于samples/hello_world代码非常简洁#include zephyr/kernel.h int main(void) { printk(Hello World! %s\n, CONFIG_BOARD); return 0; }注意这里用的是printk而不是标准 C 的printf。printk是 Zephyr 内核里的格式化输出函数它不依赖 libc在最小配置下也能工作输出目标可以是串口、shell、甚至是 RTT。这是 RTOS 开发和 Linux 应用开发很不一样的一个点你不能默认“标准 C 库一定被完整链接进来了”。先不碰硬件用 QEMU 模拟 x86 开发板跑起来cd ~/zephyrproject/zephyr west build -b qemu_x86 samples/hello_world west build -t runwest build的-b参数指定目标板卡-t run表示运行模拟器。第一次编译会比较慢因为要生成各种配置头文件和设备树二进制后面就快多了。编译完成后QEMU 窗口里会打印*** Booting Zephyr OS build v3.x *** Hello World! qemu_x86看到这行字说明你的 Zephyr 环境从代码拉取、工具链编译到运行整个闭环已经全部打通。这时候再回过头去看刚才的一堆命令是不是觉得其实也没那么复杂。4. 换到真实开发板以 STM32F103 和 nRF52840 为例4.1 先确认板卡是否在支持列表QEMU 只是虚拟验证嵌入式开发的最终目标当然是烧到真实硬件。Zephyr 支持的板卡很多但并不是所有板卡都能开箱即用。打开~/zephyrproject/zephyr/boards/目录你可以看到所有官方支持的板卡列表每个板卡都有一个自己的目录里面有board.cmake、Kconfig、xxx_defconfig等文件。比如经典的 STM32F103 系列很多同学手里是“野火”或者“正点原子”的板子芯片是 STM32F103ZET6 或 C8T6对应的 Zephyr 板卡名是nucleo_f103rbST 官方 Nucleo 板或者olimexino_stm32。虽然后者不是 ST 官方板但芯片一致很多基础外设驱动都能复用。你可以在boards/arm/目录下找找有没有跟你手上芯片匹配的 board 目录。找到匹配的板卡名之后构建命令就是west build -b nucleo_f103rb samples/hello_world west flashwest flash会调用板卡目录里board.cmake定义的烧录工具。对nucleo_f103rb来说默认烧录器是 ST-Link它需要你电脑上安装好 OpenOCD。如果没装会提示找不到烧录器。4.2 烧录过程中的常见问题与心得west flash失败是最常见的。归结起来无非三种原因第一没有安装 OpenOCD。在 Ubuntu 上直接sudo apt install openocd即可。第二调试器权限问题。ST-Link 的 USB 设备在 Linux 下默认访问权限不够表现为openocd报libusb_open()failed 之类的错误。解决办法是添加 udev 规则给/etc/udev/rules.d/下新建一个99-stlink.rules内容里写入 ST-Link 设备的 USB Vendor ID0x0483然后重新插拔 USB。Zephyr SDK 安装时其实附带了openocd路径通常在~/.local/zephyr-sdk-xxx/sysroots/x86_64-pokysdk-linux/usr/bin/openocd但 udev 规则还是得自己加。第三板子型号和west flash默认烧录接口不匹配。有的板卡是 j-link有的是 daplink有的是串口 DFUwest flash默认用的是板卡目录里配置好的方式一般不用改。如果你用的是第三方板子但复用了官方 board 目录就要格外注意烧录器是否一致。我个人的习惯是在第一次用真实板子之前先手动跑一遍 OpenOCD 确认能连上目标芯片。这样可以隔离问题到底是我环境问题还是 Zephyr 的 flash 脚本问题一目了然。这一步也建议你试省得在west flash报错时一头雾水。5. 常见问题与排查技巧实录这一部分是我希望前面能有人早点告诉我的内容。把最容易踩的坑按症状列出来方便你在遇到同样问题的时候直接对号入座。5.1 west 命令相关症状可能原因解决办法west: command not found虚拟环境没激活执行source ~/zephyrproject/.venv/bin/activatepip install west非常慢或超时默认 PyPI 源在海外使用国内 pip 镜像源pip install west -i https://pypi.tuna.tsinghua.edu.cn/simplewest update卡住不动网络原因仓库过多不要 CtrlC等几分钟如果长时间无进展重试west update已拉取的不会重复下载west init后找不到zephyr/目录忘执行west updatewest init只是初始化清单源码要 update 才有西风有个常用命令是west config可以用来查看和修改配置。比如你想指定拉代码时并发数量可以用west config manifest.fetch --parallel 8加快首次 update 的速度亲测有效。5.2 编译与 SDK 相关症状可能原因解决办法CMake Error: Could not find ZephyrZEPHYR_BASE环境变量没设好在 zephyr 目录下确认source zephyr-env.sh或设置export ZEPHYR_BASE~/zephyrproject/zephyrCould not find a suitable toolchainZEPHYR_TOOLCHAIN_VARIANT 没设置为 zephyr检查~/.bashrc中的 export重新sourceThe current CMake version is ...系统 CMake 太老用 pip 装新 cmakepip install cmake或者从官方源装新版编译时报gperf: command not found依赖没装全重新执行sudo apt install gperfninja: error: loading build.ninja之前构建配置损坏删除build目录后重新west build这是最粗暴有效的方法编译一次后改配置不生效构建缓存问题用west build -p always强制重新构建或者手动删 build 目录-p参数我要单独说一句。west build -p有auto、never、always三种模式。-p always会强制全量重建虽然慢点但能有效规避增量构建的脏缓存问题。我遇到改 Kconfig 配置后行为不变的情况基本都是靠这一步解决的。5.3 串口输出与硬件相关Hello World在 QEMU 能打印但上真板子串口看不到输出这是另一种高频问题。排查顺序我一般遵循三步第一步确认 printk 输出到底去了哪。查板子的 defconfig确认CONFIG_SERIALy以及CONFIG_CONSOLEy。这是板级支持里本来就配好的一般不用管但如果是自己移植的板子这个就非常关键。第二步确认串口接线和电平。Zephyr 的 hello_world 输出通常走 UART而且很多开发板的调试串口是 TTL 电平需要接 USB-TTL 转换器。波特率默认一般是 115200。如果你用 MicroUSB 直连 ST-Link 的虚拟串口通常没有电平问题但要注意选择对正确的 COM 口。第三步确认你看到的是不是 boot banner。Zephyr 启动时默认会打印*** Booting Zephyr OS build v3.x ***这行。如果你只看到这一行但没有 Hello World说明内核启动正常但 main 函数里的 printk 可能被配置剪掉了一般是 console 驱动没生效。如果连 banner 都没有那就回到烧录是否成功的排查上。另外强烈建议新手在串口工具里打开“显示时间戳”或者“自动换行”因为 Zephyr 的 printk 和 shell 输出混在一起时肉眼很难分清哪些是系统打印、哪些是你的应用打印。用screen /dev/ttyACM0 115200或者 minicom 都行看个人习惯。5.4 我踩过最深的一个坑West 和 CMake 的版本绑定最后分享一个我记忆深刻的坑。之前我在一台老机器上搭建 Zephyr系统是 Ubuntu 20.04预装的 CMake 是 3.16。Zephyr 官方要求 CMake 3.20 以上但构建时并不会直接报“版本过低”而是在某个很偏门的外设配置项上报错。我花了大半天排查后来用west build -v查看详细日志才发现是 CMake 版本的问题。解决办法也很简单不要动系统自带的 cmake直接用 pip 装一个高版本到虚拟环境里pip install cmake这样cmake命令会优先用虚拟环境里的新版。这个经验告诉我Zephyr 的 bug 报错信息往往不直观遇到奇怪问题先看west build -v的完整输出再检查工具版本能少走很多弯路。6. 搭建完成之后下一步往哪走环境跑通只是万里长征第一步。Zephyr 真正有魅力的地方在于它的模块化和可裁剪性你可以在 Kconfig 里一张一张地勾选需要的子系统。我的建议是在 Hello World 跑通之后紧接着做三件事第一熟悉prj.conf和设备树。Zephyr 的配置体系和 Arduino 那种“直接操作寄存器”完全不同它是把硬件资源和内核选项全部抽象成配置项。花点时间看懂prj.conf里的CONFIG_XXX是怎么影响编译的后面学 GPIO、UART、I2C 都会顺很多。第二把 shell 子系统跑起来。在配置里加一行CONFIG_SHELLy然后编译下载你就能在串口里直接敲命令查看线程状态、内存使用、内核版本。这是调试 Zephyr 应用的一大利器很多问题靠 shell 一眼就能定位。第三搞一个自己的板卡目录。哪怕只是复制官方相近板卡的目录改一改也能帮你建立“配置焦虑免疫”。因为 Zephyr 这套体系里最让新手上头的就是 dts 和 Kconfig 的联动关系亲手改一遍自己的板卡配置这些知识才算真正内化。如果你后面想跑 WiFi 连接、蓝牙、MQTT、甚至加密芯片这些Zephyr 都有现成的子系统而且你只要配好 board 支持上层代码基本可以跨平台复用。这大概是 Zephyr 这套“重环境”的路线图最终带给你最大的回报——一次搭建到处编译不同芯片之间迁移时核心应用代码几乎不用动。
