这次我们来看一个针对嵌入式开发者的实用教程如何在 VSCode 环境下为一块经典的 STM32F103C8T6 最小系统板运行 Zephyr RTOS 项目并成功获取到关键的device字段。如果你正在从传统的 Keil、IAR 开发环境转向更现代的 Zephyr VSCode 组合或者在使用 Zephyr 时遇到了No Cortex-M SW device found这类设备识别问题这篇文章就是为你准备的。Zephyr 是一个功能强大、模块化的实时操作系统但它的设备树Device Tree和驱动模型对于新手来说有一定门槛。尤其是在 VSCode 这种编辑器环境中如何正确配置、编译并让程序在真实的硬件上找到对应的设备device是项目跑通的第一步。本文不会空谈概念而是直接聚焦于一个可复现的实操流程从环境搭建、项目创建、代码编写到编译、烧录、调试最后在串口终端里看到device字段被成功获取并打印出来。整个过程基于最常见的 STM32F103C8T6 “蓝色药丸”开发板硬件成本低但涉及的配置思路适用于所有 Zephyr 支持的 Cortex-M 设备。对于开发者而言最关心的几个问题无非是环境配置复杂吗需要多少磁盘空间编译一次要多久烧录工具用什么代码怎么写才能拿到device指针调试信息怎么看本文将逐一拆解。你将了解到使用 VSCode 和 Zephyr 进行 STM32 开发的核心工作流掌握设备树绑定、Kconfig 配置、以及通过device_get_bindingAPI 获取设备的关键方法。无论你是想评估 Zephyr 是否适合你的下一个产品还是已经决定使用却卡在了第一步这篇教程都能提供一条清晰的路径。1. 核心能力速览在深入细节之前我们先通过一个表格快速了解本教程所构建的开发环境的核心特性和要求让你判断是否值得继续投入时间。能力项说明与要求目标硬件STM32F103C8T6 最小系统板即“蓝色药丸”开发板核心为 Cortex-M364KB Flash20KB RAM。操作系统Zephyr RTOS版本建议 3.6.x。这是一个可伸缩的实时操作系统支持多种架构和外设。开发环境Visual Studio Code (VSCode) 作为主编辑器配合 Zephyr 官方推荐的插件和工具链。工具链Zephyr SDK 或 GNU Arm Embedded Toolchain。用于编译生成针对 ARM Cortex-M 的二进制文件。编译系统CMake West (Zephyr 的元工具)。West 用于管理 Zephyr 本身、项目及依赖。核心演示功能获取并打印指定设备如 UART、GPIO、I2C的device结构体指针验证驱动加载成功。硬件连接需要 USB 转 TTL 串口模块用于打印日志以及 ST-Link V2 或 DAPLink 等调试器用于烧录和调试。环境准备复杂度中等。需要安装 Python、Git、CMake、工具链和 West并正确配置环境变量步骤明确但需耐心。适合场景学习 Zephyr RTOS 开发、评估 Zephyr 在 STM32F1 上的可行性、为现有硬件移植 Zephyr、构建现代化嵌入式开发工作流。不适合场景追求极简几KB RAM/Flash的裸机开发、必须使用 Keil/IAR 专有工具链的封闭项目、对 C 有重度依赖Zephyr 主要使用 C。2. 适用场景与使用边界本教程构建的环境和代码示例主要服务于以下几类开发者和应用场景1. 学习者与教育者如果你是一名学生、嵌入式爱好者或讲师希望学习一个现代、开源、有活跃社区的 RTOSZephyr 是一个绝佳的选择。STM32F103C8T6 硬件普及且成本低廉是理想的入门平台。通过本教程你可以理解 Zephyr 的设备模型、驱动框架和项目构建方式。2. 传统开发者的现代化转型如果你习惯了 Keil MDK 或 IAR Embedded Workbench 的“一站式”环境但对它们的闭源、高昂费用或相对陈旧的编辑体验感到不满希望转向基于 VSCode 的免费、开源、高度可定制化工作流本教程提供了一个从零开始的迁移指南。3. 产品原型快速验证对于需要快速验证某个外设如 LoRa、BLE、传感器在 Zephyr 框架下驱动稳定性的项目本教程提供的环境可以作为一个干净的起点。你可以在stm32f103c8t6这个通用硬件上快速搭建测试工程。4. 为现有硬件移植 Zephyr如果你手头有基于 STM32F103 或其他 Cortex-M 芯片的产品并考虑将其软件升级到 Zephyr 以获得更好的模块化、网络协议栈如 LwM2M、CoAP或文件系统支持本教程的配置过程具有参考价值。使用边界与注意事项资源限制STM32F103C8T6 的 RAM (20KB) 和 Flash (64KB) 资源有限。虽然 Zephyr 可裁剪但启用复杂功能如完整的网络协议栈、文件系统后可能资源紧张。本教程的示例项目非常精简主要演示设备获取。驱动完备性Zephyr 对 STM32F1 系列的支持stm32f1SoC 系列已相当成熟但并非所有外设驱动都像标准库或 HAL 库那样面面俱到。在选用前建议查阅 Zephyr 源码中的drivers目录确认所需驱动是否存在。调试体验虽然 VSCode 可以配置 Cortex-Debug 进行单步调试但其体验可能仍不及 Keil/IAR 的专用调试器流畅尤其是在复杂断点和变量查看方面。日志打印通过 UART是本教程和初期开发的主要调试手段。知识产权Zephyr 采用 Apache 2.0 许可证商业使用友好。但项目中使用的其他库如有需单独确认其许可证。STM32 的硬件 IP 属于 STMicroelectronics。3. 环境准备与前置条件在开始写代码之前我们需要一个稳定、可编译的环境。以下是详细的准备工作清单请确保你的开发主机Windows, Linux, 或 macOS满足以下条件。3.1 操作系统与基础软件操作系统Windows 10/11, Ubuntu 22.04 LTS (或更高版本), 或 macOS Monterey (或更高版本)。本教程以 Windows 为例但命令在 Linux/macOS 上大同小异。Python 3.8Zephyr 的工具west基于 Python。请从 python.org 下载并安装务必在安装时勾选 “Add Python to PATH”。Git用于克隆 Zephyr 源码和项目。从 git-scm.com 下载安装。CMake 3.20.0构建系统生成器。从 cmake.org 下载安装并确保cmake命令可在终端中运行。3.2 获取并安装 Zephyr SDK (推荐)Zephyr SDK 是一个包含了编译工具链、调试工具和必要主机工具的集成包是新手最稳妥的选择。访问 Zephyr SDK 发布页面 下载对应你操作系统的最新版本安装包如zephyr-sdk-0.16.5_windows-x86_64.7z。将其解压到一个没有空格和中文的路径例如C:\zephyr-sdk-0.16.5。以管理员身份打开 PowerShell 或 CMD进入解压目录运行安装脚本# 在解压后的 sdk 目录下执行 .\setup.cmd该脚本会将工具链路径添加到系统环境变量并安装 USB 驱动Windows 需要。验证安装打开新的终端运行arm-zephyr-eabi-gcc --version应能看到 GCC 版本信息。3.3 获取 Zephyr 源码并初始化工作空间我们不直接下载源码而是使用west工具来管理它能处理依赖关系。创建一个工作空间目录例如C:\zephyrproject。在此目录下使用west init命令初始化一个 west 工作空间并指定从官方仓库获取源码cd C:\zephyrproject west init -m https://github.com/zephyrproject-rtos/zephyr --mr main-m指定仓库地址--mr main指定使用主分支可替换为稳定版本标签如v3.6.0。使用west update拉取 Zephyr 源码及其所有模块这需要一些时间因为要克隆多个仓库west update导出 Zephyr 环境变量使得后续的west命令知道 Zephyr 的根目录在哪。在 PowerShell 中$env:ZEPHYR_BASE “C:\zephyrproject\zephyr” cd $env:ZEPHYR_BASE pip install -r scripts/requirements.txt对于 Linux/macOS使用export ZEPHYR_BASE/path/to/zephyrproject/zephyr和source zephyr-env.sh。3.4 安装 VSCode 及必要插件下载并安装 Visual Studio Code 。安装以下核心插件C/C(Microsoft)提供代码智能感知、跳转、错误检查。CMake Tools(Microsoft)提供 CMake 项目的配置、构建、调试集成。Zephyr IDE(Zephyr Project)官方插件提供 Kconfig、设备树语法高亮、代码片段等。Cortex-Debug(Marus25)用于 ARM Cortex-M 的硬件调试。Serial Monitor方便在 VSCode 内查看串口日志可选但推荐。至此软件环境准备完毕。接下来需要准备硬件。3.5 硬件准备与连接STM32F103C8T6 最小系统板一块。ST-Link V2(或兼容的 DAPLink) 调试编程器一个。USB 转 TTL 串口模块一个如 CH340、CP2102 等。按照下图进行连接ST-LinkSWCLK- 板子SWCLK(PA14)SWDIO- 板子SWDIO(PA13)3.3V- 板子3.3VGND- 板子GNDUSB-TTLRX- 板子TX(PA9, USART1)TX- 板子RX(PA10, USART1)GND- 板子GND给板子供电可通过 ST-Link 的 3.3V 或单独的 Micro USB 口。连接好后将 ST-Link 和 USB-TTL 分别插入电脑的 USB 口。在设备管理器中确认两者驱动已正确安装ST-Link 显示为STMicroelectronics STLinkUSB-TTL 显示为对应芯片的 COM 端口。4. 创建与配置 Zephyr 项目环境就绪后我们开始创建一个专属于 STM32F103C8T6 的 Zephyr 应用程序项目。4.1 使用 west 创建项目Zephyr 推荐将应用程序放在工作空间内。我们在zephyrproject目录下创建一个新目录作为我们的项目。cd C:\zephyrproject west create -t app -b stm32f103c8t6_minimal my_stm32_app-t app指定模板类型为应用程序。-b stm32f103c8t6_minimal指定开发板为stm32f103c8t6_minimal。这是 Zephyr 内置的一个针对该板子的基础配置。my_stm32_app你的项目文件夹名称。命令执行后west会在当前目录下创建my_stm32_app文件夹里面包含了一个最简单的src/main.c和CMakeLists.txt、prj.conf等文件。4.2 理解项目关键文件进入项目目录看看生成的文件cd my_stm32_app tree /f # Windows 查看目录树关键文件说明src/main.c应用程序的主源文件我们将在这里编写获取device的代码。CMakeLists.txt告诉 CMake 如何构建这个项目通常不需要修改。prj.conf项目的 Kconfig 配置文件。这里可以启用或禁用 Zephyr 的内核功能、驱动、协议栈等。这是我们配置串口打印功能的关键文件。board.cmake(可能没有)如果创建时指定了-bwest 会自动处理板级配置。west.yml描述项目的 west 清单用于管理依赖。4.3 配置项目以启用串口控制台默认的prj.conf可能是空的。为了让我们的程序能通过串口打印信息需要启用UART驱动和CONSOLE功能。 用 VSCode 打开my_stm32_app文件夹然后编辑prj.conf文件添加以下内容# prj.conf - 启用串口和控制台 CONFIG_PRINTKy CONFIG_STDOUT_CONSOLEy CONFIG_UART_CONSOLEy CONFIG_SERIALy CONFIG_UART_INTERRUPT_DRIVENn # 先使用轮询模式简化 # 根据板子定义USART1 通常被用作控制台 CONFIG_UART_1y保存文件。这些配置项的意思是CONFIG_PRINTK启用内核的打印函数。CONFIG_STDOUT_CONSOLE和CONFIG_UART_CONSOLE将标准输出重定向到 UART 控制台。CONFIG_SERIAL和CONFIG_UART_1启用串口驱动并具体启用 USART1 实例。5. 编写代码获取并打印 device 字段现在来到核心部分修改src/main.c文件编写代码来获取一个设备这里以控制台使用的 UART 设备为例的device指针并将其信息打印出来。5.1 分析设备树与设备名称在 Zephyr 中硬件资源通过设备树Device Tree描述。板级定义文件boards/arm/stm32f103c8t6_minimal/stm32f103c8t6_minimal.dts中已经定义了该板子的外设节点。对于stm32f103c8t6_minimal其控制台通常绑定到usart1。因此我们可以通过设备树生成的标签来获取设备。 一个更通用的方法是使用DT_LABEL宏或直接使用设备树节点标识符。但最简单直接的方式是使用 Zephyr 为常见设备定义的Kconfig 符号对应的设备名称。对于控制台 UART其设备名称通常是DT_LABEL(DT_CHOSEN(zephyr_console))或直接是字符串“USART_1”。为了演示的明确性我们使用后者。5.2 修改 main.c 文件用以下代码完全替换src/main.c中的内容/* * Copyright (c) 2024 Your Name * SPDX-License-Identifier: Apache-2.0 */ #include zephyr/kernel.h #include zephyr/device.h #include zephyr/drivers/uart.h #include zephyr/sys/printk.h void main(void) { printk(“Zephyr Device Get Demo on STM32F103C8T6\n”); /* 关键步骤1定义要查找的设备名称 */ const char *device_name “USART_1”; // 对应设备树中 usart1 的 label /* 关键步骤2使用 device_get_binding 获取设备指针 */ const struct device *uart_dev device_get_binding(device_name); /* 关键步骤3检查设备是否获取成功 */ if (uart_dev NULL) { printk(“Error: Failed to get device binding for %s\n”, device_name); return; } printk(“Success: Device ‘%s’ found!\n”, device_name); /* 关键步骤4打印设备的基本信息 */ printk(“Device name: %s\n”, uart_dev-name); printk(“Device is ready: %s\n”, device_is_ready(uart_dev) ? “YES” : “NO”); /* 可以进一步使用该设备例如发送数据 */ /* uart_poll_out(uart_dev, ‘A’); */ while (1) { /* 主循环可以添加其他任务 */ k_sleep(K_SECONDS(5)); printk(“Device pointer: %p, System uptime: %d ms\n”, (void *)uart_dev, k_uptime_get_32()); } }代码解析头文件引入了内核、设备、UART 驱动和打印所需的头文件。设备名称“USART_1”是 Zephyr 为 STM32 USART1 外设定义的典型设备名称对应设备树中的label。这是获取设备的关键标识。device_get_binding这是 Zephyr 驱动模型的核心 API。它通过设备名称字符串返回一个指向struct device的常量指针。如果驱动初始化成功且设备树配置正确这个指针就是有效的。错误检查必须检查返回的指针是否为NULL。如果是说明驱动未初始化或设备名称错误。设备信息成功获取后我们打印了设备的名称uart_dev-name和就绪状态device_is_ready。device_is_ready是一个更全面的检查确保设备已初始化且可用。主循环程序进入一个简单循环每隔 5 秒打印一次设备指针的值和系统运行时间以证明设备指针持续有效程序在运行。6. 编译、烧录与运行验证代码写好了接下来就是将其变成能在板子上运行的二进制文件。6.1 使用 west 进行编译在 VSCode 终端中确保当前目录是你的项目根目录 (my_stm32_app)然后执行编译命令west build -b stm32f103c8t6_minimal-b stm32f103c8t6_minimal指定目标板。west会根据此板名找到对应的设备树和 Kconfig 配置。首次编译会花费较长时间可能几分钟因为需要配置 CMake 并编译 Zephyr 内核及其依赖。编译成功后输出文件位于build/zephyr/目录下其中最重要的两个文件是zephyr.elf包含调试信息的可执行文件。zephyr.bin纯二进制镜像用于烧录。6.2 使用 west flash 进行烧录确保 ST-Link 已连接好板子和电脑。然后执行烧录命令west flashwest flash命令会自动调用合适的烧录工具如 openocd 或 pyocd来将zephyr.bin文件烧录到开发板的 Flash 中。你会在终端看到烧录过程的进度日志最终显示 “Flashing done” 或类似信息。6.3 通过串口监视器查看输出烧录完成后板子会自动复位并运行程序。现在需要查看串口打印的信息。打开设备管理器记下你的 USB-TTL 模块对应的 COM 端口号例如COM5。你可以使用任意串口工具如 Putty、Tera Term、SecureCRT设置波特率为115200数据位 8停止位 1无校验无流控。打开串口然后按一下板子的复位键RESET。你应该能在串口终端中看到类似以下的输出*** Booting Zephyr OS build v3.6.0-rc1 *** Zephyr Device Get Demo on STM32F103C8T6 Success: Device ‘USART_1’ found! Device name: USART_1 Device is ready: YES Device pointer: 0x20000000, System uptime: 0 ms Device pointer: 0x20000000, System uptime: 5000 ms ...恭喜这表示你已成功编译了 Zephyr 应用程序。将其烧录到 STM32F103C8T6。程序正确运行并通过device_get_bindingAPI 获取到了USART_1这个 UART 设备的device指针。串口控制台功能工作正常。7. 在 VSCode 中配置调试环境可选但推荐虽然串口打印足以验证功能但单步调试对于复杂问题排查至关重要。我们配置 VSCode 的 Cortex-Debug 插件进行调试。7.1 生成调试配置在项目根目录下创建一个.vscode文件夹并在其中创建launch.json文件{ “version”: “0.2.0”, “configurations”: [ { “name”: “Cortex Debug (ST-Link)”, “cwd”: “${workspaceFolder}”, “executable”: “${workspaceFolder}/build/zephyr/zephyr.elf”, “request”: “launch”, “type”: “cortex-debug”, “servertype”: “openocd”, “device”: “STM32F103C8”, “configFiles”: [ “interface/stlink.cfg”, “target/stm32f1x.cfg” ], “svdFile”: “${env:ZEPHYR_BASE}/../modules/hal/stm32/svd/stm32f103.svd”, “runToEntryPoint”: “main”, “postLaunchCommands”: [ “monitor reset halt”, “load”, “monitor reset init” ] } ] }配置说明executable指向编译生成的.elf文件。servertype和configFiles指定使用 OpenOCD 和 ST-Link 接口目标芯片为 STM32F1x。svdFile指定 SVD 文件路径它描述了芯片的所有外设寄存器使得在 VSCode 中可以查看外设寄存器值。postLaunchCommands在启动调试时执行复位、加载程序、再初始化的命令序列。7.2 开始调试确保 ST-Link 连接正常。在 VSCode 中切换到“运行和调试”视图CtrlShiftD。在顶部的下拉菜单中选择 “Cortex Debug (ST-Link)”。点击绿色的“开始调试”按钮或按 F5。程序会被加载到板子并暂停在main函数入口。现在你可以设置断点例如在device_get_binding那一行单步执行F10步入函数F11并查看变量uart_dev的值观察它从NULL变为一个有效的地址如0x20000000。这比单纯看串口打印更直观地验证了device的获取过程。8. 扩展获取其他设备与错误排查成功获取 UART 设备只是开始。Zephyr 驱动模型是统一的获取 GPIO、I2C、SPI 等设备的方法类似。8.1 获取 GPIO 设备示例假设你想控制板载的 LED如果板子有通常连接在 PC13。首先需要在prj.conf中启用 GPIO 驱动# prj.conf 追加 CONFIG_GPIOy然后在main.c中增加获取 GPIO 设备的代码#include zephyr/drivers/gpio.h // 新增头文件 void main(void) { // ... 之前的 UART 设备获取代码 ... const char *gpio_device_name “GPIOC”; // STM32 的 GPIOC 端口 const struct device *gpio_dev device_get_binding(gpio_device_name); if (gpio_dev NULL) { printk(“Error: Failed to get GPIO device %s\n”, gpio_device_name); } else { printk(“Success: GPIO device ‘%s’ found at %p\n”, gpio_dev-name, (void *)gpio_dev); // 这里可以配置引脚为输出并控制 LED 闪烁 } // ... 主循环 ... }注意设备名称“GPIOC”来源于设备树中gpio...节点的label属性。你需要查阅板级 DTS 文件或 Zephyr 的文档来确认准确的标签。8.2 常见问题与排查方法在获取device的过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案编译失败提示undefined reference to ‘device_get_binding’没有包含必要的头文件或驱动未启用检查#include zephyr/device.h是否存在检查对应的驱动如CONFIG_UART_1是否在prj.conf中设置为y。添加正确的头文件在prj.conf中启用对应驱动配置。串口无任何输出1. 串口线接反TX/RX2. 波特率不匹配3. 控制台 UART 配置错误4. 程序未运行1. 交换 TX/RX 线序测试。2. 尝试常见波特率 115200, 9600。3. 检查prj.conf中CONFIG_UART_CONSOLE和CONFIG_UART_1是否启用。4. 用调试器单步或观察板载 LED 是否闪烁如果有。纠正接线确认波特率确保prj.conf配置正确检查烧录是否成功。device_get_binding返回NULL1. 设备名称字符串错误2. 对应驱动未初始化或初始化失败3. 设备树中该节点未定义或 label 不匹配1. 打印传入的设备名称字符串确认。2. 检查编译日志看是否有该驱动的错误。3. 查看build/zephyr/include/generated/devicetree_generated.h或build/zephyr/zephyr.dts文件搜索设备节点确认其label属性。使用设备树生成的正确标签确保驱动依赖在 Kconfig 中正确配置。程序运行但很快复位或卡死1. 堆栈溢出2. 中断配置冲突3. 硬件时钟配置错误1. 增加主线程堆栈大小 (CONFIG_MAIN_STACK_SIZE)。2. 检查是否有多个驱动使用了同一中断线。3. 对于 STM32F1确保系统时钟配置正确通常由板级定义处理。分析编译后生成的.map文件查看内存布局简化程序逐个功能测试查阅板级定义。west flash失败1. ST-Link 连接不稳定或驱动问题2. 目标板供电不足3. OpenOCD 配置错误1. 重新插拔 ST-Link检查设备管理器。2. 尝试单独给板子供电。3. 查看west flash的详细错误信息。确保连接可靠使用west flash -v查看详细日志尝试使用其他烧录工具如 STM32CubeProgrammer手动烧录zephyr.bin进行对比。9. 项目结构与工作流最佳实践掌握基本操作后遵循一些最佳实践能让你的 Zephyr 开发更高效、更少踩坑。9.1 项目目录结构一个规范的 Zephyr 应用项目可能如下所示my_stm32_app/ ├── CMakeLists.txt # 项目构建定义 ├── prj.conf # 主项目配置Kconfig ├── prj_dbg.conf # 调试专用配置可选 ├── boards/ # 板级覆盖文件如果需要自定义 │ └── stm32f103c8t6_minimal.overlay ├── src/ │ ├── main.c # 主应用程序源文件 │ └── driver_alias.h # 集中管理设备名称宏定义推荐 ├── include/ # 项目私有头文件 ├── samples/ # 示例代码可选 └── README.md # 项目说明文档boards/*.overlay设备树覆盖文件。如果你想修改默认的引脚分配例如将控制台从 USART1 换到 USART2就在这里添加节点覆盖而不是直接修改 Zephyr 源码。driver_alias.h创建一个头文件用宏定义所有你用到的设备名称避免在代码中硬编码字符串便于维护。// driver_alias.h #ifndef DRIVER_ALIAS_H #define DRIVER_ALIAS_H #define CONSOLE_UART_DEV_NAME “USART_1” #define LED_GPIO_DEV_NAME “GPIOC” #define I2C_DEV_NAME “I2C_1” #endif9.2 配置管理多配置管理可以创建多个.conf文件如prj_release.conf优化尺寸、prj_dbg.conf启用日志和调试在编译时通过-DOVERLAY_CONFIGprj_dbg.conf指定。使用 menuconfig在项目目录下运行west build -t menuconfig可以启动一个图形化界面来浏览和修改所有 Kconfig 选项这对于探索可用功能非常有用。修改后保存配置会自动更新到build目录下的zephyr/.config文件。9.3 编译与清理增量编译修改代码后只需在build目录存在的情况下再次运行west buildCMake 会进行增量编译速度很快。彻底清理west build -t clean或直接删除build文件夹。编译详情使用west build -v可以输出详细的编译命令用于排查编译错误。9.4 版本控制将你的应用项目my_stm32_app纳入 Git 管理。通常不需要将build目录和zephyrproject目录下的 Zephyr 源码提交到仓库。使用.gitignore文件忽略它们。在README.md中清晰说明如何设置 Zephyr 环境west init和west update以及如何编译你的项目。通过本教程你完成了一个完整的闭环从零搭建 VSCode Zephyr 开发环境为一块具体的硬件STM32F103C8T6创建项目编写代码获取并验证了核心的device字段最后还配置了调试环境。这个流程是使用 Zephyr 进行任何嵌入式开发的基础。接下来你可以尝试基于获取到的device指针调用具体的驱动 API如uart_poll_out,gpio_pin_configure去实现实际的外设控制功能从而构建更复杂的应用程序。
VSCode + Zephyr RTOS 实战:STM32F103C8T6 设备驱动获取与调试
这次我们来看一个针对嵌入式开发者的实用教程如何在 VSCode 环境下为一块经典的 STM32F103C8T6 最小系统板运行 Zephyr RTOS 项目并成功获取到关键的device字段。如果你正在从传统的 Keil、IAR 开发环境转向更现代的 Zephyr VSCode 组合或者在使用 Zephyr 时遇到了No Cortex-M SW device found这类设备识别问题这篇文章就是为你准备的。Zephyr 是一个功能强大、模块化的实时操作系统但它的设备树Device Tree和驱动模型对于新手来说有一定门槛。尤其是在 VSCode 这种编辑器环境中如何正确配置、编译并让程序在真实的硬件上找到对应的设备device是项目跑通的第一步。本文不会空谈概念而是直接聚焦于一个可复现的实操流程从环境搭建、项目创建、代码编写到编译、烧录、调试最后在串口终端里看到device字段被成功获取并打印出来。整个过程基于最常见的 STM32F103C8T6 “蓝色药丸”开发板硬件成本低但涉及的配置思路适用于所有 Zephyr 支持的 Cortex-M 设备。对于开发者而言最关心的几个问题无非是环境配置复杂吗需要多少磁盘空间编译一次要多久烧录工具用什么代码怎么写才能拿到device指针调试信息怎么看本文将逐一拆解。你将了解到使用 VSCode 和 Zephyr 进行 STM32 开发的核心工作流掌握设备树绑定、Kconfig 配置、以及通过device_get_bindingAPI 获取设备的关键方法。无论你是想评估 Zephyr 是否适合你的下一个产品还是已经决定使用却卡在了第一步这篇教程都能提供一条清晰的路径。1. 核心能力速览在深入细节之前我们先通过一个表格快速了解本教程所构建的开发环境的核心特性和要求让你判断是否值得继续投入时间。能力项说明与要求目标硬件STM32F103C8T6 最小系统板即“蓝色药丸”开发板核心为 Cortex-M364KB Flash20KB RAM。操作系统Zephyr RTOS版本建议 3.6.x。这是一个可伸缩的实时操作系统支持多种架构和外设。开发环境Visual Studio Code (VSCode) 作为主编辑器配合 Zephyr 官方推荐的插件和工具链。工具链Zephyr SDK 或 GNU Arm Embedded Toolchain。用于编译生成针对 ARM Cortex-M 的二进制文件。编译系统CMake West (Zephyr 的元工具)。West 用于管理 Zephyr 本身、项目及依赖。核心演示功能获取并打印指定设备如 UART、GPIO、I2C的device结构体指针验证驱动加载成功。硬件连接需要 USB 转 TTL 串口模块用于打印日志以及 ST-Link V2 或 DAPLink 等调试器用于烧录和调试。环境准备复杂度中等。需要安装 Python、Git、CMake、工具链和 West并正确配置环境变量步骤明确但需耐心。适合场景学习 Zephyr RTOS 开发、评估 Zephyr 在 STM32F1 上的可行性、为现有硬件移植 Zephyr、构建现代化嵌入式开发工作流。不适合场景追求极简几KB RAM/Flash的裸机开发、必须使用 Keil/IAR 专有工具链的封闭项目、对 C 有重度依赖Zephyr 主要使用 C。2. 适用场景与使用边界本教程构建的环境和代码示例主要服务于以下几类开发者和应用场景1. 学习者与教育者如果你是一名学生、嵌入式爱好者或讲师希望学习一个现代、开源、有活跃社区的 RTOSZephyr 是一个绝佳的选择。STM32F103C8T6 硬件普及且成本低廉是理想的入门平台。通过本教程你可以理解 Zephyr 的设备模型、驱动框架和项目构建方式。2. 传统开发者的现代化转型如果你习惯了 Keil MDK 或 IAR Embedded Workbench 的“一站式”环境但对它们的闭源、高昂费用或相对陈旧的编辑体验感到不满希望转向基于 VSCode 的免费、开源、高度可定制化工作流本教程提供了一个从零开始的迁移指南。3. 产品原型快速验证对于需要快速验证某个外设如 LoRa、BLE、传感器在 Zephyr 框架下驱动稳定性的项目本教程提供的环境可以作为一个干净的起点。你可以在stm32f103c8t6这个通用硬件上快速搭建测试工程。4. 为现有硬件移植 Zephyr如果你手头有基于 STM32F103 或其他 Cortex-M 芯片的产品并考虑将其软件升级到 Zephyr 以获得更好的模块化、网络协议栈如 LwM2M、CoAP或文件系统支持本教程的配置过程具有参考价值。使用边界与注意事项资源限制STM32F103C8T6 的 RAM (20KB) 和 Flash (64KB) 资源有限。虽然 Zephyr 可裁剪但启用复杂功能如完整的网络协议栈、文件系统后可能资源紧张。本教程的示例项目非常精简主要演示设备获取。驱动完备性Zephyr 对 STM32F1 系列的支持stm32f1SoC 系列已相当成熟但并非所有外设驱动都像标准库或 HAL 库那样面面俱到。在选用前建议查阅 Zephyr 源码中的drivers目录确认所需驱动是否存在。调试体验虽然 VSCode 可以配置 Cortex-Debug 进行单步调试但其体验可能仍不及 Keil/IAR 的专用调试器流畅尤其是在复杂断点和变量查看方面。日志打印通过 UART是本教程和初期开发的主要调试手段。知识产权Zephyr 采用 Apache 2.0 许可证商业使用友好。但项目中使用的其他库如有需单独确认其许可证。STM32 的硬件 IP 属于 STMicroelectronics。3. 环境准备与前置条件在开始写代码之前我们需要一个稳定、可编译的环境。以下是详细的准备工作清单请确保你的开发主机Windows, Linux, 或 macOS满足以下条件。3.1 操作系统与基础软件操作系统Windows 10/11, Ubuntu 22.04 LTS (或更高版本), 或 macOS Monterey (或更高版本)。本教程以 Windows 为例但命令在 Linux/macOS 上大同小异。Python 3.8Zephyr 的工具west基于 Python。请从 python.org 下载并安装务必在安装时勾选 “Add Python to PATH”。Git用于克隆 Zephyr 源码和项目。从 git-scm.com 下载安装。CMake 3.20.0构建系统生成器。从 cmake.org 下载安装并确保cmake命令可在终端中运行。3.2 获取并安装 Zephyr SDK (推荐)Zephyr SDK 是一个包含了编译工具链、调试工具和必要主机工具的集成包是新手最稳妥的选择。访问 Zephyr SDK 发布页面 下载对应你操作系统的最新版本安装包如zephyr-sdk-0.16.5_windows-x86_64.7z。将其解压到一个没有空格和中文的路径例如C:\zephyr-sdk-0.16.5。以管理员身份打开 PowerShell 或 CMD进入解压目录运行安装脚本# 在解压后的 sdk 目录下执行 .\setup.cmd该脚本会将工具链路径添加到系统环境变量并安装 USB 驱动Windows 需要。验证安装打开新的终端运行arm-zephyr-eabi-gcc --version应能看到 GCC 版本信息。3.3 获取 Zephyr 源码并初始化工作空间我们不直接下载源码而是使用west工具来管理它能处理依赖关系。创建一个工作空间目录例如C:\zephyrproject。在此目录下使用west init命令初始化一个 west 工作空间并指定从官方仓库获取源码cd C:\zephyrproject west init -m https://github.com/zephyrproject-rtos/zephyr --mr main-m指定仓库地址--mr main指定使用主分支可替换为稳定版本标签如v3.6.0。使用west update拉取 Zephyr 源码及其所有模块这需要一些时间因为要克隆多个仓库west update导出 Zephyr 环境变量使得后续的west命令知道 Zephyr 的根目录在哪。在 PowerShell 中$env:ZEPHYR_BASE “C:\zephyrproject\zephyr” cd $env:ZEPHYR_BASE pip install -r scripts/requirements.txt对于 Linux/macOS使用export ZEPHYR_BASE/path/to/zephyrproject/zephyr和source zephyr-env.sh。3.4 安装 VSCode 及必要插件下载并安装 Visual Studio Code 。安装以下核心插件C/C(Microsoft)提供代码智能感知、跳转、错误检查。CMake Tools(Microsoft)提供 CMake 项目的配置、构建、调试集成。Zephyr IDE(Zephyr Project)官方插件提供 Kconfig、设备树语法高亮、代码片段等。Cortex-Debug(Marus25)用于 ARM Cortex-M 的硬件调试。Serial Monitor方便在 VSCode 内查看串口日志可选但推荐。至此软件环境准备完毕。接下来需要准备硬件。3.5 硬件准备与连接STM32F103C8T6 最小系统板一块。ST-Link V2(或兼容的 DAPLink) 调试编程器一个。USB 转 TTL 串口模块一个如 CH340、CP2102 等。按照下图进行连接ST-LinkSWCLK- 板子SWCLK(PA14)SWDIO- 板子SWDIO(PA13)3.3V- 板子3.3VGND- 板子GNDUSB-TTLRX- 板子TX(PA9, USART1)TX- 板子RX(PA10, USART1)GND- 板子GND给板子供电可通过 ST-Link 的 3.3V 或单独的 Micro USB 口。连接好后将 ST-Link 和 USB-TTL 分别插入电脑的 USB 口。在设备管理器中确认两者驱动已正确安装ST-Link 显示为STMicroelectronics STLinkUSB-TTL 显示为对应芯片的 COM 端口。4. 创建与配置 Zephyr 项目环境就绪后我们开始创建一个专属于 STM32F103C8T6 的 Zephyr 应用程序项目。4.1 使用 west 创建项目Zephyr 推荐将应用程序放在工作空间内。我们在zephyrproject目录下创建一个新目录作为我们的项目。cd C:\zephyrproject west create -t app -b stm32f103c8t6_minimal my_stm32_app-t app指定模板类型为应用程序。-b stm32f103c8t6_minimal指定开发板为stm32f103c8t6_minimal。这是 Zephyr 内置的一个针对该板子的基础配置。my_stm32_app你的项目文件夹名称。命令执行后west会在当前目录下创建my_stm32_app文件夹里面包含了一个最简单的src/main.c和CMakeLists.txt、prj.conf等文件。4.2 理解项目关键文件进入项目目录看看生成的文件cd my_stm32_app tree /f # Windows 查看目录树关键文件说明src/main.c应用程序的主源文件我们将在这里编写获取device的代码。CMakeLists.txt告诉 CMake 如何构建这个项目通常不需要修改。prj.conf项目的 Kconfig 配置文件。这里可以启用或禁用 Zephyr 的内核功能、驱动、协议栈等。这是我们配置串口打印功能的关键文件。board.cmake(可能没有)如果创建时指定了-bwest 会自动处理板级配置。west.yml描述项目的 west 清单用于管理依赖。4.3 配置项目以启用串口控制台默认的prj.conf可能是空的。为了让我们的程序能通过串口打印信息需要启用UART驱动和CONSOLE功能。 用 VSCode 打开my_stm32_app文件夹然后编辑prj.conf文件添加以下内容# prj.conf - 启用串口和控制台 CONFIG_PRINTKy CONFIG_STDOUT_CONSOLEy CONFIG_UART_CONSOLEy CONFIG_SERIALy CONFIG_UART_INTERRUPT_DRIVENn # 先使用轮询模式简化 # 根据板子定义USART1 通常被用作控制台 CONFIG_UART_1y保存文件。这些配置项的意思是CONFIG_PRINTK启用内核的打印函数。CONFIG_STDOUT_CONSOLE和CONFIG_UART_CONSOLE将标准输出重定向到 UART 控制台。CONFIG_SERIAL和CONFIG_UART_1启用串口驱动并具体启用 USART1 实例。5. 编写代码获取并打印 device 字段现在来到核心部分修改src/main.c文件编写代码来获取一个设备这里以控制台使用的 UART 设备为例的device指针并将其信息打印出来。5.1 分析设备树与设备名称在 Zephyr 中硬件资源通过设备树Device Tree描述。板级定义文件boards/arm/stm32f103c8t6_minimal/stm32f103c8t6_minimal.dts中已经定义了该板子的外设节点。对于stm32f103c8t6_minimal其控制台通常绑定到usart1。因此我们可以通过设备树生成的标签来获取设备。 一个更通用的方法是使用DT_LABEL宏或直接使用设备树节点标识符。但最简单直接的方式是使用 Zephyr 为常见设备定义的Kconfig 符号对应的设备名称。对于控制台 UART其设备名称通常是DT_LABEL(DT_CHOSEN(zephyr_console))或直接是字符串“USART_1”。为了演示的明确性我们使用后者。5.2 修改 main.c 文件用以下代码完全替换src/main.c中的内容/* * Copyright (c) 2024 Your Name * SPDX-License-Identifier: Apache-2.0 */ #include zephyr/kernel.h #include zephyr/device.h #include zephyr/drivers/uart.h #include zephyr/sys/printk.h void main(void) { printk(“Zephyr Device Get Demo on STM32F103C8T6\n”); /* 关键步骤1定义要查找的设备名称 */ const char *device_name “USART_1”; // 对应设备树中 usart1 的 label /* 关键步骤2使用 device_get_binding 获取设备指针 */ const struct device *uart_dev device_get_binding(device_name); /* 关键步骤3检查设备是否获取成功 */ if (uart_dev NULL) { printk(“Error: Failed to get device binding for %s\n”, device_name); return; } printk(“Success: Device ‘%s’ found!\n”, device_name); /* 关键步骤4打印设备的基本信息 */ printk(“Device name: %s\n”, uart_dev-name); printk(“Device is ready: %s\n”, device_is_ready(uart_dev) ? “YES” : “NO”); /* 可以进一步使用该设备例如发送数据 */ /* uart_poll_out(uart_dev, ‘A’); */ while (1) { /* 主循环可以添加其他任务 */ k_sleep(K_SECONDS(5)); printk(“Device pointer: %p, System uptime: %d ms\n”, (void *)uart_dev, k_uptime_get_32()); } }代码解析头文件引入了内核、设备、UART 驱动和打印所需的头文件。设备名称“USART_1”是 Zephyr 为 STM32 USART1 外设定义的典型设备名称对应设备树中的label。这是获取设备的关键标识。device_get_binding这是 Zephyr 驱动模型的核心 API。它通过设备名称字符串返回一个指向struct device的常量指针。如果驱动初始化成功且设备树配置正确这个指针就是有效的。错误检查必须检查返回的指针是否为NULL。如果是说明驱动未初始化或设备名称错误。设备信息成功获取后我们打印了设备的名称uart_dev-name和就绪状态device_is_ready。device_is_ready是一个更全面的检查确保设备已初始化且可用。主循环程序进入一个简单循环每隔 5 秒打印一次设备指针的值和系统运行时间以证明设备指针持续有效程序在运行。6. 编译、烧录与运行验证代码写好了接下来就是将其变成能在板子上运行的二进制文件。6.1 使用 west 进行编译在 VSCode 终端中确保当前目录是你的项目根目录 (my_stm32_app)然后执行编译命令west build -b stm32f103c8t6_minimal-b stm32f103c8t6_minimal指定目标板。west会根据此板名找到对应的设备树和 Kconfig 配置。首次编译会花费较长时间可能几分钟因为需要配置 CMake 并编译 Zephyr 内核及其依赖。编译成功后输出文件位于build/zephyr/目录下其中最重要的两个文件是zephyr.elf包含调试信息的可执行文件。zephyr.bin纯二进制镜像用于烧录。6.2 使用 west flash 进行烧录确保 ST-Link 已连接好板子和电脑。然后执行烧录命令west flashwest flash命令会自动调用合适的烧录工具如 openocd 或 pyocd来将zephyr.bin文件烧录到开发板的 Flash 中。你会在终端看到烧录过程的进度日志最终显示 “Flashing done” 或类似信息。6.3 通过串口监视器查看输出烧录完成后板子会自动复位并运行程序。现在需要查看串口打印的信息。打开设备管理器记下你的 USB-TTL 模块对应的 COM 端口号例如COM5。你可以使用任意串口工具如 Putty、Tera Term、SecureCRT设置波特率为115200数据位 8停止位 1无校验无流控。打开串口然后按一下板子的复位键RESET。你应该能在串口终端中看到类似以下的输出*** Booting Zephyr OS build v3.6.0-rc1 *** Zephyr Device Get Demo on STM32F103C8T6 Success: Device ‘USART_1’ found! Device name: USART_1 Device is ready: YES Device pointer: 0x20000000, System uptime: 0 ms Device pointer: 0x20000000, System uptime: 5000 ms ...恭喜这表示你已成功编译了 Zephyr 应用程序。将其烧录到 STM32F103C8T6。程序正确运行并通过device_get_bindingAPI 获取到了USART_1这个 UART 设备的device指针。串口控制台功能工作正常。7. 在 VSCode 中配置调试环境可选但推荐虽然串口打印足以验证功能但单步调试对于复杂问题排查至关重要。我们配置 VSCode 的 Cortex-Debug 插件进行调试。7.1 生成调试配置在项目根目录下创建一个.vscode文件夹并在其中创建launch.json文件{ “version”: “0.2.0”, “configurations”: [ { “name”: “Cortex Debug (ST-Link)”, “cwd”: “${workspaceFolder}”, “executable”: “${workspaceFolder}/build/zephyr/zephyr.elf”, “request”: “launch”, “type”: “cortex-debug”, “servertype”: “openocd”, “device”: “STM32F103C8”, “configFiles”: [ “interface/stlink.cfg”, “target/stm32f1x.cfg” ], “svdFile”: “${env:ZEPHYR_BASE}/../modules/hal/stm32/svd/stm32f103.svd”, “runToEntryPoint”: “main”, “postLaunchCommands”: [ “monitor reset halt”, “load”, “monitor reset init” ] } ] }配置说明executable指向编译生成的.elf文件。servertype和configFiles指定使用 OpenOCD 和 ST-Link 接口目标芯片为 STM32F1x。svdFile指定 SVD 文件路径它描述了芯片的所有外设寄存器使得在 VSCode 中可以查看外设寄存器值。postLaunchCommands在启动调试时执行复位、加载程序、再初始化的命令序列。7.2 开始调试确保 ST-Link 连接正常。在 VSCode 中切换到“运行和调试”视图CtrlShiftD。在顶部的下拉菜单中选择 “Cortex Debug (ST-Link)”。点击绿色的“开始调试”按钮或按 F5。程序会被加载到板子并暂停在main函数入口。现在你可以设置断点例如在device_get_binding那一行单步执行F10步入函数F11并查看变量uart_dev的值观察它从NULL变为一个有效的地址如0x20000000。这比单纯看串口打印更直观地验证了device的获取过程。8. 扩展获取其他设备与错误排查成功获取 UART 设备只是开始。Zephyr 驱动模型是统一的获取 GPIO、I2C、SPI 等设备的方法类似。8.1 获取 GPIO 设备示例假设你想控制板载的 LED如果板子有通常连接在 PC13。首先需要在prj.conf中启用 GPIO 驱动# prj.conf 追加 CONFIG_GPIOy然后在main.c中增加获取 GPIO 设备的代码#include zephyr/drivers/gpio.h // 新增头文件 void main(void) { // ... 之前的 UART 设备获取代码 ... const char *gpio_device_name “GPIOC”; // STM32 的 GPIOC 端口 const struct device *gpio_dev device_get_binding(gpio_device_name); if (gpio_dev NULL) { printk(“Error: Failed to get GPIO device %s\n”, gpio_device_name); } else { printk(“Success: GPIO device ‘%s’ found at %p\n”, gpio_dev-name, (void *)gpio_dev); // 这里可以配置引脚为输出并控制 LED 闪烁 } // ... 主循环 ... }注意设备名称“GPIOC”来源于设备树中gpio...节点的label属性。你需要查阅板级 DTS 文件或 Zephyr 的文档来确认准确的标签。8.2 常见问题与排查方法在获取device的过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案编译失败提示undefined reference to ‘device_get_binding’没有包含必要的头文件或驱动未启用检查#include zephyr/device.h是否存在检查对应的驱动如CONFIG_UART_1是否在prj.conf中设置为y。添加正确的头文件在prj.conf中启用对应驱动配置。串口无任何输出1. 串口线接反TX/RX2. 波特率不匹配3. 控制台 UART 配置错误4. 程序未运行1. 交换 TX/RX 线序测试。2. 尝试常见波特率 115200, 9600。3. 检查prj.conf中CONFIG_UART_CONSOLE和CONFIG_UART_1是否启用。4. 用调试器单步或观察板载 LED 是否闪烁如果有。纠正接线确认波特率确保prj.conf配置正确检查烧录是否成功。device_get_binding返回NULL1. 设备名称字符串错误2. 对应驱动未初始化或初始化失败3. 设备树中该节点未定义或 label 不匹配1. 打印传入的设备名称字符串确认。2. 检查编译日志看是否有该驱动的错误。3. 查看build/zephyr/include/generated/devicetree_generated.h或build/zephyr/zephyr.dts文件搜索设备节点确认其label属性。使用设备树生成的正确标签确保驱动依赖在 Kconfig 中正确配置。程序运行但很快复位或卡死1. 堆栈溢出2. 中断配置冲突3. 硬件时钟配置错误1. 增加主线程堆栈大小 (CONFIG_MAIN_STACK_SIZE)。2. 检查是否有多个驱动使用了同一中断线。3. 对于 STM32F1确保系统时钟配置正确通常由板级定义处理。分析编译后生成的.map文件查看内存布局简化程序逐个功能测试查阅板级定义。west flash失败1. ST-Link 连接不稳定或驱动问题2. 目标板供电不足3. OpenOCD 配置错误1. 重新插拔 ST-Link检查设备管理器。2. 尝试单独给板子供电。3. 查看west flash的详细错误信息。确保连接可靠使用west flash -v查看详细日志尝试使用其他烧录工具如 STM32CubeProgrammer手动烧录zephyr.bin进行对比。9. 项目结构与工作流最佳实践掌握基本操作后遵循一些最佳实践能让你的 Zephyr 开发更高效、更少踩坑。9.1 项目目录结构一个规范的 Zephyr 应用项目可能如下所示my_stm32_app/ ├── CMakeLists.txt # 项目构建定义 ├── prj.conf # 主项目配置Kconfig ├── prj_dbg.conf # 调试专用配置可选 ├── boards/ # 板级覆盖文件如果需要自定义 │ └── stm32f103c8t6_minimal.overlay ├── src/ │ ├── main.c # 主应用程序源文件 │ └── driver_alias.h # 集中管理设备名称宏定义推荐 ├── include/ # 项目私有头文件 ├── samples/ # 示例代码可选 └── README.md # 项目说明文档boards/*.overlay设备树覆盖文件。如果你想修改默认的引脚分配例如将控制台从 USART1 换到 USART2就在这里添加节点覆盖而不是直接修改 Zephyr 源码。driver_alias.h创建一个头文件用宏定义所有你用到的设备名称避免在代码中硬编码字符串便于维护。// driver_alias.h #ifndef DRIVER_ALIAS_H #define DRIVER_ALIAS_H #define CONSOLE_UART_DEV_NAME “USART_1” #define LED_GPIO_DEV_NAME “GPIOC” #define I2C_DEV_NAME “I2C_1” #endif9.2 配置管理多配置管理可以创建多个.conf文件如prj_release.conf优化尺寸、prj_dbg.conf启用日志和调试在编译时通过-DOVERLAY_CONFIGprj_dbg.conf指定。使用 menuconfig在项目目录下运行west build -t menuconfig可以启动一个图形化界面来浏览和修改所有 Kconfig 选项这对于探索可用功能非常有用。修改后保存配置会自动更新到build目录下的zephyr/.config文件。9.3 编译与清理增量编译修改代码后只需在build目录存在的情况下再次运行west buildCMake 会进行增量编译速度很快。彻底清理west build -t clean或直接删除build文件夹。编译详情使用west build -v可以输出详细的编译命令用于排查编译错误。9.4 版本控制将你的应用项目my_stm32_app纳入 Git 管理。通常不需要将build目录和zephyrproject目录下的 Zephyr 源码提交到仓库。使用.gitignore文件忽略它们。在README.md中清晰说明如何设置 Zephyr 环境west init和west update以及如何编译你的项目。通过本教程你完成了一个完整的闭环从零搭建 VSCode Zephyr 开发环境为一块具体的硬件STM32F103C8T6创建项目编写代码获取并验证了核心的device字段最后还配置了调试环境。这个流程是使用 Zephyr 进行任何嵌入式开发的基础。接下来你可以尝试基于获取到的device指针调用具体的驱动 API如uart_poll_out,gpio_pin_configure去实现实际的外设控制功能从而构建更复杂的应用程序。