ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Zephyr RTOS在STM32F103C8T6上的完整开发指南:从环境搭建到VSCode调试

2026/8/2 12:02:08 拓冰建站 浏览量
Zephyr RTOS在STM32F103C8T6上的完整开发指南:从环境搭建到VSCode调试

大家好,我是专注于嵌入式开发的技术博主。最近在尝试将 Zephyr RTOS 应用到 STM32F103C8T6 这款经典的“蓝色药丸”最小系统板上时,发现很多教程要么环境搭建不全,要么编译过程报错不断,特别是配合 VSCode 进行开发时,配置更是让人头疼。本文将为你梳理一套从零开始、手把手式的完整流程,涵盖 Zephyr 环境搭建、VSCode 配置、项目编译、烧录与调试的全过程。无论你是刚接触 Zephyr 的新手,还是想将现有 Keil 项目迁移到更现代的 VSCode + Zephyr 开发流,这篇文章都能提供清晰的指引和可复现的代码示例。

1. 背景与核心概念

在深入实操之前,我们有必要厘清几个核心概念,这能帮助你理解我们正在构建的整个技术栈。

1.1 什么是 Zephyr RTOS?

Zephyr 是一款由 Linux 基金会托管的、开源、可扩展的实时操作系统(RTOS)。它专为资源受限的嵌入式设备设计,支持超过 450 款开发板和 30 多种架构,包括 ARM Cortex-M(如 STM32)、RISC-V、Xtensa 等。与 FreeRTOS、uC/OS 等传统 RTOS 相比,Zephyr 的特点在于其高度模块化、强大的配置系统(基于 Kconfig 和 Devicetree)以及活跃的社区生态。它不仅仅是一个内核,更是一个完整的 SDK,提供了丰富的组件,如文件系统、网络协议栈(包括 LwM2M、CoAP)、蓝牙协议栈等,非常适合物联网(IoT)设备的开发。

1.2 为什么选择 VSCode + Zephyr?

传统的嵌入式开发往往依赖于 Keil MDK、IAR 等商业 IDE。这些工具功能强大,但通常价格昂贵,且跨平台支持有限。VSCode 作为一个免费、开源、跨平台的代码编辑器,凭借其海量的插件生态和强大的可定制性,已成为现代开发者的首选。将 Zephyr 与 VSCode 结合,可以带来以下优势:

  • 统一的开发体验:在 Windows、Linux、macOS 上使用相同的工具链。
  • 强大的代码智能感知:通过 C/C++ 插件获得代码补全、跳转、错误检查等功能。
  • 集成化的构建与调试:可以直接在 VSCode 内调用 Zephyr 的构建系统(West)进行编译,并通过 Cortex-Debug 等插件进行图形化调试。
  • 版本控制友好:VSCode 与 Git 深度集成,方便代码管理。

1.3 目标硬件:STM32F103C8T6 最小系统板

STM32F103C8T6 基于 ARM Cortex-M3 内核,主频 72MHz,拥有 64KB Flash 和 20KB RAM,因其极高的性价比和丰富的资源,常被称为“蓝色药丸”,是学习 STM32 和嵌入式开发的经典入门型号。我们将以此板卡为目标,演示如何运行一个基础的 Zephyr 应用(如闪烁 LED)。

2. 环境准备与版本说明

搭建一个稳定可用的 Zephyr 开发环境是成功的第一步。以下步骤在 Windows 10/11 系统上验证通过,同样适用于 Linux 和 macOS(命令略有不同)。

2.1 安装必备工具

Zephyr 的开发依赖一系列工具,我们使用官方推荐的安装方式。

1. 安装 Python 3.8 或更高版本Zephyr 的工具链(如 West)由 Python 编写。请从 Python官网 下载并安装。务必在安装时勾选 “Add Python to PATH”。

2. 安装 Git用于获取 Zephyr 源代码。从 Git官网 下载安装。

3. 安装 WestWest 是 Zephyr 的元工具,用于管理多个仓库(Manifest)和执行构建、烧录等命令。在命令行中执行:

pip install west

安装完成后,运行west --version确认安装成功。

4. 获取 Zephyr 源代码并安装 Python 依赖选择一个合适的目录(路径不要有中文或空格),执行以下命令。这里我们使用官方 Manifest,它定义了 Zephyr 项目及其所有模块。

# 初始化 west 工作区,并克隆 manifest 仓库 west init zephyrproject # 进入工作区目录 cd zephyrproject # 拉取 Zephyr 源码及所有模块 west update # 导出 Zephyr 的 CMake 包,使得在其他目录也能找到 Zephyr west zephyr-export # 安装 Zephyr 所需的 Python 依赖包 pip install -r zephyr/scripts/requirements.txt

这个过程会下载大量文件,请保持网络通畅。

5. 安装工具链Zephyr 需要针对目标架构的编译器。对于 ARM Cortex-M(STM32),我们安装 GNU Arm Embedded Toolchain。

  • Windows: 下载 gcc-arm-none-eabi 安装包并安装。建议安装到C:\Program Files\Arm GNU Toolchain arm-none-eabi这类简单路径。
  • Linux/macOS: 通常可以通过包管理器安装,如sudo apt install gcc-arm-none-eabi(Ubuntu)。

安装后,需要将工具链的bin目录添加到系统的PATH环境变量中。例如,Windows 下路径可能是C:\Program Files\Arm GNU Toolchain arm-none-eabi\13.2.rel1\bin

6. 安装 CMake 和 NinjaCMake 是 Zephyr 的构建系统生成器,Ninja 是构建后端。

  • Windows: 从 CMake官网 下载安装包,安装时选择“为所有用户添加 CMake 到系统 PATH”。Ninja 可以从 其 GitHub 发布页 下载ninja-win.zip,解压后将ninja.exe所在目录也加入PATH
  • Linux/macOS:sudo apt install cmake ninja-build(Ubuntu)。

2.2 安装并配置 VSCode

  1. 从 VSCode官网 下载并安装。
  2. 安装以下必备扩展:
    • C/C++(ms-vscode.cpptools): 提供代码智能感知、调试支持。
    • CMake Tools(ms-vscode.cmake-tools): 集成 CMake 构建、配置、调试。
    • Cortex-Debug(marus25.cortex-debug): 用于 ARM Cortex-M 芯片的图形化调试。
    • (可选)Zephyr IDE(zephyr.zephyr-ide): 提供 Zephyr Kconfig 和 Devicetree 的语法高亮和预览。

3. 创建并构建第一个 Zephyr 项目

环境就绪后,我们开始创建第一个项目。Zephyr 提供了丰富的示例(Samples),我们从最简单的blinky(LED 闪烁)开始。

3.1 创建项目工作区

我们不直接在zephyrproject目录下开发,而是创建一个独立的应用目录,这样更清晰。

# 假设在 D:\ 盘根目录 mkdir d:\my_zephyr_app cd d:\my_zephyr_app

3.2 初始化应用程序

Zephyr 应用需要遵循固定的目录结构。最简单的方式是复制一个示例。

# 将 zephyr 的 blinky 示例复制到当前目录的 app 文件夹下 cp -r %ZEPHYR_BASE%\samples\basic\blinky app # 注意:%ZEPHYR_BASE% 是环境变量,指向 zephyrproject\zephyr。如果未设置,请使用绝对路径,例如: # cp -r d:\zephyrproject\zephyr\samples\basic\blinky app

现在你的my_zephyr_app目录下有一个app文件夹,里面就是blinky示例的源码。

3.3 为 STM32F103C8T6 配置项目

默认的blinky示例可能不是针对 STM32F103C8T6 的。我们需要创建一个构建配置。

  1. 创建build目录并进入

    mkdir build cd build
  2. 使用 CMake 配置项目: 我们需要告诉 CMake 我们的目标硬件和工具链。通过-DBOARD参数指定板型。STM32F103C8T6 最小系统板在 Zephyr 中对应的板型名称通常是nucleo_f103rb(因为引脚兼容),或者更通用的stm32f103c8t6(如果社区有支持)。我们以nucleo_f103rb为例,它被广泛支持。

    # 在 build 目录下执行 cmake -GNinja -DBOARD=nucleo_f103rb ..\app
    • -GNinja: 指定生成 Ninja 构建文件。
    • -DBOARD=nucleo_f103rb: 设置目标板。
    • ..\app: 指定 CMakeLists.txt 所在的源目录(上一级的 app 文件夹)。

    执行成功后,会在build目录下生成一系列文件,包括zephyr/.config(Kconfig 配置) 和zephyr/include/generated/(Devicetree 生成头文件)。

  3. 构建项目: 配置完成后,使用 Ninja 进行编译。

    ninja

    如果一切顺利,你将看到编译进度,最后输出类似[100%] Linking C executable zephyr\zephyr.elf[100%] Built target zephyr_final的信息。编译产物位于build\zephyr目录下,最重要的文件是zephyr.hexzephyr.binzephyr.elf

