资讯详情

资讯详情

Zephyr实践:从零搭建开发环境并跑通Hello World

Hello World 大概是每个程序员的第一行代码。当年大一新生敲下printf(hello world!\n)的那一刻确实激动但那种成就感顶多持续三秒因为电脑装了编译器就能跑。而嵌入式领域的 Hello World 完全不是这个剧本交叉编译、目标板、烧录器、串口调试一环扣一环。尤其遇到 Zephyr 这种号称“模块化物联网 RTOS”的东西光开发环境搭建就能劝退一半人。这篇记录是我个人 Zephyr 实践的第一篇目标只有一个把 Zephyr 开发环境搭建这道最劝退的坎完全趟平然后跑通 Hello World 并理解背后发生了什么。不管你是在校学生、做物联网想换 RTOS 的工程师还是手里吃灰着几块开发板的爱好者这篇文章应该能帮你少走很多弯路。我会以 Ubuntu 22.04 为主最后单独讲 Windows/WSL2 的差异并整理一份常见报错排查表。1. Zephyr 到底是啥以及为什么环境搭建最劝退1.1 一句话理解 Zephyr你可以把 Zephyr 理解成“嵌入式界的 Linux 内核”当然它不是 Linux它要小得多但它和 Linux 有几件事很像开源、由 Linux 基金会托管、有完整的生态、用设备树描述硬件、用 Kconfig 做配置。它跑在内存只有几 KB 到几 MB 的 MCU 上目标是物联网终端、可穿戴设备、工业传感器这一类场景。和 FreeRTOS 这类传统 RTOS 相比Zephyr 的最大特点是“原生支持蓝牙、Wi-Fi、Thread、802.15.4 等无线协议栈”并且把驱动框架、电源管理、安全机制都整合进来了。也就是说你做一个联网的物联网设备不再需要自己东拼西凑各种协议栈Zephyr 已经帮你把轮子造好你只需要把应用写出来。不过这些优势对应的代价就是——学习门槛偏高。Zephyr 的构建系统不是简单的 Makefile而是 CMake Ninja west 的组合拳还引入了设备树Device Tree和 Kconfig。新手最容易栽的坑不是写应用而是“环境搭不起来”代码连编译都过不去。1.2 搭建环境的三个卡点工具链、SDK 与 west先说工具链。MCU 和 PC 的 CPU 架构不同你要在 PC 上编译出可以在 MCU 上运行的二进制就需要交叉编译工具链。Zephyr 官方提供了一套预编译好的 Zephyr SDK里面包含了 GNU 工具链、QEMU 模拟器、OpenOCD 调试器等一堆东西省去了你自己去配 arm-none-eabi-gcc 的麻烦。再说是 SDK。Zephyr SDK 不只是编译器它还内置了 QEMU 和调试工具这也是为什么你即使没有开发板也可以先在 PC 上模拟运行 hello_world。对新手来说先跑通模拟器再碰真实硬件是最稳的路径。最后是 west。west 是 Zephyr 的“元工具”负责拉取和管理整个 Zephyr 工程的多个仓库。Zephyr 项目本身不是一个单独的 Git 仓库而是由 zephyr、hal、cmsis、openthread 等一堆子仓库组成。如果手动用 git clone非常容易漏掉模块或者版本不匹配而 west 就是用来干这个活的管理工具类似于 Linux 下的 repo。1.3 动手前先备齐这些东西正式开始搭建之前我建议你先确认一下手上的资源够不够避免装到一半发现磁盘满了。一台安装了 Ubuntu 22.04 的电脑x86_64 架构虚拟机也行建议内存 8GB 以上磁盘剩余空间 20GB 以上实测 Zephyr 源码加 SDK 加构建中间文件轻轻松松吃掉十几个 GB能稳定访问 GitHub如果网速不理想大概率会遇到下载中断建议提前更换镜像源或者准备好稳定的代理下载方式一块可选的目标开发板比如经典的 STM32F103C8T6蓝丸、ESP32、nRF52840 都可以没有板子的话先跑 QEMU 模拟器也不会影响学习有句话我得放在前面Zephyr 的版本更新非常快主线几乎每周都有合入。建议新手不要追主线直接用 LTS 版本比如当前阶段的 3.7 LTS 就非常稳。后面所有的命令我都是基于这个版本来写的。2. Ubuntu 下从零搭建 Zephyr 开发环境2.1 安装系统依赖包别上来就 pip很多新手拿到教程第一件事就是pip install west然后发现后面编译各种报错缺这个缺那个。原因很简单Zephyr 构建过程依赖的很多工具不在 Python 生态里得先用系统包管理器装好。打开终端先更新源再安装这一串包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逐个解释一下这些包是干什么的因为知其然才能少踩坑ninja-buildZephyr 默认的构建后端比 make 快很多CMake 会生成 build.ninja 然后交给 ninja 执行device-tree-compiler设备树编译器Zephyr 用设备树描述硬件资源配置编译时必须把 .dts 源文件编译成 .dtbgperf用于生成完美哈希表Zephyr 内核里一些系统调用查表逻辑依赖它libmagic1文件类型识别库Zephyr 的构建系统会用它检测文件类型gcc-multilib和g-multilib编译 32 位和 64 位 host 工具时可能会用到libsdl2-devQEMU 模拟器图形显示依赖如果你要在 QEMU 里跑带屏的例子这个必须装libssl-dev在这条命令里我没有写进去但建议单独装一下sudo apt install libssl-dev因为后面编译某些依赖时可能会用到 OpenSSL 头文件。装完这些基础包之后再去管 Python 侧的工具顺序就对了。2.2 用 venv 安装 west绕开 Python 权限坑Python 这块我强烈建议用虚拟环境而不是直接全局 pip install。Ubuntu 22.04 自带的 Python 3.10 已经启用了 PEP 668 机制直接pip install west很可能报error: externally-managed-environment提示你系统 Python 环境被系统包管理器托管不允许随意装包。创建一个独立的虚拟环境一劳永逸python3 -m venv ~/zephyr-venv source ~/zephyr-venv/bin/activate pip install --upgrade pip pip install west装完之后验证一下west --version如果输出类似West version: v1.2.0就说明成功了。注意每次打开新终端如果要用 west都需要先执行source ~/zephyr-venv/bin/activate。嫌麻烦的话可以把这一行写进~/.bashrc里echo source ~/zephyr-venv/bin/activate ~/.bashrc但我不太建议直接把 venv 自动激活写进全局配置因为 venv 会覆盖系统 Python 环境后面你开其他项目可能莫名其妙用错 Python。我更推荐的做法是用 west 的时候手动激活或者直接用绝对路径调用比如~/zephyr-venv/bin/west --version。2.3 下载 Zephyr SDK 并配置环境变量接下来是重头戏下载 Zephyr SDK。这里说的 SDK 不是 Python 依赖而是官方预编译好的跨平台工具链。官方下载地址在 GitHub 的 sdk-ng 仓库 releases 页面我写这篇记录时用的版本是 0.17.0你可以根据自己的系统架构选择对应的 tar.xz 文件。cd ~ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.0/zephyr-sdk-0.17.0_linux-x86_64.tar.xz tar xf zephyr-sdk-0.17.0_linux-x86_64.tar.xz cd zephyr-sdk-0.17.0 ./setup.shsetup.sh会把 SDK 里的工具链路径注册到系统里过程中可能会询问你是否要安装 host 工具选 yes 即可。安装完成后SDK 目录里会多出一个sysroots目录里面装好了 QEMU、OpenOCD 等运行环境。然后配置环境变量。新建一个专门的文件管理 Zephyr 相关变量避免把~/.bashrc弄乱echo export ZEPHYR_TOOLCHAIN_VARIANTzephyr ~/.zephyrrc echo export ZEPHYR_SDK_INSTALL_DIR$HOME/zephyr-sdk-0.17.0 ~/.zephyrrcZEPHYR_TOOLCHAIN_VARIANTzephyr是告诉 Zephyr 构建系统使用 Zephyr SDK 自带工具链而不是你自己在系统里装的 arm-none-eabi-gcc。这一步很关键漏掉它的话CMake 会提示找不到工具链或者在你系统里乱找一通最后给你一个摸不着头脑的报错。2.4 拉取工程并通过 west update 同步模块SDK 装好之后就可以初始化 Zephyr 的工程目录了。新建一个目录然后用 west init 初始化mkdir ~/zephyrproject cd ~/zephyrproject west init -m https://github.com/zephyrproject-rtos/zephyr --mr v3.7.0这里-m指定 manifest 仓库--mr v3.7.0指定分支或 tag。如果你网速慢可以把 GitHub 地址替换成你本地的 mirror 仓库地址或使用代码托管平台提供的镜像效果是一样的。初始化完成后目录下会有一个.west目录和一个zephyr仓库。此时还不能直接编译需要执行west update这个命令会读取 manifest 文件把 zephyr、hal、cmsis、openthread 等所有子模块全部拉取到本地。这一步比较耗时取决于你的网络可能需要十几分钟。注意west update 之后还要安装 Zephyr 的 Python 依赖pip install -r zephyr/scripts/requirements.txtrequirements.txt 里包含 pyelftools、pykwalify、canopen 等一堆库编译时都会用到。漏掉这一步运行 west build 时可能会报No module named elftools之类的错误。到这里开发环境的“骨架”就算搭好了。你可以执行west topdir确认工程根目录输出~/zephyrproject就是正确的。3. Hello World 实战构建、运行与输出3.1 先跑通 qemu_x86不碰硬件也能练手环境搭完不建议一上来就烧板子先用模拟器跑通再说。Zephyr SDK 自带 QEMU支持很多模拟目标板其中qemu_x86是最常用的一个。在~/zephyrproject目录下执行source ~/zephyr-venv/bin/activate cd ~/zephyrproject west build -b qemu_x86 zephyr/samples/hello_world第一次构建会比较慢因为要生成设备树、配置 Kconfig、编译内核大概一两分钟取决于机器性能。构建成功后会在build目录下生成zephyr.elf、zephyr.bin等文件。然后运行west build -t run此时 QEMU 窗口弹出终端里会显示*** Booting Zephyr OS build v3.7.0 *** Hello World! qemu_x86看到这行输出你的 Zephyr 环境就算真正跑通了。用CtrlA松开后再按X可以退出 QEMU 模拟器。如果你连图形界面都懒得开也可以选择native_posix板子直接把 Zephyr 编译成一个 Linux 可执行文件跑在宿主机上west build -b native_posix zephyr/samples/hello_world -p ./build/zephyr/zephyr.exe这种方式启动更快调试也更方便非常适合写业务逻辑时本地验证唯一的限制是没法模拟真实 MCU 的硬件外设。3.2 再看懂 hello_world 的三个文件跑通之后建议打开zephyr/samples/hello_world目录看看这个演示项目到底由什么组成。目录下的核心文件只有三个src/main.c、CMakeLists.txt、prj.conf。main.c 的内容非常简洁#include zephyr/kernel.h int main(void) { printk(Hello World! %s\n, CONFIG_BOARD); return 0; }注意两点第一头文件不是标准 C 的stdio.h而是zephyr/kernel.h这是 Zephyr 所有应用都要包含的内核头文件第二打印函数不是 printf而是 printk这是 Zephyr 提供的内核打印函数通过串口输出不依赖标准 C 库。CMakeLists.txt 是构建入口cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(hello_world) target_sources(app PRIVATE src/main.c)核心就是find_package(Zephyr)它会去查找 Zephyr 的构建系统然后ZEPHYR_BASE环境变量就是定位 Zephyr 源码的路径。这也是为什么我前面专门提到环境变量配置很重要如果ZEPHYR_BASE没有设置或者指向错误这一步就会失败。prj.conf 在 hello_world 里是空的或者只有很少的配置项。Zephyr 的配置体系是 Kconfig就是内核配置系统之后你要加 Wi-Fi、加蓝牙、开日志都是在这里加CONFIG_XXXy之类的配置。我再强调一下构建核心逻辑Zephyr 的构建分两层一层是配置层Kconfig通过prj.conf和其他配置文件决定“编译哪些功能”另一层是硬件描述层设备树通过.dts文件决定“硬件上有什么、驱动怎么连接”。这两套体系是 Zephyr 开发绕不开的核心建议在跑通 Hello World 之后花点时间把这两个机制理解透。3.3 烧到真实板卡以 STM32F103 为例模拟器跑通之后再烧到真实板卡上你就会发现其实没那么复杂。我手上有一块经典的 STM32F103C8T6 蓝色板Zephyr 官方支持它的 target 是stm32f103_mini。先确认你已经用 ST-Link 连接好板子然后执行west build -b stm32f103_mini zephyr/samples/hello_world -p west flash-p参数是 pristine 模式会清掉上一次的构建缓存防止旧配置残留。west flash会读取板子自带的 flash 配置自动调用 openocd 通过 ST-Link 烧录。Zephyr SDK 自带 openocd所以一般不需要你手动安装。烧录完成后把板子的串口通过 USB-TTL 接到电脑上查看设备节点ls /dev/ttyUSB*然后用任意串口工具连接波特率 115200就能看到输出*** Booting Zephyr OS build v3.7.0 *** Hello World! stm32f103_mini这里顺便说个细节Zephyr 的 hello_world 默认使用串口输出所以CONFIG_SERIAL和CONFIG_CONSOLE这些配置通常会由板级配置默认开启不需要用户在 prj.conf 里手动配置。但如果你后面移植到自行设计的板卡串口默认不启用printk 就完全没有输出这个坑我后面会讲怎么排查。4. Windows 用户怎么搭WSL2 与原生方案4.1 首选 WSL2把 Ubuntu 流程平移过来Zephyr 官方文档里Linux 是一等公民Windows 虽然也能跑但官方明确建议使用 WSL2 获得最佳体验。原因很简单Zephyr 的构建工具链、Device Tree 编译器和 OpenOCD 在 Windows 下要么不好装要么版本不匹配而 WSL2 就是一个完整的 Linux 环境能把你从环境地狱里解救出来。Windows 10 以上系统以管理员身份打开 PowerShell执行wsl --install -d Ubuntu-22.04装好之后重启进入 Ubuntu 子系统然后所有步骤就和我前面写的完全一样了先更新 apt再装系统依赖再装 west 和 SDK。你可以照着第二节从头操作一遍没有任何区别。有一件事需要留意WSL2 对 USB 设备的支持不像原生 Linux 那么直接。开发板通过 USB 连接的 ST-Link 调试器或串口在 WSL2 里默认是不可见的。如果你主要烧录 STM32 这类 USB 调试设备建议用 usbipd-win 把 USB 设备转发到 WSL2 里或者干脆把编译放在 WSL2烧录工具放在 Windows 侧。我个人更推荐后者在 WSL2 里west build生成的 .bin 文件放到 Windows 目录再用 Windows 下的 STM32CubeProgrammer 或 OpenOCD 烧录。这种方式最省心不折腾 USB 转发。4.2 原生方案也可以但你要有心理准备如果你的开发任务不涉及 ST-Link 这类 USB 调试器而是用 J-Link 或者网络调试器在 Windows 原生环境下搭建也不是不行。思路和 Linux 类似但每一步都多一点变数。你需要先安装这些基础工具Python 3.10 以上安装时勾选 Add Python to PATHCMake 3.20 以上建议用 chocolatey 或 cmake 官方安装包Ninja用pip install ninja或 chocolatey 安装MSYS2 环境用它安装dtc和gperf因为 Windows 下没有简洁的二进制包装好之后再执行pip install west然后初始化工程下载 SDK。Windows 版本的 SDK 是一个.exe安装器运行之后会装到指定目录。环境变量方面需要手动设置ZEPHYR_TOOLCHAIN_VARIANTzephyr和ZEPHYR_SDK_INSTALL_DIR这点和 Linux 没有区别。原生方案的坑主要体现在路径上。如果安装路径里有中文或者空格CMake 的某些模块处理不好会直接报错。Git 的 submodule 拉取在 Windows 下也有可能出现符号链接解析失败的问题尤其是west update拉取 hal_espressif 这类大仓库时莫名报错就问你怕不怕。所以在 Windows 上我的态度很明确能上 WSL2 就上 WSL2不要和自己过不去。5. 环境搭建常见问题与排查实录5.1 west 装好了却找不到命令这是一个出现频率极高的新手问题。装完 west 之后在终端敲west --version报错command not found。原因和解决方案基本只有两类你用了 venv但没有激活虚拟环境你用了pip install --user west而 Python 的 bin 目录不在 PATH 里第一类很好解决source ~/zephyr-venv/bin/activate即可。第二类先执行python3 -m site --user-base找到用户级 Python 目录然后在 PATH 里加上base/bin。比如输出是/home/user/.local那就执行export PATH$HOME/.local/bin:$PATH如果不想每次都手动 export把它写进~/.bashrc。但最推荐的还是直接用 venv路径固定、依赖隔离、不会污染系统 Python。5.2 CMake 编译时报 SDK 或工具链找不到如果你执行west build时看到这样的报错Zephyr version cannot be determined CMake Error: The following variables are used in this project, but they are set to NOTFOUND ZEPHYR_SDK_INSTALL_DIR基本可以断定是环境变量问题。检查一下echo $ZEPHYR_SDK_INSTALL_DIR echo $ZEPHYR_TOOLCHAIN_VARIANT如果输出为空说明你的~/.zephyrrc没有被加载。Zephyr 的构建脚本会自动读取~/.zephyrrc但前提是你的终端环境变量里没有显式覆盖它。如果你用的是 WSL2 或者从~/.bashrc里 export 过要在新终端生效必须重新 source。还有一种情况是west update没执行成功导致zephyr仓库缺少子模块。这时候直接重跑一次west update然后重新west build -p。5.3 烧录成功但串口没有任何输出代码编译烧录都成功但打开串口什么都没有。这个问题我在各种单片机开发者身上见得太多了原因也五花八门波特率不对Zephyr 默认 UART console 波特率是 115200但板级配置文件可能改过串口设备连错了比如板子有两个串口程序输出走的是另一个板子没有复位或启动配置不对有些板子烧录之后要手动复位一次USB-TTL 的 TX/RX 接反了这是最基础但最容易犯的错CONFIG_SERIAL没有使能hello_world 虽然默认在标准板型上是开的但自定义板卡就必须自己确认排查思路很简单先从硬件着手确认 TX/RX 连线、确认波特率再用短接测试 USB-TTL 是否正常。硬件没问题之后再检查软件配置看 prj.conf 和板级 defconfig 里串口使能情况。最后逼不得已用逻辑分析仪抓一下 TX 引脚上有没有数据波形有波形就是软件或连线问题没波形就是程序压根没跑到串口初始化的地方。5.4 下载慢、编译卡、磁盘爆掉等问题west update拉取大仓库慢是最常见的问题。Zephyr 的 hal 仓库动辄几百 MB一些模块的历史记录又重。解决办法有几个第一用git clone --depth 1之类的浅克隆方式但 west 本身不支持直接配置浅克隆需要手动修改 manifest 或者使用 git 的配置项第二使用镜像仓库替换 manifest 里的 URL速度会有明显提升第三如果只是学习暂时不需要的模块可以不管反正编译时会自动跳过未拉取的模块吗不一定所以最好还是完整 update。编译卡这个问题多数是内存不足。Zephyr 编译一个工程内存占用随随便便上 2GB如果虚拟机只给了 4GB 内存很容易出现编译进程被 OOM killer 杀掉。解决方案是加大虚拟机内存或者减少并行任务。如果你用的是高版本 CMake可以试试加-j2参数限制并行度west build -b qemu_x86 zephyr/samples/hello_world -- -j2磁盘空间也是容易被忽略的一点。一个工程构建目录大约是几百 MBSDK 和解压后的源码加起来十几个 GB。如果磁盘剩余空间不足 10GB建议先清理一下或者直接把~/zephyrproject和 SDK 放到空间充足的磁盘分区。5.5 问题速查表症状可能原因解决方向west command not foundvenv 未激活或 PATH 未配置source venv 或手动加入 PATH找不到 ZEPHYR_BASE环境变量未加载检查 ~/.zephyrrc 和 ~/.bashrcCMake 报 SDK 版本不匹配SDK 与 Zephyr 版本不兼容使用 Zephyr 3.7 LTS SDK 0.17west build 卡住不动网络或内存问题检查网络、加大内存或降并行度刷板后串口无输出TX/RX 接反、波特率错误、串口未使能先查硬件再查串口配置构建报 No module named elftools未安装 requirementspip install -r zephyr/scripts/requirements.txt上一次构建缓存导致异常构建缓存脏了west build -p 强制清缓存重建6. Hello World 之后下一步怎么走6.1 从 printk 到点灯多线程与 GPIO跑通 Hello World 之后建议下一个试水的目标是 blinky 点灯例程它比 Hello World 信息量大很多涉及 GPIO 和内核延时机制。west build -b qemu_x86 zephyr/samples/basic/blinky -p在 QEMU 里运行你能看到模拟的 LED 周期闪烁。如果你有真实开发板把它烧到stm32f103_mini上可以看到板上的 PC13 引脚 LED 在闪。这个例程会引入设备树里 GPIO 节点的使用方式比如gpio_dt_spec和gpio_pin_configure这些 API它们也是后续所有外设驱动开发的基础。跑通 blinky 之后再去接触线程创建、消息队列、传感器驱动会顺畅很多。Zephyr 的多种调度方式、线程优先级、内核对象逻辑上比裸机开发高一个维度但一旦适应了这种“跑 RTOS”的思维方式写复杂物联网应用会轻松不少。6.2 想移植自己的板子要看哪些东西Zephyr 官方支持大量开发板但不是每块板子都能直接跑。如果你手里的板子不在boards目录里就涉及到“移植”这个概念。新手听到移植可能觉得高深其实对 Zephyr 来说大部分工作已经做了。你需要做的事可以简单分为三步在boards/arch/your_board目录下新建板级目录添加board.cmake、Kconfig.board、Kconfig.defconfig、board_defconfig、board.dts、board.yaml这几个文件设备树描述硬件把 SoC 型号、Flash/RAM 大小、外设地址和中断这些信息写进 .dts板级 defconfig 配置默认使能的外设和引脚复用听起来不复杂但这里面坑很多尤其是引脚复用pinmux和设备树之间的对应关系搞错一个引脚就是跑不起来。如果你只是想让手头的板子快速跑起来最现实的做法是找一个官方支持的、和你板子同系列的 target看它的配置文件是怎么写的然后照着改。Zephyr 源码里最不缺的就是参考示例复制粘贴再修改比从零画图快得多。6.3 我踩过的一些坑写给后来的你最后分享几个纯个人体会。第一SDK 版本和 Zephyr 版本尽量配对使用。Zephyr 3.7 对应 SDK 0.17Zephyr 4.x 对应 SDK 0.18 以上。版本错配最容易触发一些匪夷所思的编译错误而我去查官方 release notes 才发现是版本问题。新版本发布时sdk-ng 的 release 页面会明确写明支持哪些 Zephyr 版本对号入座最安全。第二第一次编译别急着优化。我一个朋友为了图快把west build包装成一个脚本加了各种缓存清理策略结果环境变量传递出问题折腾了一下午。老老实实按官方文档的路径走跑通一次之后你自然而然地会明白哪些可以优化。第三保存好你的环境变量配置。我一般会把 Zephyr 相关配置拆到~/.zephyrrc单独管理换机器、换项目都能快速恢复也不用担心把~/.bashrc折腾得一团糟。第四多读源码少相信博客。Zephyr 最大的财富是它高可读性的源码和详尽的文档。遇到不懂的 API直接在zephyr/include目录里 grep通常能得到比任何二手资料都准确的答案。从环境搭建到 Hello World 再到第一个点灯例程Zephyr 的学习曲线前期确实略陡但跨过这道坎之后你会发现它的内核模块、驱动框架和构建体系设计得非常清晰值得投入时间。下一个阶段我建议你认真啃一下设备树和 Kconfig 这两个机制它们是 Zephyr 的灵魂。到时候再回头来看这篇环境搭建教程你会对每一步的配置有更深刻的理解。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →