最近在尝试将 Zephyr RTOS 应用到 STM32F103C8T6 这款经典的“蓝色药丸”开发板上时,发现虽然网上有不少关于 Zephyr 的零散资料,但真正能串联起从环境搭建、项目创建、编译到烧录运行全流程,并且适配 VSCode 这一主流开发环境的完整教程却不多。很多开发者,尤其是从传统 Keil、IAR 环境转过来的朋友,在面对全新的工具链和构建系统时容易感到困惑,卡在环境配置或编译错误环节。

本文旨在提供一个从零开始的、手把手的实战指南。我将详细演示如何在 Windows 环境下,使用 VSCode 作为核心开发工具,为 STM32F103C8T6 最小系统板配置 Zephyr 开发环境,并完成一个基础项目(如 LED 闪烁)的编译、烧录与调试全流程。无论你是嵌入式新手想接触现代 RTOS 开发,还是有一定经验的开发者希望将 Zephyr 应用于 STM32F1 系列,这篇文章都能为你提供一套可复现的闭环解决方案。

1. 背景与核心概念:为什么选择 Zephyr + VSCode + STM32F103?

在深入实操之前,我们有必要厘清几个核心概念,理解这个技术组合的价值所在。

Zephyr RTOS 是一个由 Linux 基金会托管的、开源、可扩展的实时操作系统(RTOS),专为资源受限的嵌入式设备设计。它与 FreeRTOS、RT-Thread 等同属 RTOS 范畴,但其优势在于高度模块化、强大的设备树(Devicetree)抽象、以及活跃的社区和广泛的芯片支持。Zephyr 采用 CMake 构建系统,强调跨平台和可移植性,这使得它非常适合用于学习和产品原型开发。

Visual Studio Code (VSCode) 是微软推出的免费、开源、跨平台的代码编辑器。它通过丰富的扩展生态系统(如 C/C++、CMake、嵌入式调试等)可以变身为一款强大的集成开发环境(IDE)。对于 Zephyr 开发而言,VSCode 的优势在于:

  1. 出色的代码智能感知和导航 :借助 C/C++ 扩展,可以轻松跳转定义、查找引用。
  2. 集成的终端和任务运行 :可以直接在编辑器内运行 West 命令、编译和烧录。
  3. 强大的调试支持 :配合 Cortex-Debug 等扩展,可以实现图形化的源码级调试。
  4. 跨平台一致性 :无论在 Windows、Linux 还是 macOS 上,体验基本一致。

STM32F103C8T6 是意法半导体(ST)基于 ARM Cortex-M3 内核的经典微控制器,因其性价比极高、资源丰富(72MHz主频、64KB Flash、20KB RAM)且拥有庞大的社区和资料库,常被称为“蓝色药丸”开发板,是嵌入式入门和原型验证的绝佳选择。

这三者结合的意义在于 :它代表了一种现代、开源、高效的嵌入式开发工作流。你不再被绑定在某个特定的商业 IDE 上,整个工具链(编译器、调试器、构建系统)都是开源且可定制的。通过这个组合,你可以学习到 CMake 构建、设备树配置、West 项目管理等在现代嵌入式开发中越来越重要的技能。

2. 环境准备与工具链安装

工欲善其事,必先利其器。在 Windows 上搭建 Zephyr 开发环境需要一系列工具。请注意,以下步骤基于当前(撰写时)稳定的工具版本,如果未来有重大更新,请以 Zephyr 官方文档为准。

2.1 安装 Python 和 Git

Zephyr 的工具链管理工具 west 是基于 Python 的,因此首先需要安装 Python。

  1. 安装 Python 3.8 或更高版本 :访问 Python 官网 下载 Windows 安装包。安装时务必勾选 “Add Python to PATH” 选项,这将把 Python 和 Pip 添加到系统环境变量。
  2. 验证安装 :打开命令提示符(CMD)或 PowerShell,输入以下命令:
    python --version
    pip --version
    
    应分别显示 Python 和 Pip 的版本号。
  3. 安装 Git :访问 Git 官网 下载并安装 Git。安装过程中,在“Adjusting your PATH environment”步骤,建议选择 “Git from the command line and also from 3rd-party software” ,以便在任何终端都能使用 git 命令。