4. 在 VSCode 中集成与优化工作流

命令行构建虽然强大,但在 VSCode 中集成可以极大提升效率。

4.1 使用 VSCode 打开项目

用 VSCode 打开my_zephyr_app文件夹。

4.2 配置 CMake Tools 扩展

  1. 按下Ctrl+Shift+P,输入 “CMake: Select a Kit”,选择你安装的 GCC Arm 工具链,例如 “GCC 13.2.1 arm-none-eabi”。
  2. 再次按下Ctrl+Shift+P,输入 “CMake: Select Variant”,可以选择DebugRelease
  3. 最后,输入 “CMake: Select Configure Preset”。如果没有预设,我们需要创建一个。在项目根目录(my_zephyr_app)下创建.vscode/settings.json文件,并添加 CMake 配置预设:
    { "cmake.configureSettings": { "BOARD": "nucleo_f103rb" }, "cmake.sourceDirectory": "${workspaceFolder}/app", "cmake.buildDirectory": "${workspaceFolder}/build", "cmake.generator": "Ninja", "cmake.configureArgs": [ "-GNinja" ] }
  4. 现在,你可以点击 VSCode 底部状态栏的 “Build” 按钮(或按F7)来编译项目,效果与命令行运行ninja相同。输出窗口会显示构建日志。

4.3 配置调试环境

