Ubuntu下Zephyr开发环境搭建实战:从west到SDK完整指南
1. 为什么我现在推荐在Ubuntu上搭Zephyr环境1.1 Zephyr的定位不是又一个玩具RTOS上个月我把一块吃灰了很久的开发板翻出来想在Ubuntu上把Zephyr跑起来。本以为照着官方文档敲几行命令就完事结果从系统依赖到SDK路径、从QEMU到设备树整整折腾了两个晚上。回头看整个过程问题其实集中在几个点上系统包的版本、west和SDK的路径关系、以及QEMU在Linux下的表现。这篇就当作一份实测记录分享我在Ubuntu下安装Zephyr、跑通示例的完整链路。先聊几句为什么选Zephyr。Zephyr是Linux基金会托管的开源实时操作系统Apache 2.0许可证面向物联网和嵌入式场景。它最吸引我的不是又一个RTOS而是这套组合拳Kconfig实现内核功能裁剪、设备树描述硬件、统一驱动模型、内置蓝牙/Thread/Wi-Fi协议栈、支持ARM/RISC-V/x86/Xtensa等多种架构。这意味着你学的不只是一套API而是一整套现代嵌入式软件的工程方法。1.2 和FreeRTOS、RT-Thread相比Zephyr赢在生态而非单点性能很多朋友会问嵌入式开发有FreeRTOS国产的还有RT-Thread为什么非要折腾Zephyr我的回答是如果只是做个简单的控制板FreeRTOS足够学习曲线还更低但如果你想接触设备树、Kconfig、可移植驱动、以及一套能跨厂商复用代码的工程体系Zephyr的生态优势很明显。维度Zephyr RTOSFreeRTOSRT-Thread许可证Apache 2.0MIT部分组件另有条款Apache 2.0内核 商业组件配置方式Kconfig 设备树头文件宏定义menuconfig / SCons驱动模型统一设备驱动框架跨厂商复用以厂商SDK为主可移植性靠封装设备驱动框架较完善内置协议栈BLE、Thread、Wi-Fi、CoAP等依赖第三方或厂商组件化按需集成构建系统CMake Ninja经west统一管理各家工具链混杂SCons / CMake 均可说直白点Zephyr是工程化优先的RTOSFreeRTOS是轻量优先的RTOS。你在Ubuntu上学Zephyr其实是在学一整套嵌入式工程基础设施。1.3 选择Ubuntu的客观理由Zephyr官方文档对Linux的支持最完善Ubuntu又是最流行的开发发行版生态和社区资料最全。别小看这一点你遇到编译问题、Kconfig问题随便一搜大概率有Ubuntu用户已经踩过同一坑。Windows也能跑Zephyr但WSL和本地路径、USB烧录的兼容性总有一些隐形问题macOS在烧写某些ARM调试器时也有驱动烦恼。从一开始就用Ubuntu能省掉大量环境层面的烦恼。我在VMware虚拟机里安装过Ubuntu 22.04 LTS来跑这套环境后面会专门讲虚拟机里会遇到哪些额外问题。2. 环境准备先把Ubuntu侧的依赖喂饱2.1 Ubuntu版本和Python版本怎么选Zephyr官方对Ubuntu没有硬性版本限制社区里的主流选择是20.04、22.04和24.04。我实测用的是Ubuntu 22.04 LTS。选LTS版本的原因很简单长期支持周期内会持续更新apt源里的工具链版本相对稳定不至于半年后就得因为依赖太旧而重装。Python版本是重头戏。Zephyr 3.x系列要求Python 3.8以上Ubuntu 22.04自带Python 3.10满足要求。如果你是老版本Ubuntu比如18.04自带Python 3.6直接上Zephyr会有一堆依赖兼容问题我的建议是直接升级系统不要想着在老系统上硬凑Python版本那样会陷入依赖地狱。先确认一下当前环境的底子python3 --version cmake --version # 如果有的话 pip3 --version看到Python版本之后下一步就该装系统包了。2.2 一批必须提前装好的系统包以及它们各自是干什么的很多人的安装失败不是因为步骤复杂而是因为缺了一个不起眼的系统包。Zephyr官方的Ubuntu依赖清单我整理成一条apt命令sudo apt update sudo apt upgrade 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 gcc-multilib g-multilib libsdl2-dev libmagic1这些包各自是干什么的理解用途能帮你后续排查问题时更快定位cmake ninjaZephyr的构建系统就靠这两位。CMake负责生成构建规则Ninja负责真正执行编译。Ninja是增量构建的利器比Make快很多。gperf生成完美哈希函数Zephyr的Kconfig系统用它处理配置项。缺了这个Kconfig阶段就会报错。device-tree-compilerdtc设备树编译器。Zephyr用设备树描述板级硬件编译时要把dts源文件编译成dtb这一步必须有dtc。ccache编译缓存。嵌入式项目每次构建都会编译大量文件缓存命中后能快很多。对反复修改代码的日常开发这个提升非常明显。libsdl2-devQEMU在图形模式下运行需要SDL库。如果你只跑纯终端输出的示例可能用不到但一旦示例里用到显示窗口缺这个包就会黑屏或报错。python3-dev、python3-wheel、python3-tk部分Python包在安装时需要编译C扩展或者导入图形库。Tkinter是Zephyr的west支持中某些图形工具会用到的。dfu-util给板子做DFU升级的通用工具。如果你后续用nRF系列或者其他支持USB DFU的板子必不可少。gcc / gcc-multilib / g-multilib宿主机的编译工具链。编译host工具和部分测试用例时需要。libmagic1文件类型识别库部分构建脚本依赖它判断文件格式。装完这一组系统层面基本就位了。2.3 为什么我不建议用系统python直接pyp install接下来是Python侧的依赖管理。很多人习惯直接执行pip3 install west这在刚装的干净系统上往往没问题但如果系统里还有其他Python项目或者你曾经手动装过一些包直接装可能污染系统环境。而且pip在部分Ubuntu版本上坚持要求外置管理环境直接装west可能报externally-managed-environment错误。我推荐的稳妥方式是用用户级安装把命令工具放到用户目录避免和系统包管理器冲突pip3 install --user west export PATH$HOME/.local/bin:$PATH west --version这里有个细节--user装完west之后它的可执行文件在~/.local/bin下。如果PATH里没包含这个目录你会面临明明安装了却提示找不到命令的尴尬。执行完上面的export后可以在~/.bashrc里追加一句export PATH$HOME/.local/bin:$PATH这样以后每次开终端都不用重新设置。3. 正式安装west、Zephyr仓库、SDK一条龙3.1 west是Zephyr的工程总管先把它装好west不是普通的构建工具而是Zephyr的多仓库管理工具。它管的不只是zephyr主仓库还有项目的依赖仓库、子模块、工具链配置。理解west的定位很关键你用west初始化项目、拉取代码、构建、烧录、调试它贯穿整个开发流程。安装完west并确认版本可用后首先创建工作目录并初始化mkdir -p ~/zephyrproject cd ~/zephyrproject west init -m https://github.com/zephyrproject-rtos/zephyrwest init -m指定的是west manifest仓库的地址也就是Zephyr的主仓库。执行完成后~/zephyrproject下会出现一个zephyr目录和一个west.yml文件。west.yml是项目的清单文件定义了Zephyr主仓库以及所有依赖仓库的版本和位置。接着同步所有子模块cd ~/zephyrproject west update这一步会把Zephyr依赖的所有外部仓库拉到本地包括hal、tools、modules等目录。整个拉取过程取决于网络状况国内网络环境下可能较慢建议耐心等待或者选择网络状况好的时段执行。执行完成后west会提示你需要安装Python依赖pip3 install --user -r zephyr/scripts/requirements.txt这一步不能跳过。requirements.txt里是Zephyr构建系统的Python库依赖比如pyelftools、python-magic等。缺了这些后面构建时会报Import Error之类的错误而且报错信息往往不直接指向缺包排查起来非常浪费精力。3.2 Zephyr SDK工具链、调试器、QEMU全家桶Zephyr编译目标平台的可执行文件需要交叉编译工具链。Zephyr SDK是官方提供的工具链合集里面包含了ARM、RISC-V、x86、Xtensa等架构的编译器、汇编器、链接器还自带OpenOCD和QEMU。下载SDK可以从GitHub releases页面获取以0.16.8版本为例cd ~ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.8/zephyr-sdk-0.16.8_linux-x86_64.tar.xz tar xf zephyr-sdk-0.16.8_linux-x86_64.tar.xz这里有两个版本选择坑需要留意SDK版本必须和Zephyr主仓库版本匹配。Zephyr版本更新后对SDK的版本要求也会变。如果你用最新版Zephyr但下载了太老的SDK构建时会出现工具链版本不支持、编译器参数不识别等错误。建议到GitHub releases页面查看当前Zephyr 3.x对应的推荐SDK版本。下载中断问题。SDK压缩包通常接近一个GB网络差的时候容易中断。用wget -c断点续传比重新下载更高效。解压后进入SDK目录执行安装脚本cd ~/zephyr-sdk-0.16.8 ./setup.shsetup.sh会把SDK里的工具链配置写到系统环境里。如果你只想给当前用户配置可以执行./setup.sh -h查看选项。特别说明setup.sh运行完毕通常会提示你选择是否配置~/.zephyrrc选是就行这个文件后续会被Zephyr构建系统自动加载。3.3 环境变量和zephyrrc快照SDK装好后还需要让构建系统知道去哪找工具链。Zephyr通过环境变量ZEPHYR_TOOLCHAIN_VARIANT和ZEPHYR_SDK_INSTALL_DIR来定位工具链。这两个变量缺一不可很多C compiler cannot create executables的报错都是因为没设置它们。echo export ZEPHYR_TOOLCHAIN_VARIANTzephyr ~/.bashrc echo export ZEPHYR_SDK_INSTALL_DIR$HOME/zephyr-sdk-0.16.8 ~/.bashrc source ~/.bashrc我踩过一次很隐蔽的坑SDK目录不能放在带空格的路径下也不能用~这种符号来简化写入~/.zephyrrc否则构建系统解析路径时可能出问题。直接用$HOME展开的绝对路径最稳妥。到这里整套环境就搭好了。验证一下west --version cmake --version python3 --version echo $ZEPHYR_TOOLCHAIN_VARIANT echo $ZEPHYR_SDK_INSTALL_DIR看到所有版本信息都正常就可以编译第一个示例了。4. 编译跑通第一个示例hello_world和blinky4.1 hello_world编译流程详解进入Zephyr主仓库的sample目录编译hello_worldcd ~/zephyrproject/zephyr west build -p auto -b qemu_x86 samples/hello_world拆解一下这条命令-p auto每次构建前自动清理build目录。这个参数在做多板型验证时非常有用避免上一次编译的残留物污染本次构建。-b qemu_x86指定目标板。qemu_x86是Zephyr内置的模拟x86平台不需要真实硬件就能跑起来验证环境。samples/hello_world指定要编译的sample路径相对于zephyr仓库根目录。第一次构建会比较慢因为要编译内核、驱动、libc和示例代码。构建日志末尾能看到生成的可执行文件信息。构建完成后运行west build -t run-t run会调用QEMU启动生成的可执行文件模拟一个x86目标平台。正常输出如下Hello World! qemu_x86看到这行输出说明你的Zephyr环境已经完全打通了交叉编译没问题SDK路径没问题运行仿真也没问题。这个时刻值得多体会一下——你已经完成了一次完整的交叉编译模拟运行。4.2 编译产物结构和board概念构建完成后build目录里藏着很多值得了解的东西。最核心的几个文件build/zephyr/zephyr.elf带调试信息的ELF文件调试器用的就是这个。build/zephyr/zephyr.bin纯二进制镜像最终烧录到Flash的就是它。build/zephyr/zephyr.hexIntel HEX格式部分烧录工具需要这个格式。当你换成真实开发板时流程完全一致只要改-b参数为板子的名称比如-b nrf52dk/nrf52832或-b rpi_pico然后west flash烧录就行。Zephyr的板级描述在boards目录下每种板子都有自己的设备树文件和defconfig配置这就是Zephyr可移植性的根基。查看当前west支持的boards清单west boards | head -30west boards输出的就是几百种开发板。搜索特定厂家的板子可以配合grep比如west boards | grep nrf或west boards | grep stm32这样能快速找到对应的board target名。4.3 跑一个LED灯闪烁的blinky示例hello_world验证了工具链但还没展示Zephyr的设备驱动模型。接着编译blinky示例west build -p auto -b qemu_x86 samples/basic/blinky west build -t runblinky示例在真实板子上会让板载LED闪烁但在qemu_x86上没有实际LED硬件。Zephyr的QEMU运行会通过串口输出模拟的LED状态信息你能在终端看到类似LED is ON/OFF的状态切换日志说明GPIO驱动已经在模拟环境里跑起来了。如果就想看图形界面效果可以换用-b qemu_cortex_m3跑一些带显示屏的示例但那需要SDL库的支持上一章那个libsdl2-dev就派上用场了。这块我放在后面的坑里细说。还有一个值得尝试的是native_sim目标west build -p auto -b native_sim samples/hello_world ./build/zephyr/zephyr.exenative_sim把Zephyr编译成普通Linux进程来运行不需要QEMU启动速度和调试体验都很舒服。开发调试阶段我经常先用native_sim快速验证逻辑再编译到真实板子做硬件联调。这对刚入门的人来说也是一个非常好的第一个目标——它完全绕开了对硬件的依赖。5. 我实测中遇到的坑以及完整的排查链路5.1 坑一SDK路径没设置构建器报could not find a C compiler第一次正式编译时我直接在zephyr目录下执行了west build -p auto -b qemu_x86 samples/hello_world结果刚开始构建就卡住报错信息核心是-- The C compiler identification is unknown CMake Error: ... Check for working C compiler ...这个报错看起来像是编译器坏了其实和编译器完全无关。排查链路如下先确认SDK是否真的装好了ls ~/zephyr-sdk-0.16.8目录存在。检查环境变量echo $ZEPHYR_SDK_INSTALL_DIR结果输出为空。检查echo $ZEPHYR_TOOLCHAIN_VARIANT同样为空。问题就在这里setup.sh虽然装了SDK但~/.bashrc里的环境变量是我手工敲的那些echo命令去追加的而我在执行追加之前先跑了构建命令。也就是说环境变量写入和构建之间没确认好先后顺序。重新执行source ~/.bashrc之后再验证一次环境变量然后重新构建问题立刻消失。这个小坑的教训是Zephyr的构建系统按固定顺序找工具链——ZEPHYR_TOOLCHAIN_VARIANT变量先决定去哪找工具链如果这个变量是空的它就尝试系统默认的gcc而系统gcc不会生成目标平台的机器码自然报c compiler cannot create executables。遇到这类报错不要急着去重装编译器先用env | grep ZEPHYR看看环境变量。5.2 坑二cmake版本过低导致构建阶段直接爆红另一个高频坑是cmake版本问题。Zephyr 3.x对cmake的最低版本要求是3.20以上。Ubuntu 20.04系统源里的cmake只有3.16构建时会直接爆红报错信息类似CMake 3.20.0 or higher is required. You are running version 3.16.3记住这跟Zephyr本身没关系纯粹是构建系统对cmake版本的要求。解决办法有两条路径路径A升级Ubuntu源里的cmake。用sudo snap install cmake --classic或者添加Kitware的apt源。这种方式对系统全局影响更大但版本可以很新。路径B用pip装cmake。pip3 install --user cmake然后确保~/.local/bin在PATH前面这样优先用用户级的新版cmake。我选的是这条路因为pip安装的cmake只影响当前用户不会动系统全局测试完不满意可以直接卸。另外注意pip安装的cmake如果版本太新比如4.x偶尔也会和Zephyr的CMakeLists脚本有兼容问题卡在某个不明显的语法解析错误上。这时候看报错路径如果指向了cmake modules就怀疑版本对应关系。检查版本对应关系最好的方式是去Zephyr的west.yml和SDK release note里确认官方推荐组合。5.3 坑三QEMU运行黑屏/SDL报错跑需要图形界面的示例时QEMU可能直接黑屏或者报一串SDL相关的错误。典型报错Could not initialize SDL( - Could not find variant SDL ...) exiting这不是Zephyr的问题是宿主机没有SDL开发库导致QEMU的图形后端无法初始化。解决方式很简单sudo apt install -y libsdl2-dev装完之后删除build目录里上一次的缓存重新west build -p auto -b qemu_cortex_m3 samples/...。需要说明的是如果你在虚拟机里跑这套环境虚拟机本身就要分配足够的内存和图形支持否则就算SDL装了QEMU窗口也可能渲染异常。给VMware分配至少4GB内存、2个CPU核心并开启3D加速能减少很多图形类怪问题。5.4 坑四在虚拟机里跑QEMU导致又慢又卡很多朋友跟我一样是在VMware里装的UbuntuZephyr的编译本身还好但QEMU运行多线程网络仿真或图形仿真时特别慢。我试过在虚拟机里跑qemu_x86的hello_world终端日志正常但CPU占用非常高模拟运行速度比真实机器慢好几倍。排查思路是这样的QEMU在Linux下默认利用KVM硬件虚拟化加速。但虚拟机里再跑虚拟机需要宿主机开启嵌套虚拟化VMware里叫虚拟化Intel VT-x/EPT或AMD-V/RVI。如果你的CPU支持硬件虚拟化建议在虚拟机设置里开启这个选项QEMU会直接使用KVM性能提升非常明显。如果嵌套虚拟化开不了或者就是单纯不想在QEMU上纠结性能我的建议是换用native_sim目标。native_sim编译出来的像普通进程一样直接跑在Ubuntu上没有QEMU那一层模拟调试体验好得多。5.5 坑五west update反复失败west update拉取依赖仓库时失败也是给人印象很深的一类问题。现象就是某个仓库一直拉到一半就中断重试还是老位置断。这通常是网络稳定性的问题跟Zephyr本身无关。我的处理顺序是先检查Git配置git config --global --get http.postBuffer如果没设置先调大再重试git config --global http.postBuffer 524288000。再检查仓库锁的位置进入~/zephyrproject目录west update失败后rm -rf .west里的临时锁文件避免west认为更新还在进行。最后建议在west update之前先确认网络通顺然后一次性执行中间别去切换网络。多次中断后就算最后拉成功部分git对象也可能有异常保险起见可以cd zephyr git status看看仓库状态是否干净。最后一招是乾坤大挪移如果某个依赖仓库始终拉不动可以直接去对应的Git仓库手动clone到~/zephyrproject/modules下对应位置然后回到zephyr仓库里west update让它只补校验。这个方法略微hack但确实能绕过顽固的断点问题。5.6 几个小毛病的速查表顺手列一张速查表覆盖我周边朋友问得最多的几个问题症状根因快速处置west命令找不到~/.local/bin不在PATHexport PATH$HOME/.local/bin:$PATH构建时ImportErrorwest Python依赖缺失pip3 install --user -r zephyr/scripts/requirements.txt缺dtc命令没装device-tree-compilersudo apt install device-tree-compiler编译报错缺少gperf没装gperfsudo apt install gperf设备树解析报错SDK版本和Zephyr不匹配确认release note里的SDK版本建议hello_world能编过但跑不起来QEMU或native_sim启动参数冲突删除build目录重新west build -p auto这张表是我在实际答疑过程中沉淀出来的前四个坑基本覆盖了80%的新手安装问题。6. 环境跑通之后接下来往哪儿走6.1 从sample到自己的应用Zephyr环境装好不是终点而是起点。Zephyr的sample目录里有一大批官方示例按功能分类排好基础内核、驱动程序、蓝牙、传感器、网络协议栈、显示等。我的建议路线是先把samples/basic下的例子逐个跑一遍。hello_world、thread、synchronization适用于理解线程调度philosophers和serving_fibonacci适合理解信号量和优先级。再跑samples/drivers里的传感器示例。大多数板子都带几个传感器温度、加速度、光线等可以把它们的驱动跑起来看串口输出。然后自己动手写一个应用工程。Zephyr 3.x开始推荐用west create创建独立应用工程。这样应用代码和SDK分离版本管理更清晰。6.2 连真实开发板前必须准备的工具用QEMU验证完环境后大家可以考虑用真实开发板做一次实际烧录。建议准备一块官方支持的开发板nRF52系列、STM32系列、ESP32系列都行、一根数据线以及对应的烧录调试工具nRF系通常用nRF Command Line Tools或pyOCDSTM32系用ST-Link工具链ESP32系有独立的esptool。Zephyr对这三类板子的支持都是官方级别的选哪家都能体验到从编译到烧录的全流程。烧录命令是west flash这个命令会自动识别调试器并烧录。第一次连接时可能需要用一个jasn如果报权限错误比如无法访问USB设备先把当前用户加入plugdev组或者dialout组sudo usermod -aG dialout $USER然后重新登录一次再试。6.3 长期开发中的环境维护习惯整套环境搭好之后我在实际开发中逐渐总结出几个维护习惯升级之前先备份升级Zephyr主仓库或SDK之前先把current build的build目录清掉。Zephyr官方推荐环境版本尽量保持一致别半路换SDK版本还指望旧构建缓存能继续用。定期west updateZephyr的modules生态更新比较活跃建议每周拉一次上游更新。如果在一个长期项目中想锁定版本就在west.yml里固定manifest的revision别乱动。同一台机器维护多个项目Zephyr支持workspace模型一个workspace下可以有多个应用工程。用west清单管理切换项目时只要west update一下就行不用重复安装SDK。还有一个小技巧在老版本Zephyr里source zephyr/zephyr-env.sh是常用的环境加载方式但新版本推荐直接依赖~/.zephyrrc和west本身不再需要手动source。如果你在旧教程里看到source命令可以直接忽略只要确认ZEPHYR_SDK_INSTALL_DIR和ZEPHYR_TOOLCHAIN_VARIANT两个变量正确就行。最后说几句实在的这次从零搭环境给我的最大感触是Zephyr本身的坑往往不在Zephyr而在宿主系统的版本冲突和路径设置上。每次west update后顺手记一下当前版本出问题时先确认cmake、Python和SDK的版本对应关系很多问题能省不少排查时间。另外别贪多把hello_world和blinky跑熟了再往驱动和协议栈上扎这条路比一口气读十篇教程都更快见效。