2.2 获取 Zephyr 源码并安装 West

West 是 Zephyr 项目的元工具,用于管理多个 Git 仓库(包括 Zephyr 源码本身、各种模块和示例)。

  1. 使用 Pip 安装 West

    pip install west
    

    安装完成后,验证:

    west --version
    
  2. 克隆 Zephyr 主仓库 :选择一个合适的目录(例如 C:\Users\YourName\zephyrproject ),在该目录下打开命令行执行:

    west init zephyrproject
    cd zephyrproject
    west update
    

    west init 会创建一个新的目录并初始化 west 配置。 west update 会拉取 Zephyr 源码及其所有必要的模块(如 hal_stm32, cmsis 等),这个过程耗时较长,取决于网络状况。

  3. 导出 Zephyr CMake 包 :为了让 CMake 能够找到 Zephyr,需要设置 ZEPHYR_BASE 环境变量并运行导出脚本。

    west zephyr-export
    

2.3 安装 Zephyr SDK(工具链)

Zephyr SDK 是一个集成了编译器(GCC)、调试器(GDB)、以及各种二进制工具(如 openocd)的套件,是编译 Zephyr 应用所必需的。

  1. 下载 Zephyr SDK :访问 Zephyr SDK 发布页面 。对于 STM32F103(Cortex-M3),我们需要的是支持 ARM 架构的版本。通常下载文件名类似 zephyr-sdk-0.16.5_windows-x86_64.zip 的最新版本。
  2. 安装 SDK :将下载的 ZIP 文件解压到你希望的目录, 路径中不要包含中文或空格 ,例如 C:\zephyr-sdk-0.16.5 。进入该目录,运行 setup.cmd 脚本。这个脚本会设置必要的环境变量并安装 USB 驱动(用于调试器)。
  3. 验证工具链 :安装完成后,打开新的命令行窗口,输入:
    arm-zephyr-eabi-gcc --version
    
    应该能看到 GCC 的版本信息。

2.4 安装 VSCode 及必要扩展

  1. 安装 VSCode :从 VSCode 官网 下载并安装。
  2. 安装扩展 :打开 VSCode,进入扩展市场(Ctrl+Shift+X),搜索并安装以下关键扩展:
    • C/C++ (ms-vscode.cpptools):提供代码智能感知、调试支持。
    • CMake Tools (ms-vscode.cmake-tools):提供 CMake 项目的配置、构建、调试集成。
    • Cortex-Debug (marus25.cortex-debug):专用于 ARM Cortex-M 调试的扩展,支持 J-Link、ST-Link、OpenOCD 等。
    • (可选) Zephyr IDE (zephyr.zephyr-ide):提供 Zephyr 特定的代码片段和工具集成,非必需但很方便。

2.5 安装硬件调试工具(OpenOCD)

为了通过 ST-Link 调试器给 STM32F103C8T6 烧录程序,我们需要 OpenOCD。

  1. 下载 OpenOCD :可以从 OpenOCD 官方 xPack 项目 下载预编译的 Windows 二进制包。
  2. 安装与配置 :解压到合适目录,例如 C:\openocd 。将 C:\openocd\bin 添加到系统的 PATH 环境变量中。
  3. 验证安装 :打开命令行,输入 openocd --version ,应显示版本信息。

至此,所有软件环境准备完毕。接下来我们创建一个针对 STM32F103C8T6 的 Zephyr 示例项目。

3. 创建并配置第一个 Zephyr 项目

我们将创建一个最简单的 blinky (LED 闪烁)应用。

3.1 使用 West 创建应用

zephyrproject 目录外,创建一个新的应用目录,例如 my_zephyr_app

# 假设当前在 C:\Users\YourName
cd C:\Users\YourName
mkdir my_zephyr_app && cd my_zephyr_app

使用 west 命令初始化一个应用,并指定开发板为 stm32f103c8t6 。Zephyr 已经为许多开发板提供了预定义配置, stm32f103c8t6 是其中之一。

west init -l app/
west build -p always -b stm32f103c8t6 app

注意 west init -l app/ 会创建一个名为 app 的本地应用目录,但通常我们更推荐手动创建项目结构。更清晰的做法是:

  1. my_zephyr_app 目录下,手动创建以下结构:
    my_zephyr_app/
    ├── CMakeLists.txt
    ├── prj.conf
    └── src/
        └── main.c
    

3.2 编写项目文件

1. CMakeLists.txt 这是项目的构建定义文件,告诉 CMake 这是一个 Zephyr 应用。

# my_zephyr_app/CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_blinky)

target_sources(app PRIVATE src/main.c)

第一行指定 CMake 最低版本。 find_package 用于查找 Zephyr 包, HINTS 指向我们之前设置的 ZEPHYR_BASE 环境变量。 project 定义项目名。 target_sources 将我们的源文件 main.c 添加到构建目标 app 中。

2. prj.conf 这是项目的 Kconfig 配置文件,用于启用或禁用 Zephyr 内核和模块的特性。

# my_zephyr_app/prj.conf
CONFIG_GPIO=y
CONFIG_SERIAL=y
CONFIG_PRINTK=y
CONFIG_STDOUT_CONSOLE=y

这里我们启用了 GPIO(控制LED)、串口(用于打印日志)和打印输出到控制台的功能。

3. src/main.c 这是应用的主源文件。

// my_zephyr_app/src/main.c
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   1000

/* 根据你的板子原理图修改LED的GPIO引脚。
 * 对于常见的STM32F103C8T6最小系统板,用户LED通常连接在PC13。
 */
#define LED0_NODE DT_ALIAS(led0)

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

void main(void)
{
	int ret;

	if (!device_is_ready(led.port)) {
		return;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return;
	}

	while (1) {
		/* 设置引脚为低电平点亮LED(假设低电平有效) */
		ret = gpio_pin_set_dt(&led, 1);
		if (ret < 0) {
			return;
		}
		k_msleep(SLEEP_TIME_MS);

		/* 设置引脚为高电平熄灭LED */
		ret = gpio_pin_set_dt(&led, 0);
		if (ret < 0) {
			return;
		}
		k_msleep(SLEEP_TIME_MS);
	}
}

这段代码做了以下几件事:

  • 包含必要的 Zephyr 头文件。
  • 使用设备树(Devicetree)别名 led0 来获取 LED 的 GPIO 规格。这是一种硬件抽象,使得代码不直接依赖具体引脚号,更具可移植性。
  • main 函数中,首先检查 GPIO 设备是否就绪,然后配置引脚为输出模式。
  • 在一个无限循环中,交替设置引脚电平并睡眠,实现 LED 闪烁。

3.3 配置设备树(Devicetree)覆盖

我们的代码引用了 DT_ALIAS(led0) ,但标准的 stm32f103c8t6 板级定义可能没有定义 led0 别名,或者定义的引脚与我们的实际硬件不符。因此,我们需要提供一个设备树覆盖(Overlay)文件来指定 LED 的具体引脚。

在项目根目录 my_zephyr_app 下创建 boards 文件夹,并在其中创建对应板子的 .overlay 文件。

my_zephyr_app/
├── boards/
│   └── stm32f103c8t6.overlay
├── CMakeLists.txt
├── prj.conf
└── src/
    └── main.c

boards/stm32f103c8t6.overlay 内容如下:

// my_zephyr_app/boards/stm32f103c8t6.overlay
/ {
	aliases {
		led0 = &led0;
	};

	leds {
		compatible = "gpio-leds";
		led0: led_0 {
			gpios = <&gpioc 13 GPIO_ACTIVE_LOW>;
			label = "User LED";
		};
	};
};

这个文件定义了一个名为 led0 的别名,指向一个 led_0 节点。该节点指定 LED 连接在 GPIOC 的第 13 引脚(PC13),并且是低电平有效(即输出低电平时 LED 亮)。这符合大多数 STM32F103C8T6 最小系统板的硬件连接。

4. 在 VSCode 中构建、编译与烧录

现在,我们将使用 VSCode 和 CMake Tools 扩展来管理整个构建流程。

4.1 使用 VSCode 打开并配置项目

  1. 用 VSCode 打开 my_zephyr_app 文件夹。
  2. 底部状态栏会显示 CMake Tools 扩展的按钮。如果没有自动检测到工具链,你需要点击状态栏的“No Kit Selected”或“Unconfigured”来选择一个工具链。
  3. 在弹出的列表中,选择 Zephyr SDK (arm-zephyr-eabi) 。这是之前安装的 Zephyr SDK 提供的工具链。
  4. 接下来选择构建目标。点击状态栏的“Build Target”部分(可能显示为“[all]”),选择 stm32f103c8t6 作为目标开发板。

4.2 配置构建目录与构建

  1. CMake Tools 会提示你选择一个构建目录(Build Directory)。通常建议在项目外创建一个 build 目录,例如 ../build 。这保持了源码的清洁。
  2. 选择后,CMake 将开始配置项目。你可以在 VSCode 的“终端”面板看到输出。配置成功后,状态栏会显示开发板名称和工具链。
  3. 开始编译 :你可以通过以下几种方式编译:
    • 快捷键 :按 F7 Ctrl+Shift+B
    • 命令面板 :按 Ctrl+Shift+P ,输入 “CMake: Build” 并选择。
    • 状态栏 :点击底部状态栏的“Build”按钮(锤子图标)。
  4. 编译过程会在“终端”面板显示。如果一切顺利,最后会看到类似 [100%] Built target zephyr_final 的输出,并在构建目录(如 ../build/zephyr )下生成 zephyr.bin zephyr.hex zephyr.elf 等文件。

4.3 连接硬件与烧录程序

将你的 STM32F103C8T6 最小系统板通过 ST-Link(或兼容的调试器)连接到电脑的 USB 口。确保驱动已正确安装(Windows 通常会自动安装 ST-Link 的 USB 驱动)。

