
写这篇内容之前先说个现象我做 STM32 开发这几年身边同事的桌面上从清一色的 Keil 图标慢慢变成了 VS Code 和黑乎乎的终端窗口。原以为只是编辑器之争深入用下来才发现这其实是整套工具链和开发方式的切换尤其是在 AI 编程助手普及之后VS Code 这套开放生态的优势被放大了数倍。这一期就把我实际搭建 STM32 VS Code 开发环境的完整过程、踩过的坑以及怎么把 AI 编程真正接入嵌入式工作流一次说透。1. 为什么我把 STM32 开发环境从 Keil 迁到 VS Code1.1 传统 IDE 的痛点让我越来越不想打开 Keil先不急着否定 Keil。MDK 作为 STM32 入门的标准工具工程模板多、教程多、遇到问题能搜到大量现成答案这些优势不可否认。早期我所有外设驱动都是在 Keil 里写的调试器用 J-Link 和 ST-Link 都顺手。但随着项目数量增加几个问题越来越明显编辑器的代码补全还停留在十年前水平多文件跳转和全局搜索一旦工程大一点就卡Git 集成基本靠外部工具最要命的是跨平台能力差我偶尔想在 Linux 或远程服务器上编译一下MDK 完全给不了支持。还有一点很关键就是 AI 编程工具的生态。现在主流的 AI 编程插件几乎都是优先支持 VS Code 和 JetBrains 系对齐到 Keil 上的插件少得可怜即便能用也不是原生体验。2025 年做嵌入式开发不顺手用 AI 辅助真的有点吃亏——这不是偷懒而是把重复劳动压缩把时间花在更值得的架构设计和协议实现上。所以迁移环境这件事本质上是向现代开发工作流靠拢。1.2 这套工具链的组成与选型思路我从头到尾用的方案是STM32CubeMX生成初始化代码 VS Code编辑器 GCC ARM 工具链编译 OpenOCD调试烧录 ST-Link调试器。每个组件的位置和作用我理一下组件作用为什么选它STM32CubeMX生成工程骨架、外设初始化代码、时钟树配置官方工具HAL 库代码自动生成减少手写出错VS Code代码编辑、项目管理、插件生态轻量、跨平台、AI 插件丰富远程开发也方便ARM GCCarm-none-eabi-gcc编译、链接免费开源命令行可控适合脚本化和 CIOpenOCD烧录、调试代理开源调试器配合 ST-Link 即可完成任务ST-Link硬件调试器板载即可成本低几乎人手一个这套组合要理解成一个流水线CubeMX 负责“生成”VS Code 负责“编辑”GCC 负责“编译”OpenOCD 负责“烧录和调试”。每一环都可以单独替换比如把调试器换成 J-Link只要把 OpenOCD 的配置文件换掉就行把编译工具换成 CLang 也并非不可能。这种解耦正是 VS Code 方案的魅力没有被某个厂商锁死。实际搭建的时候我推荐直接安装 ST 官方的 STM32CubeCLTCommand Line Tool。它把 ARM GCC、OpenOCD 以及 STM32CubeProgrammer 的命令行工具打包在一起一次安装就解决了编译器和调试器两大依赖省得手动去 Arm Developer 官网和 OpenOCD 官网分别下载。2. 手把手搭建从安装软件到点亮一颗 LED2.1 安装 VS Code 与核心插件VS Code 的安装本身不多说官方安装包装完即可。但插件不要贪多核心就这几个C/CMicrosoft 官方——提供 IntelliSense 代码补全、语法高亮、调试支持是整个环境的基础。Cortex-Debug——配合 OpenOCD 或 PyOCD 做 ARM Cortex-M 调试比自带的调试器好用得多。Makefile Tools可选如果使用 Makefile 工程这就用得上——提供 Makefile 目标浏览和编译任务识别。CMake Tools如果走 CMake 工程路线——替代 Makefile 工具链CubeMX 也可以生成 CMake 工程。插件安装完第一件事我建议先用arm-none-eabi-gcc --version在终端里确认编译器可用。如果提示找不到命令说明环境变量没配好这一步不解决后面全卡住。2.2 安装编译器、OpenOCD 与 make如果你和我一样装了 STM32CubeCLT那么编译器和 OpenOCD 都在里面。Windows 下默认路径类似C:\ST\STM32CubeCLT_1.15.0\GNU-tools-for-STM32\bin\arm-none-eabi-gcc.exe C:\ST\STM32CubeCLT_1.15.0\OpenOCD\bin\openocd.exe需要手动加环境变量。右键“此电脑”→“属性”→“高级系统设置”→“环境变量”在 Path 里加入上述 bin 路径。添加后重开终端测试三个命令arm-none-eabi-gcc --version openocd --version make --version如果make提示找不到说明系统里还没有 GNU Make。CubeMX 生成的 Makefile 工程需要 make 来驱动这一步缺了编译起不来。我一般用 Chocolatey 装一行命令choco install make不想装 Chocolatey 的话去 GnuWin32 或 EzWin32 下载 make.exe 并把所在目录加入 Path 也行。社区版的 GNU ARM Embedded Toolchain 也经常附带 make 的独立包多看两眼就好。有一点要提醒VS Code 对 Makefile 工程的支持主要通过任务Task实现不是打开目录就能自动编译。所以配置 tasks.json 是必不可少的一步。2.3 用 STM32CubeMX 生成基础工程CubeMX 的图形化界面本身不复杂但我见过不少新手卡在“选芯片”这一步。我的建议是先确认你的板子具体是哪个型号、Flash 多大、引脚编号是多少再在 CubeMX 里搜索对应的 MCU。比如经典的蓝丸板用的是 STM32F103C8T6重点信息是 Cortex-M3 内核、64KB Flash、20KB RAM、LQFP48 封装。CubeMX 里的关键设置流程新建工程选择自己的 MCU 型号。配置时钟树RCC。外部晶振如果用的 8MHz HSE就在 RCC 里选择 Crystal/Ceramic Resonator然后在 Clock Configuration 页面把 HCLK 调到你需要的频率比如 72MHz。配置外设。点亮 LED 最基础的就是一个 GPIO 输出引脚把它设为 GPIO_Output初始电平按板子实际情况设。在 Project Manager 页面设置工程名、存放路径。Toolchain / IDE 选择这里注意我推荐选Makefile这样会生成一整套带 Makefile 的 GCC 工程。当然也可以选CMake看你自己路线。点击 GENERATE CODE等待生成完成。生成后的目录里会有Core、Drivers等目录以及一个Makefile。接下来就用 VS Code 打开这个工程文件夹。这里有个很实用的经验CubeMX 每次重新生成代码时会覆盖用户代码区域之外的初始化部分但/* USER CODE BEGIN */和/* USER CODE END */包裹的代码会被保留。所以自己的业务逻辑务必写在 USER CODE 段里面否则重新生成时可能被冲掉。刚开始用 VS Code 方案的朋友尤其容易在这块翻车。2.4 配置编译任务与一键烧录命令打开工程后按下CtrlShiftP输入Tasks: Configure Task选择“使用模板创建 tasks.json 文件”然后从模板中选择Others。我这边直接给出已经验证可用的配置{ version: 2.0.0, tasks: [ { label: build, type: shell, command: make, args: [-j8], options: { cwd: ${workspaceFolder} }, group: { kind: build, isDefault: true }, problemMatcher: [$gcc] }, { label: flash, type: shell, command: openocd, args: [ -f, interface/stlink.cfg, -f, target/stm32f1x.cfg, -c, program build/main.elf verify reset exit ], options: { cwd: ${workspaceFolder} }, dependsOn: [build] } ] }这个配置里有两个任务build负责编译flash负责编译后烧录。-j8表示并行编译 8 个任务编译更快。program build/main.elf verify reset exit这条命令的意思是把 ELF 文件烧录到芯片校验复位然后退出 OpenOCD。实际执行时按CtrlShiftB默认执行 build烧录就到命令面板里运行“任务运行任务”选择 flash或者直接在终端手动输入openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program build/main.elf verify reset exit注意 target 配置文件要和芯片系列对应STM32F1 就用stm32f1x.cfgF4 就用stm32f4x.cfg。如果你用的是 ST 官方开发板OpenOCD 的 board 目录里一般有对应板卡的 cfg 文件直接board/st_nucleo_f103rb.cfg也行比手动指定 interface 和 target 更省事。2.5 配置 Cortex-Debug 实现 F5 一键调试编译和烧录解决后下一步就是调试。VS Code 的调试需要两份配置一个是launch.json定义调试会话一个是c_cpp_properties.json让 IntelliSense 知道头文件和宏定义。先看 launch.json。在 VS Code 左侧“运行和调试”面板点击“创建 launch.json 文件”选择Cortex-Debug: OpenOCD调试类型然后修改为类似下面这样{ version: 0.2.0, configurations: [ { name: STM32 Debug, type: cortex-debug, request: launch, servertype: openocd, device: STM32F103C8, interface: swd, executable: ${workspaceFolder}/build/main.elf, svdFile: ${workspaceFolder}/STM32F103.svd, runToEntryPoint: main, cwd: ${workspaceFolder}, serverargs: [ -f, interface/stlink.cfg, -f, target/stm32f1x.cfg ] } ] }其中executable指定编译生成的 ELFsvdFile是寄存器描述文件有了它调试时可以实时查看外设寄存器状态。SVD 文件在 STM32CubeF1 固件包里或者从芯片厂商官网下载放到工程目录后把路径配好。配置完成后再设置c_cpp_properties.json。这一步是新手问得最多的地方因为很多从 Keil 转过来的朋友一打开 VS Code 就看到满屏红色波浪线像代码全错了一样其实只是 IntelliSense 不知道头文件在哪。我在这个文件里放的是实际生效配置{ configurations: [ { name: STM32, includePath: [ ${workspaceFolder}/Core/Inc, ${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc, ${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy, ${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include, ${workspaceFolder}/Drivers/CMSIS/Include ], defines: [ STM32F103xB, USE_HAL_DRIVER ], compilerPath: C:/ST/STM32CubeCLT_1.15.0/GNU-tools-for-STM32/bin/arm-none-eabi-gcc.exe, cStandard: c11, intelliSenseMode: gcc-arm } ], version: 4 }这份配置值的两个要点第一includePath必须包含所有头文件目录尤其是 HAL 驱动目录和 CMSIS 目录漏一个就会红一片第二defines里的STM32F103xB要和芯片型号匹配不同 F1 型号的宏定义不同F103C8 用STM32F103xBF103ZET6 用STM32F103xE写错会导致条件编译片段对不上同样疯狂报红。配置完成后按 F5Cortex-Debug 会自动启动 OpenOCD 服务并连接 ST-Link程序会停在 main 函数入口接下来就可以像用其他 IDE 一样打断点、看变量、看寄存器了。3. 把 AI 编程能力接进 STM32 开发流程3.1 嵌入式场景下的 AI 编程工具选型标题里说的“嵌入式软件 AI 编程”不是噱头而是我现在工作的常态。2025 年的 AI 编程工具已经发展出两条路线我们做嵌入式的人得分开看路线一是“补全和问答”型代表是 GitHub Copilot、通义灵码、CodeGeeX 这类。它们嵌入 VS Code写代码时自动补全下一段选中代码可以对话解释。它们胜在轻量但面对 STM32 这种需要精确配置寄存器的场景补全质量一般仍然需要自己动手校正。路线二是“Agent 任务”型代表是 Claude Code、OpenAI Codex CLI、Cline 这类。它们能读写工程文件、执行终端命令、查看编译输出按自然语言任务自动修改代码。特别是 Claude Code 这种命令行 Agent我试过直接让它“把串口轮询改成 DMA 接收 空闲中断”它能自己翻代码、改驱动、重构回调函数我只需要做代码审查。还有一类值得提本地大模型 Continue 插件像 Ollama 跑的 Qwen配合 Continue 这个开源插件接入 VS Code。适合对数据敏感、不能把代码传到云端 API 的场景。嵌入式项目有时涉及未公开的板卡和协议本地部署更安心。我的建议是不想折腾的用通义灵码或 Copilot 这类先把补全用起来愿意花时间调教工作流的优先试 Continue/Cline 接 DeepSeek API成本低质量也可控。3.2 在 VS Code 里接入 AI 的两种实用方式方式一通过插件接入云端大模型 API。以 Continue 插件为例安装后在配置里新建一个聊天模型填入 API Key 和 Base URL。DeepSeek 的 OpenAI 兼容接口以https://api.deepseek.com为 Base URL模型名填deepseek-chat即可。之后框选代码让它在侧边栏分析、生成、解释。我实测下来最常用的场景是“把这个 HAL 初始化函数改成不用 DMA 的版本”以及“帮我解释这行位操作的含义”。方式二在 VS Code 终端里跑命令行 Agent。比如 Claude Code 的 CLI在项目根目录启动后用自然语言描述任务它会自己读取文件、修改代码、执行 make 编译。这种方式一开始让我有点不适因为把自己不太能掌控的修改权交给了 AI但用熟了之后习惯变成先要求它“不要动用户代码段以外的初始化区域”“只修改指定函数”再逐条检查 Diff最后编译验证。相对可控效率也高。还要重点提一下“skill”概念。Cline 这类工具支持定义固定提示词模板我在实际项目中维护了一个stm32-skill.md里面写好了几条固定的工作流。比如“生成 GPIO 驱动”“生成 DMA空闲中断串口框架”“生成 Modbus CRC 计算”调用的时候直接让 AI 按模板执行输出稳定很多。这比随手打一句“帮我写个驱动”强十倍因为模板里写明了芯片型号、HAL 版本、引脚定义、错误处理风格等约束条件。3.3 嵌入式专属提示词的写法让大模型不再“胡编”不少朋友抱怨 AI 生成的 STM32 代码一编译全是错其实大部分问题出在提示词没给足上下文。大模型不像人不会自己去看你的原理图和型号规格书它只能根据你提供的信息推断。我给一个实际的模板你直接抄就能用你是一名精通 STM32 的嵌入式软件工程师。 请为 STM32F103C8T6 编写一段 HAL 库代码实现按键控制 LED 的翻转。 硬件连接 - PA0 接按键按下为低电平悬空为高 - PB0 接 LED高电平点亮 要求 - 使用 STM32Cube HAL不混用标准库 - 按键消抖时间 20ms - 按一次按键翻转一次 LED长按不重复翻转 - 函数命名以 BSP_ 开头提供 .c 和 .h代码要能直接编译 - 不要解释直接输出代码。看到没有核心要素就三个字芯片STM32F103C8T6、库HAL、行为按键控制 LED消抖 20ms翻转模式。很多 AI 出错的场景是因为用户只写了“写一个按键控制 LED”没有指定 HAL、没指定引脚、没指定消抖方式AI 就会用自己训练数据里最常见的方式发挥可最常见的方式可能来自 Arduino也可能来自标准库虽然也能跑但这个项目里就未必合适。我还常用一个专门做代码审查的提示词你是一名嵌入式硬件安全审查专家请审查下面的 STM32 HAL 代码 1. 是否存在与外设初始化冲突的重复配置 2. 中断回调中是否有耗时操作 3. 是否遗漏了外设时钟使能 4. GPIO 配置是否有上下拉、速度、模式冲突 5. 是否有潜在的编译警告。 请逐条列出问题并给出修改建议。这个提示词特别适合在接受 AI 生成代码之后做一轮自动 review很多低级错误比如忘记__HAL_RCC_GPIOB_CLK_ENABLE()都会被揪出来。3.4 实战用 AI 生成按键控制 LED 代码并编译验证下面这一节完全是实录。我新建了一个 CubeMX 工程只初始化了 RCC 和 GPIO其余都交给 AI 去填。我向 Claude 发送了上文写好的模板提示词它返回的代码经过我微调后是void BSP_KeyLed_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); } void BSP_KeyLed_Process(void) { if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { HAL_Delay(20); if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { } } } }然后在 main 里调用int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); BSP_KeyLed_Init(); while (1) { BSP_KeyLed_Process(); } }整个流程从提问到编译通过大概十分钟其中我自己改动的时间不到五分钟。这里最值得说的不是 AI 多厉害而是人机分工CubeMX 管最底层时钟和芯片初始化AI 管逻辑代码我管接线验证和检查。这个分工模式基本确定了我后续做复杂项目时的工作流骨架给 CubeMX模块逻辑给 AI驱动细节和问题排查留给人。4. 常见问题与排查技巧实录4.1 编译没问题VS Code 却满屏红色波浪线这是从 Keil 切换到 VS Code 后几乎每个人都会遇到的第一道坎。症状很典型终端里 make 编译通过固件也烧进去了但 VS Code 编辑器里到处都是红色波浪线点开一看提示“无法打开源文件 stm32f1xx_hal.h”。原因通常是c_cpp_properties.json里的 includePath 没配全或者 defines 少了芯片型号宏。像 STM32F103C8T6 需要STM32F103xB这个宏影响的是 HAL 库里条件编译的分支选择少了它会有一堆头文件定义冲突或缺失红色波浪线自然一片。解决办法就是把上文给出的c_cpp_properties.json对号入座改一遍。编译器的路径也确认要指向 arm-none-eabi-gcc.exe这个路径不对同样会引发各种奇怪的 IntelliSense 错误。还有个小技巧调完后在代码里随便找一个头文件包含按Ctrl点击能跳转进去基本就代表路径配置成功了。如果不能跳转逐项排查 includePath 里的相对路径是否和工程目录匹配。CubeMX 生成的 Makefile 工程目录结构是固定的Core/Inc、Drivers/...照着填就行。4.2 make 命令无法识别或者编译报“未找到命令”Windows 下最容易出现这个问题。打开 VS Code 终端输入make提示“不是内部或外部命令”而arm-none-eabi-gcc --version却有输出说明编译器的 path 加进去了但 make 没加。更诡异的情况是系统终端里 make 能用VS Code 终端里不能用。这是因为 VS Code 的终端环境变量来自它启动时读取的配置如果 VS Code 是在添加环境变量之前启动的终端里还是旧环境。重启 VS Code 就能解决或者在终端里输入$env:Path [System.Environment]::GetEnvironmentVariable(Path,Machine) ; [System.Environment]::GetEnvironmentVariable(Path,User)手动刷新一次。如果实在不想碰 make也可以回到 CubeMX 重新生成 CMake 工程然后改用 CMake Tools 插件。CMake 工程用 CMakeLists.txt 驱动调 CMake Ninja 方式编译效果等价环境变量问题会少一些。4.3 OpenOCD 烧录失败或者连接不上芯片烧录调试时报错的种类不少但最常见的是这几种报错信息原因解决办法“Error: open failed”ST-Link 驱动没有正确安装或者端口被占用安装 ST 官方 ST-Link 驱动关闭其他占用 ST-Link 的程序“target not halted”接线松动、目标芯片供电异常或芯片进入低功耗/读保护状态检查 SWDIO/SWCLK/GND 接线确保 3.3V 供电用 STM32CubeProgrammer 连接并解除读保护“Info : Unable to match requested speed”目标板稳定性差调试速度太高在 OpenOCD 参数里加-c adapter speed 1000降速“Error: flash write failed”Flash 保护或程序在运行中断开了调试器先用 STM32CubeProgrammer 的“Full chip erase”清一次再试我遇到最多的还是“target not halted”尤其是用了国产 ST-Link 拷贝版的时候。这种问题往往不是软件配置错而是接线太长或者供电不稳定。排查时先把 SWD 线缩短到 10cm 以内调试时钟降到 1MHz绝大多数情况能解决。如果 OpenOCD 一直报找不到目标芯片可以换用 STM32CubeProgrammer 的 GUI 模式连接一下试试。它出错的提示比 OpenOCD 更智能能直接告诉你读保护状态、连接方式对不对是很好的辅助排查工具。4.4 AI 生成代码的坑我用几次踩出来的血泪教训AI 编程终究不是万能灵药尤其在嵌入式这种硬件耦合很强的领域。我总结过几个高频坑第一AI 容易混用库版本。你让它生成 STM32F1 的代码它记住了 HAL 库和标准库两套 API有时输出一个GPIOB-BSRR ...然后又接了一段HAL_Delay混合时代码可能编译通过但行为异常。规避办法是在提示词里明确“只用 HAL不要混用标准库”生成后检查有没有裸操作寄存器的代码。第二AI 经常不知道你的 CubeMX 已经初始化了哪些外设。如果 CubeMX 里已经初始化了 LED 引脚AI 又在自己的代码里重复调用HAL_GPIO_Init它会覆盖之前的配置。这属于“上下文缺失”问题所以提示词里最好写明“GPIO 由 CubeMX 初始化不要重复初始化只添加业务逻辑”。第三AI 写的延时阻塞在裸机工程里没问题但一旦到了带实时系统的工程它的 HAL_Delay 策略就会出问题。让它生成串口接收时它默认给一个大循环轮询如果在 RTOS 环境里这就是灾难。所以我在提示词里如果涉及中断或 DMA都会明确“不要在阻塞代码里等待数据”否则它默认走轮询路线。我现在的习惯是AI 生成的代码默认不信任先过一遍编译再过一遍代码逻辑最后上板验证。就算这样它依然帮我节省了大量搭基础框架的时间。把 AI 当实习生用初稿它来审核我管这才是比较健康的关系。最后再分享一个我这几个月一直在用的安排VS Code 里左侧是代码右侧常驻一个 Continue 对话窗口。遇到不熟悉的芯片外设把 HAL 驱动源码里对应的局部代码发给它让它用一句话解释角色和注意事项。遇到大片重复的初始化逻辑就选中代码让它“总结成函数并给出调用点”。这些动作虽然不起眼但积少成多每天省出来的时间够多而且我对代码库的理解也一直在加深。对嵌入式工程师来说这算是普通编辑器和 AI 辅助编辑器最本质的差别。