调试是开发的关键。我们需要配置 Cortex-Debug 插件。

  1. 确定调试探头:STM32F103C8T6 最小系统板通常通过 ST-LINK 或 J-Link 进行调试。这里以最常见的 ST-LINK 为例。

  2. 安装 OpenOCD:OpenOCD 是开源的片上调试器。从 OpenOCD 官方 或 xPack 项目 下载 Windows 版本并解压,将其bin目录加入PATH

  3. 创建调试配置:在 VSCode 中,切换到“运行和调试”视图(Ctrl+Shift+D),点击“创建 launch.json 文件”,选择 “Cortex-Debug”。这会在.vscode文件夹下生成launch.json。修改其内容如下:

    { "version": "0.2.0", "configurations": [ { "name": "Cortex Debug (ST-LINK)", "cwd": "${workspaceRoot}", "executable": "${workspaceFolder}/build/zephyr/zephyr.elf", "request": "launch", "type": "cortex-debug", "servertype": "openocd", "serverpath": "C:/path/to/your/openocd/bin/openocd.exe", // 修改为你的 OpenOCD 路径 "interface": "swd", "device": "STM32F103C8", "configFiles": [ "interface/stlink.cfg", "target/stm32f1x.cfg" ], "runToEntryPoint": "main", "svdFile": "${workspaceFolder}/zephyrproject/zephyr/dts/arm/st/stm32f103.svd" // SVD文件用于查看外设寄存器 } ] }
    • 关键是要修改serverpathsvdFile的路径为你的实际路径。
    • device设置为STM32F103C8
    • configFiles指定了 OpenOCD 使用的配置文件,用于识别 ST-LINK 接口和 STM32F1 系列目标。
  4. 开始调试:连接好 ST-LINK 和板子,给板子上电。在 VSCode 中按F5或点击绿色的调试按钮,Cortex-Debug 将启动 OpenOCD,连接目标板,加载程序,并停在main函数入口。你可以设置断点、单步执行、查看变量和寄存器。

5. 适配自定义板型与引脚

如果你的板子 LED 连接的不是nucleo_f103rb默认的引脚,或者你使用的是纯粹的 STM32F103C8T6 最小系统板,就需要修改 Devicetree 覆盖文件来匹配硬件。

5.1 查找 LED 引脚

查看你的板子原理图,找到 LED 连接的 GPIO 引脚。例如,假设 LED 阳极通过电阻连接到PC13,阴极接地(这是很多“蓝色药丸”板的接法)。

5.2 创建板级支持文件

app目录下,创建一个板级定义覆盖目录结构,并添加 Devicetree 覆盖文件。

# 在 app 目录下 mkdir -p boards/arm/my_f103_board

boards/arm/my_f103_board目录下创建两个文件:

1.my_f103_board.dts:定义板级硬件。

/dts-v1/; #include <st/f1/stm32f103Xb.dtsi> #include <st/f1/stm32f103c(8-b)tx-pinctrl.dtsi> #include <zephyr/dt-bindings/gpio/gpio.h> / { model = "My Custom STM32F103C8T6 Board"; compatible = "mycompany,my-f103-board"; chosen { zephyr,console = &usart1; zephyr,shell-uart = &usart1; zephyr,sram = &sram0; zephyr,flash = &flash0; }; leds { compatible = "gpio-leds"; led0: led_0 { gpios = <&gpioc 13 GPIO_ACTIVE_HIGH>; label = "User LED"; }; }; aliases { led0 = &led0; }; }; &usart1 { current-speed = <115200>; pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; pinctrl-names = "default"; status = "okay"; }; &usart2 { current-speed = <115200>; pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>; pinctrl-names = "default"; status = "okay"; };

这个文件定义了板子模型、兼容性字符串,并将led0映射到了GPIOC的第 13 引脚,并配置了 USART1 用于控制台输出。

2.my_f103_board.yaml:板级定义元数据。

identifier: my_f103_board name: My Custom F103 Board type: mcu arch: arm toolchain: - zephyr - gnuarmemb - xtools ram: 20 flash: 64 supported: - arduino_gpio - arduino_i2c - arduino_spi - uart - gpio - i2c - spi

5.3 修改应用源码以使用自定义板

现在,修改app/src/main.c,确保它使用我们定义的 LED 别名。

#include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 /* 使用设备树中的 led0 别名获取 LED 设备 */ static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); void main(void) { int ret; // 检查 LED 设备是否就绪 if (!gpio_is_ready_dt(&led)) { return; } // 将 LED 引脚配置为输出,并初始化为低电平(熄灭) ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return; } while (1) { // 翻转 LED 状态 ret = gpio_pin_toggle_dt(&led); if (ret < 0) { return; } k_msleep(SLEEP_TIME_MS); } }

5.4 使用自定义板型构建

在构建时,指定我们自定义的板型。

# 在 build 目录下(先清空或新建一个build目录) rm -rf * cmake -GNinja -DBOARD=my_f103_board ..\app ninja

这样,编译出的固件就会使用PC13来控制 LED。

6. 烧录与运行

编译成功后,我们需要将zephyr.binzephyr.hex文件烧录到开发板。

6.1 使用 West 命令烧录

West 提供了便捷的烧录命令,它会自动调用合适的工具(如 OpenOCD)。

# 在 build 目录下 west flash

west flash会尝试自动检测调试探头和板型。对于 ST-LINK 和nucleo_f103rb(或我们自定义的板型,如果配置了正确的烧录器),它通常能直接工作。如果失败,可能需要检查 OpenOCD 安装和连接。

6.2 使用 STM32CubeProgrammer 或 pyOCD

  • STM32CubeProgrammer:ST 官方工具,图形化界面,支持 ST-LINK 和 UART 等多种方式连接。
  • pyOCD:一个基于 Python 的 ARM Cortex-M 调试器。可以通过pip install pyocd安装,然后使用pyocd flash build/zephyr/zephyr.bin --target stm32f103c8命令烧录。

烧录完成后,复位开发板,你应该能看到 LED 开始闪烁。

7. 常见问题与排查思路

在搭建和开发过程中,你可能会遇到以下问题:

问题现象可能原因排查步骤与解决方案
west initwest update失败,网络错误网络连接问题,或 Git 仓库地址访问慢1. 检查网络。2. 尝试使用代理。3. 可以手动修改zephyrproject/.west/config中的url-base,使用国内镜像(如 Gitee 镜像,需注意镜像可能滞后)。
cmake配置时,找不到编译器或工具链1. 工具链未安装。2. 工具链路径未添加到PATH。3. 环境变量未生效。1. 运行arm-none-eabi-gcc --version确认工具链可用。2. 在命令行中检查PATH是否包含工具链的bin目录。3. 重启命令行终端或 VSCode。4. 在 CMake 配置时通过-DCMAKE_C_COMPILER-DCMAKE_CXX_COMPILER直接指定编译器绝对路径。
ninja编译失败,提示No such file or directoryundefined reference1. 源码路径错误。2. 依赖的 Zephyr 模块未正确拉取。3. Kconfig 配置冲突。1. 确认cmake命令指向的源目录正确。2. 在zephyrproject目录下重新运行west update。3. 清除build目录,重新配置和构建。4. 检查build/zephyr/.config文件,确保必要的配置(如CONFIG_GPIO=y)已启用。
west flash失败,无法连接目标1. 调试器(ST-LINK)驱动未安装或连接不稳。2. OpenOCD 配置错误。3. 板子未上电或 Boot 引脚设置错误。1. 安装 ST-LINK 驱动(STSW-LINK009)。2. 检查 USB 连接,尝试重新插拔。3. 使用openocd -f interface/stlink.cfg -f target/stm32f1x.cfg命令单独测试 OpenOCD 连接。4. 确认板子 Boot0 引脚已接地(从主Flash启动)。
程序已烧录,但 LED 不闪烁1. LED 引脚定义与实际硬件不符。2. 程序未正常运行(时钟、初始化问题)。3. LED 极性(高电平有效/低电平有效)配置错误。1. 核对原理图,确认 LED 连接引脚,并检查 Devicetree 中的gpios属性。2. 尝试使用调试器单步执行,看程序是否卡在某个初始化函数。3. 将GPIO_ACTIVE_HIGH改为GPIO_ACTIVE_LOW或反之。4. 使用逻辑分析仪或万用表测量引脚电平。
VSCode 智能感知报错,找不到头文件VSCode 的 C/C++ 插件未正确配置includePathdefines1. 使用 CMake Tools 插件配置项目后,通常会自动生成c_cpp_properties.json。2. 可以手动在.vscode/c_cpp_properties.json中配置,将build目录下的compile_commands.json路径添加到compileCommands字段。这是最准确的方式。
编译出的固件大小远超芯片 Flash 容量未启用优化,或包含了不必要的组件。1. 使用-DCONFIG_SIZE_OPTIMIZATIONS=y-DCONFIG_NO_OPTIMIZATIONS=n进行编译优化。2. 通过menuconfig(west build -t menuconfig) 图形化界面关闭不需要的模块(如文件系统、网络栈)。

8. 最佳实践与工程建议

掌握了基础流程后,遵循以下最佳实践能让你的 Zephyr 开发更加高效和稳健。

  1. 版本控制:将你的应用代码(app目录)纳入 Git 管理。但通常不建议将庞大的zephyrproject仓库全部提交。可以使用.gitignore忽略build目录和zephyrproject,通过west manifest文件来锁定 Zephyr 版本。
  2. 管理多个应用:使用 West 的多仓库管理能力。你可以在zephyrproject目录外创建一个west.yml清单文件,将你的应用仓库也作为 West 的一个模块管理,方便依赖和版本同步。
  3. 充分利用 Kconfig 和 Devicetree
    • Kconfig:用于软件功能配置。使用west build -t menuconfig可以打开一个图形化界面来配置内核、驱动、协议栈等选项。配置结果保存在build/zephyr/.config。对于项目特定的配置,可以在应用目录下创建prj.conf文件。
    • Devicetree:用于描述硬件。将板级硬件描述(如 LED、按键、传感器接口)放在板级目录(boards/)下的.dts文件中,实现硬件与软件的解耦。
  4. 模块化应用设计:将不同的功能模块放在单独的源文件中,并利用 Zephyr 的设备驱动模型。通过DEVICE_DT_DEFINE来定义和初始化设备,使用gpio_dt_spec等结构体来传递设备树信息,使代码更清晰、可移植。
  5. 日志与调试:积极使用 Zephyr 的日志系统(#include <zephyr/logging/log.h>)。通过设置不同的日志级别(LOG_LEVEL_DBG,LOG_LEVEL_INF等),可以在开发时输出详细信息,而在生产时减少输出。配合串口控制台,是排查问题的重要手段。
  6. 电源管理:对于电池供电的 IoT 设备,务必关注电源管理。Zephyr 提供了电源管理框架,允许在空闲时进入低功耗模式(如 STOP、SLEEP)。在prj.conf中启用CONFIG_PM=y并合理配置。
  7. 测试:Zephyr 支持单元测试(Twister)和硬件测试。对于关键驱动和模块,编写单元测试是保证长期稳定性的好习惯。可以使用west build -t run来在模拟器(如 QEMU)上运行测试。
  8. 生产固件处理:在发布固件前,考虑启用链接时优化(LTO)、删除调试符号、并可能需要对固件进行签名或加密。使用west sign命令和 Zephyr 的 MCUboot 引导加载程序可以支持安全启动和固件升级。

通过本文的步骤,你应该已经成功在 STM32F103C8T6 上运行了 Zephyr,并在 VSCode 中建立了一个舒适的开发环境。这套组合为你打开了现代嵌入式开发的大门,无论是学习 RTOS 概念,还是开发实际的 IoT 产品原型,都是一个强大的起点。接下来,你可以探索 Zephyr 提供的更多示例,如传感器驱动、蓝牙通信、文件系统等,逐步构建更复杂的应用。如果在实践中遇到新的问题,Zephyr 的官方文档和活跃的社区(如 Discord、GitHub Discussions)是寻求帮助的好地方。