Zephyr 使用 west flash 命令进行烧录,它会自动调用合适的烧录工具(如 OpenOCD)。我们可以在 VSCode 的集成终端中执行此命令。

  1. 在 VSCode 中打开终端(`Ctrl+``)。
  2. 确保当前目录是你的项目目录 my_zephyr_app
  3. 执行烧录命令:
    west flash
    
    west flash 会:
    • 自动检测连接的调试器和目标芯片。
    • 调用 OpenOCD 建立连接。
    • 将编译好的 zephyr.bin zephyr.hex 文件烧录到芯片的 Flash 中。
    • 复位芯片并开始运行程序。

如果烧录成功,你应该能看到终端输出一系列 OpenOCD 和烧录进度信息,最后程序开始运行。此时,观察你的 STM32F103C8T6 板子,连接在 PC13 的 LED 应该开始以 1 秒的间隔闪烁。

4.4 在 VSCode 中进行调试(可选但推荐)

调试是开发中不可或缺的一环。使用 Cortex-Debug 扩展,我们可以实现源码级调试。

  1. 创建调试配置 :在 VSCode 侧边栏选择“运行和调试”图标(或按 Ctrl+Shift+D ),点击“创建 launch.json 文件”,选择 Cortex-Debug
  2. 配置 launch.json :VSCode 会在 .vscode 文件夹下创建 launch.json 文件。我们需要根据 STM32F103 和 ST-Link 进行修改。一个基本的配置示例如下:
    // .vscode/launch.json
    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Cortex Debug (ST-Link)",
                "cwd": "${workspaceRoot}",
                "executable": "${workspaceRoot}/../build/zephyr/zephyr.elf", // 指向你的 .elf 文件
                "request": "launch",
                "type": "cortex-debug",
                "servertype": "openocd",
                "serverpath": "C:/openocd/bin/openocd.exe", // 你的 OpenOCD 路径
                "configFiles": [
                    "interface/stlink.cfg",
                    "target/stm32f1x.cfg"
                ],
                "runToEntryPoint": "main",
                "device": "STM32F103C8",
                "svdFile": "${env:ZEPHYR_BASE}/../modules/hal/stm32/svd/stm32f103.svd" // SVD文件用于查看外设寄存器
            }
        ]
    }
    
    关键参数说明
    • executable : 指向编译生成的 .elf 文件路径。
    • serverpath : 指向你的 openocd.exe 的完整路径。
    • configFiles : OpenOCD 的配置文件, stlink.cfg 指定调试器接口, stm32f1x.cfg 指定目标芯片。
    • svdFile : SVD 文件描述了芯片的所有外设寄存器,允许你在调试时查看和修改寄存器值。需要根据你的 Zephyr 源码路径调整。
  3. 开始调试 :确保开发板已连接。在 main.c while(1) 循环内设置一个断点(点击行号左侧)。然后按 F5 或点击调试视图的绿色开始按钮。程序将会在断点处暂停,你可以查看变量、单步执行、查看调用栈等。

5. 常见问题与排查思路

在实践过程中,你可能会遇到各种问题。下面列出一些常见问题及其解决方法。

问题现象 可能原因 排查思路与解决方案
west 命令未找到 1. Python 或 Pip 未正确安装或未添加到 PATH。
2. West 安装失败。
1. 在命令行输入 python --version pip --version 确认安装成功。
2. 尝试重新安装: pip install west --upgrade
3. 检查系统 PATH 环境变量是否包含 Python 和 Pip 的脚本目录(如 C:\Users\YourName\AppData\Local\Programs\Python\Python39\Scripts )。
west update 失败或极慢 1. 网络问题,无法访问 GitHub。
2. Git 配置问题。
1. 检查网络连接,可尝试使用代理或更换网络环境。
2. 使用 west config 设置 Git 镜像,例如 west config remote.zephyr.url-base https://gitee.com/zephyrproject-rtos (如果可用)。
3. 手动设置 Git 的 HTTP/HTTPS 代理。
CMake 配置失败,找不到 Zephyr 1. ZEPHYR_BASE 环境变量未设置或错误。
2. 未运行 west zephyr-export
1. 在命令行中 echo %ZEPHYR_BASE% 检查变量值,应指向 Zephyr 源码根目录。
2. 进入 zephyrproject 目录,重新执行 west zephyr-export
3. 在 VSCode 的终端中,确保环境变量已生效(可能需要重启 VSCode)。
编译错误:找不到编译器 arm-zephyr-eabi-gcc 1. Zephyr SDK 未安装或未正确安装。
2. SDK 的 bin 目录未添加到 PATH。
1. 确认已运行 SDK 目录下的 setup.cmd
2. 打开新的命令行,输入 arm-zephyr-eabi-gcc --version 验证。
3. 在 VSCode 的 CMake Tools 中选择正确的 Kit(Zephyr SDK)。
west flash 失败:找不到 ST-Link 或无法连接 1. ST-Link 驱动未安装。
2. 开发板未上电或连接不良。
3. 其他程序占用了 ST-Link(如 STM32CubeProgrammer)。
4. OpenOCD 配置或路径错误。
1. 检查设备管理器中是否有“STMicroelectronics STLink dongle”且无感叹号。
2. 重新插拔 USB 线,确保板子供电正常。
3. 关闭所有可能占用 ST-Link 的软件。
4. 在命令行手动运行 openocd -f interface/stlink.cfg -f target/stm32f1x.cfg 测试 OpenOCD 连接。如果失败,检查 launch.json 中的 serverpath configFiles 路径。
程序已烧录,但 LED 不闪烁 1. 设备树覆盖文件未生效或引脚定义错误。
2. LED 硬件连接与代码假设不符(如高电平有效 vs 低电平有效)。
3. 系统时钟未正确配置,导致 k_msleep 实际睡眠时间极长。
1. 检查 boards/stm32f103c8t6.overlay 文件是否存在且语法正确。
2. 查阅你的最小系统板原理图,确认 LED 连接的具体引脚和有效电平。修改 .overlay 文件中的 gpios 属性(例如改为 <&gpioc 13 GPIO_ACTIVE_HIGH> )。
3. 尝试在 main 函数开头添加 printk(“Hello Zephyr!\n”); 并通过串口工具查看是否有输出,以验证程序是否运行。
VSCode 代码智能感知报错(红色波浪线) 1. C/C++ 扩展未正确配置包含路径和定义。 1. 在项目根目录创建 .vscode/c_cpp_properties.json 文件。
2. 使用 CMake Tools 扩展提供的配置:按 Ctrl+Shift+P ,输入 “C/C++: Edit Configurations (UI)”,在 Configuration Provider 中选择 ms-vscode.cmake-tools 。这样 C/C++ 扩展会使用 CMake 生成的编译数据库来提供准确的智能感知。

6. 最佳实践与进阶建议

掌握了基础流程后,遵循一些最佳实践能让你的 Zephyr 开发之旅更加顺畅。

6.1 项目结构与版本控制

  • 分离源码与构建产物 :始终在项目目录外(如 ../build )进行构建。这便于清理(直接删除 build 文件夹)和版本控制(将 build 目录加入 .gitignore )。
  • 使用 Git 进行版本控制 :初始化 Git 仓库,并添加合理的 .gitignore 文件(忽略 build/ .vscode/ 中的部分文件等)。定期提交,为每个功能或修复创建清晰的分支。
  • 管理依赖 :如果你的项目需要额外的 Zephyr 模块或第三方库(如 LVGL、LittleFS),使用 west.yml 文件来声明依赖,而不是手动复制代码。

6.2 配置与设备树管理

  • 善用 Kconfig 和 prj.conf :将不同的功能配置(如网络、文件系统、调试级别)放在不同的 .conf 文件中,例如 prj_debug.conf prj_release.conf 。使用 west build -b <board> -- -DOVERLAY_CONFIG=prj_debug.conf 来指定配置。
  • 设备树覆盖是王道 :永远 不要 直接修改 Zephyr 源码中的板级设备树文件( .dts )。始终使用项目本地的 boards/<board>.overlay 文件来定制硬件配置。这保证了你的项目与上游 Zephyr 更新的兼容性。
  • 检查设备树生成结果 :编译后,在 build/zephyr 目录下会生成 zephyr.dts 文件,这是最终合并所有设备树源和覆盖后的结果。检查这个文件可以确认你的覆盖是否按预期生效。

6.3 调试与日志

  • 充分利用串口日志 :在 prj.conf 中启用 CONFIG_LOG=y CONFIG_SERIAL=y ,并在代码中使用 LOG_INF() , LOG_ERR() 等宏代替 printk ,可以获得带时间戳、模块名和日志等级的格式化输出,并通过串口查看,极大方便问题定位。
  • 结构化调试 :在 launch.json 中合理配置 svdFile ,这样在 VSCode 的调试视图中可以实时查看和修改芯片的外设寄存器(如 GPIO、USART、TIM 等),对于底层驱动调试非常有用。
  • 使用 Segger RTT :如果条件允许,使用 J-Link 调试器并启用 CONFIG_USE_SEGGER_RTT=y ,可以通过 RTT 实现比串口更快的日志输出,且不占用硬件串口。

6.4 性能与优化

  • 关注内存使用 :STM32F103C8T6 只有 20KB RAM,需精打细算。使用 west build -t rom_report west build -t ram_report 命令生成内存占用报告。注意栈大小配置( CONFIG_MAIN_STACK_SIZE 等)。
  • 选择合适的内核配置 :对于简单的应用,可以考虑禁用不需要的内核特性(如多线程、动态对象)以减少 footprint。但需权衡功能与资源。
  • 电源管理 :如果项目是电池供电,务必研究 Zephyr 的电源管理(PM)子系统,合理使用低功耗模式(Sleep, Stop, Standby)。

从点亮一个 LED 开始,你已经成功搭建了一个现代化的、基于 Zephyr RTOS 和 VSCode 的 STM32 开发环境。这套工作流的核心优势在于其开源、可定制和高度集成性。接下来,你可以尝试更复杂的项目:例如,添加一个按钮控制 LED,使用 PWM 驱动 RGB LED,通过 I2C 读取传感器数据,或者连接 WiFi/蓝牙模块。

建议下一步深入研究 Zephyr 的官方文档,特别是其丰富的驱动模型、内核服务(线程、信号量、消息队列)和网络协议栈。同时,多利用 VSCode 的调试功能,通过实际单步执行来理解 Zephyr 的启动流程和线程调度机制。遇到问题时,除了查阅文档,Zephyr 项目的 GitHub Discussions 和 Discord 社区也是获取帮助的宝贵资源。记住,嵌入式开发的学习曲线是实践铺就的,每解决一个具体问题,你对这套强大工具链的理解就会更深一层。

更多推荐