不少嵌入式工程师第一次用 VS Code 写 STM32 代码时,感受有点像是从老式功能机换到触屏手机:界面变了,操作逻辑变了,但很快你会发现,过去在 Keil 里让人憋屈的搜索、跳转、代码格式化,如今都顺滑了很多。
但我也想先把判断说清楚:VS Code 不会替代 Keil 或 STM32CubeIDE,它解决的是“看代码和改代码”这一层的痛苦;真正的编译、链接、烧录、调试,仍然要依赖背后的工具链。这篇文章要做的,是把 VS Code 当作 STM32 开发环境的前端来打造,后端可以是 Keil、ARM GCC,也可以是 STM32CubeCLT。
本文是《基于 VS Code 的 STM32 单片机开发环境搭建》系列第二篇。上一篇我们确定了整体技术路线和工具链方案,这一篇专门解决三件事:VS Code 软件怎么装、插件怎么选怎么配、以及新手最容易放弃的编码乱码问题。读完本文,你可以独立搭建出一个能编辑、编译、烧录 STM32 工程的 VS Code 环境,并知道遇到常见问题该去哪个文件里改。
1. STM32 开发为什么值得用 VS Code
先说一个真实场景。很多开发者在 Keil 里打开一个几千文件的 STM32 项目时,会遇到三个很实际的问题:文件查找靠翻目录树,代码跳转经常跳不到定义,Git 操作只能切到外部工具。Keil 不是不能用,但它更像“上世纪 90 年代的编辑器 + 现代调试器的缝合体”,在编辑体验上确实落后了。
VS Code 的优势并不是“界面更好看”,而是把现代语言工程能力引入了嵌入式开发:
第一,IntelliSense。配合 C/C++ 扩展,VS Code 能识别头文件、补全结构体成员、显示函数参数提示、跳转到定义。这个能力在维护 HAL 库或标准外设库项目时非常有用。
第二,任务系统。你可以把编译、烧录定义成 Task,按一个快捷键完成整套流程,不需要在 IDE 里反复点按钮。
第三,版本控制集成。左侧面板直接看 diff、暂存文件、提交代码。团队协作时,这比反复导工程、手动合并高效得多。
第四,跨平台。同一套配置在 Windows、Linux、macOS 上都能跑。很多公司的编译服务器是 Linux,本地是 Windows,VS Code 可以把两端拉近。
但我不建议无脑迁移。如果你只是写一个几十行的点灯程序,或者刚接触单片机、希望“打开工程就能编译、不折腾环境”,那 Keil 的“一键编译”体验仍然是不可替代的。VS Code 的适用场景是:中大型工程、需要频繁阅读源码、多人协作、希望用 Makefile 或 CMake 管理构建、以及未来想走上更现代工具链的人。
一句话总结:VS Code 的价值不在编辑器本身,而在它把嵌入式工程从“单一 IDE 锁定”中解放了出来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. VS Code 的核心概念与插件机制
要用好 VS Code,先要理解它的运行逻辑。VS Code 本身是一个“编辑器内核”,没有插件时,它几乎不能做 C/C++ 开发。它的能力来自两个协议:语言服务器协议(LSP)和调试适配器协议(DAP)。
LSP 的意思是,语言分析这个重活可以放在一个独立的“语言服务器”里做。VS Code 只负责显示和通信。C/C++ 扩展本质上就是微软提供的一个语言服务器前端,它把代码解析、语义分析、跳转定义的结果交给编辑器展示。
DAP 则负责调试。VS Code 不需要知道你用的是 ST-Link 还是 J-Link,只要有一层适配器能把断点、变量、单步信息翻译成统一协议,界面就能显示调试状态。Cortex-Debug 插件就是这层适配器。
插件机制决定了 VS Code 的成长性。安装插件有三种方式:
- 在扩展面板搜索安装,这是最常用的方式;
- 在命令行使用
code --install-extension安装,适合批量部署; - 从扩展市场网页下载
.vsix文件,在 VS Code 里选择“从 VSIX 安装”,适合内网环境。
理解了这一点,你就不会问出“VS Code 怎么编译 STM32”这种问题了。VS Code 自己不编译任何代码,它只是调用你配置的编译命令。插件起的作用是翻译、衔接、展示。真正干活的,是 arm-none-eabi-gcc、make、STM32_Programmer_CLI、OpenOCD 这些工具。
所以,读这篇文章时请记住一条主线:VS Code 是前端,插件是翻译层,工具链才是后端。后面所有配置都是围绕这条主线展开的。
3. VS Code 软件安装与基础配置
3.1 Windows 安装
从 VS Code 官网下载 Windows 安装包,选择 Stable 版本即可。安装时有几个选项值得留意:
- 勾选“添加到 PATH”,这样可以在终端里直接使用
code命令; - 勾选“在文件资源管理器右键菜单中打开”,以后可以在工程文件夹上右键直接“通过 Code 打开”。
安装完成后,打开 VS Code,在终端输入:
bash复制code --version
如果能输出版本信息,说明 code 命令已经生效。如果提示找不到命令,通常是因为安装时没有勾选“添加到 PATH”。解决方法是在系统环境变量中手动添加 VS Code 的安装目录,默认路径一般是:
text复制C:\Users\你的用户名\AppData\Local\Programs\Microsoft VS Code
3.2 Linux 和 macOS
Linux 上可以从官网下载 .deb 或 .rpm 安装包,也可以用官方仓库安装。如果是 tar.gz 压缩包,解压后把 code 软链到 /usr/local/bin 即可。
macOS 上一般下载 zip 包,把 Visual Studio Code.app 拖入 Applications。第一次打开时系统会提示需要确认。macOS 上启用 code 命令的方式是:打开 VS Code,按下 Shift + Command + P,输入 Shell Command: Install 'code' command in PATH,然后执行。
三种系统在功能上没有本质区别,后续配置保持一致。
3.3 安装后必做的设置
安装完 VS Code,先不要急着写代码,建议先做三件事:
第一,安装中文语言包。在扩展面板搜索 Chinese,安装 Microsoft 官方语言包,然后重启。这对刚接触 VS Code 的开发者非常友好。
第二,调整用户设置。打开设置界面(Ctrl + ,),推荐设置字体大小 16、打开自动保存、关闭不必要的代码缩略图,这些根据个人习惯来。
第三,理解用户设置与工作区设置的区别。用户设置存在 VS Code 的全局配置文件中,影响所有工程;工作区设置在工程的 .vscode/settings.json 中,只影响当前工程。嵌入式的关键配置要放在工作区里,这样整个团队可以共享。
4. STM32 开发核心插件安装
插件不是越多越好。一个刚入职的初学者常见错误是把扩展市场里的热门插件全部装上,结果界面越来越花哨,真正出问题时不知道是谁造成的。
对 STM32 开发来说,下面这几个插件足够启动绝大多数项目。
| 插件名称 | 扩展 ID | 作用 | 建议 |
|---|---|---|---|
| C/C++ | ms-vscode.cpptools | IntelliSense、代码补全、调试支持 | 必装 |
| Cortex-Debug | marus25.cortex-debug | 连接 ST-Link/J-Link,提供嵌入式调试界面 | 调试时必装 |
| CMake Tools | ms-vscode.cmake-tools | 支持 CMake 工程构建和管理 | 如果工程用 CMake 则装 |
| Serial Monitor | ms-vscode.vscode-serial-monitor | 在 VS Code 内查看串口输出 | 强烈建议 |
| Chinese Language Pack | ms-ceintl.vscode-language-pack-zh-hans | 中文界面 | 按需 |
| Todo Tree | Gruntfuggly.todo-tree | 高亮代码中的 TODO/FIXME | 推荐 |
| GitLens | eamodio.gitlens | 查看代码提交历史和责任人 | 团队协作时推荐 |
命令行安装方式如下:
bash复制code --install-extension ms-vscode.cpptools
code --install-extension marus25.cortex-debug
code --install-extension ms-vscode.vscode-serial-monitor
如果你的开发环境无法直接访问扩展市场,可以到 VS Code 扩展市场的网页版搜索对应插件,点击下载 .vsix 文件,然后在 VS Code 扩展面板右上角的“...”菜单中,选择“从 VSIX 安装”。
这里要特别提醒:C/C++ 扩展提供的是“编辑器侧”的代码理解能力,它并不负责编译。右侧的编译按钮由任务系统完成,两者经常被混在一起,务必分清楚。
另外,ST 官方也在陆续完善 VS Code 支持。现阶段更稳妥的做法是:用 STM32CubeMX 生成 CMake 或 Makefile 工程,再交给 VS Code 调用命令行工具链编译。这样既保住了官方芯片支持,又获得了现代编辑体验。
5. 最让人头疼的编码问题:GBK 与 UTF-8
搜索“STM32 VS Code 乱码”,你会发现大量开发者被同一个问题折磨过。原因很简单:Keil 工程里的源文件默认使用 GB2312/GBK 编码,而 VS Code 默认使用 UTF-8 编码。
当 VS Code 用 UTF-8 去读一个 GBK 编码的源文件时,如果文件里只有 ASCII 字符,不会出问题;一旦有中文注释或中文字符串,就会出现一堆乱码。这种乱码不像网络请求、编译器报错那样有提示,它只会静悄悄地让代码变得不可读,甚至会让中文字符串常量在编译后变成错误内容。
具体表现有两种:
- 打开文件后中文注释显示为“锟斤拷”一类字符;
- 编译不报错,但烧录到单片机后,程序里本该显示的中文全部变成乱码,这种更隐蔽,如果用在 LCD 或 OLED 显示内容里会很麻烦。
解决办法分几种情况。
第一种:老工程,代码本身是 GBK,且团队还在用 Keil。这种情况不要强制转 UTF-8,直接在工程工作区设置固定编码即可。在项目根目录创建 .vscode/settings.json,写入:
json复制{
"files.encoding": "gbk",
"files.autoGuessEncoding": false
}
这样 VS Code 打开该工程时,会自动按 GBK 解码显示,中文注释正常,修改保存后不会破坏原编码。files.autoGuessEncoding 一定要设为 false,否则 VS Code 会“自作聪明”地推测编码,同一个文件在同事的电脑上可能显示效果不一致。
第二种:单次临时查看。可以点击 VS Code 右下角的编码按钮(比如显示“UTF-8”),选择“通过编码重新打开”,再选“GBK”或“GB2312”。这个方法适合偶尔打开一个外部文件,不改变工作区配置。
第三种:新工程,希望统一用 UTF-8。建议从源头开始就保持 UTF-8。如果已经有文件因为误读变成了“乱码”,不要手动改,先把乱码文件恢复成一种编码,再统一转换。转换命令在 Git Bash 或 Linux 终端下可以用:
bash复制# 建议先备份
cp main.c main.c.bak
# 单个文件转换
iconv -f GBK -t UTF-8 main.c > main_utf8.c && mv main_utf8.c main.c
Windows 用户也可以在 VS Code 中安装支持批量编码转换的插件,但转换完成后务必做一次编译验证,因为转码过程可能会影响字符串字面量的字节内容。
这里的关键判断是:编码问题不是你用错了编辑器,而是工程历史遗留的编码惯性。提前在 settings.json 里固定编码,比每次都手动“通过编码重新打开”可靠得多。
6. .vscode 关键配置文件与示例
STM32 的 VS Code 工程是否好用,很大程度上取决于 .vscode 目录下三个文件的质量。这三个文件也是 VS Code 从“编辑器”变成“嵌入式 IDE”的关键。
6.1 settings.json
这个文件控制编辑器行为和部分扩展行为。除了编码,还可以配置 C/C++ 扩展的默认头文件路径和宏定义。下面是一个实用示例:
json复制{
"files.encoding": "gbk",
"files.autoGuessEncoding": false,
"editor.fontSize": 16,
"C_Cpp.default.includePath": [
"${workspaceFolder}/Core/Inc",
"${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc",
"${workspaceFolder}/Drivers/CMSIS/Include"
],
"C_Cpp.default.defines": [
"STM32F103xB",
"USE_HAL_DRIVER"
]
}
注意上面的 includePath 要根据你的工程实际目录调整。defines 里的宏也和芯片型号相关,比如 STM32F103C8 通常对应 STM32F103xB,STM32F407 对应 STM32F407xx。写错了 IntelliSense 会不正常,但编译用的是构建系统里的定义,不一定受这里影响,所以问题往往表现得比较隐蔽。
6.2 c_cpp_properties.json
这个文件是 C/C++ 扩展的“配置中心”,重要程度排在第一位。 VS Code 的 C/C++ 扩展通过它理解编译器类型、头文件路径、宏定义和标准。
json复制{
"version": 4,
"configurations": [
{
"name": "STM32-Makefile",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"STM32F103xB",
"USE_HAL_DRIVER"
],
"compilerPath": "C:/arm-gnu-toolchain/bin/arm-none-eabi-gcc.exe",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "gcc-arm"
}
]
}
compilerPath 指向 ARM GCC 编译器路径。如果你现场没有安装独立工具链,而是通过 STM32CubeIDE 的命令行编译,也可以填 STM32CubeIDE 内嵌的 gcc 路径,具体以本机实际安装位置为准。
这里容易踩坑的是 intelliSenseMode。如果填成 windows-msvc-x64,依然会有语法高亮,但很多 ARM 特有的宏和变量会被判定为错误。gcc-arm 是 ARM GCC 工程更合适的选择。
6.3 tasks.json
任务系统是 VS Code 连接编译和烧录的桥梁。我们以 STM32CubeMX 生成的 Makefile 工程为例,创建两个任务:一个是编译,一个是烧录。
json复制{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"args": ["-j8"],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"]
},
{
"label": "flash",
"type": "shell",
"command": "STM32_Programmer_CLI",
"args": [
"-c", "port=SWD",
"-w", "build/<你的固件名>.elf",
"-v"
],
"dependsOn": "build"
}
]
}
build 任务直接调用 make,适用于根目录有 Makefile 的工程。烧录任务调用 STM32_Programmer_CLI,这是 ST 官方命令行烧录工具,需要提前搭配驱动和 ST-Link 使用。
如果你的团队仍然以 Keil 为编译后端,也可以把 command 配成 Keil 命令行工具,让 VS Code 只负责编辑,不贡献编译逻辑。这种“编辑器 + 编译后端分离”的做法是很多公司从 Keil 过渡到 VS Code 的务实选择。
6.4 launch.json
调试配置。以 Cortex-Debug 连接 OpenOCD 为例:
json复制{
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug STM32F103",
"cwd": "${workspaceFolder}",
"executable": "${workspaceFolder}/build/<你的固件名>.elf",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"device": "stm32f103c8",
"interface": "swd",
"configFiles": [
"interface/stlink.cfg",
"target/stm32f1x.cfg"
]
}
]
}
device 字段要根据你的芯片改成对应型号。configFiles 是 OpenOCD 的配置片段,常见的 stm32f1x.cfg 对应 STM32F1 系列,如果用 F4 就写 stm32f4x.cfg。调试不是这一篇的重点,你只需要知道这种配置结构以后会用到。
7. 编译、烧录与效果验证
7.1 编译工程
在工作区打开工程后,按 Ctrl + Shift + B,VS Code 会列出可用的构建任务。选中 build,底部会弹出任务终端,输出类似这样的结果:
text复制make -j8
...
arm-none-eabi-size build/firmware.elf
text data bss dec hex filename
xxx x x x x build/firmware.elf
判断成功的标准很简单:任务结束没有 error,且生成了 .elf 文件。VS Code 里的红色波浪线只代表 IntelliSense 的理解结果,不代表编译结果,这个区别需要特别强调。
7.2 烧录
烧录前,请确认 ST-Link 和单片机的连接正确:SWDIO、SWCLK、GND,以及必要的 3V3 供电。然后执行烧录任务:
bash复制STM32_Programmer_CLI -c port=SWD -w build/firmware.elf -v
如果配置正确,终端会输出下载校验成功的提示,例如 “Download verified successfully”。之后按下单片机上的复位键,程序开始运行。
7.3 验证和失败排查顺序
验证程序是否运行,最简单的办法是看一个已知动作:点灯、蜂鸣器或串口打印。如果有串口输出,强烈建议在 VS Code 里安装 Serial Monitor 扩展,直接在编辑器里看串口日志。
如果失败,不要盲目反复试,按下面的顺序排查:
- 先看编译终端里的完整输出,定位到第一个 error;
- 如果是
No such file or directory,说明头文件或源文件路径有问题,优先检查 Makefile 和c_cpp_properties.json; - 如果是烧录时报
No target connected,优先检查 ST-Link 驱动、接线顺序和单片机供电; - 如果串口输出了乱码但程序在跑,检查波特率和串口工具的字符编码,而不是先怀疑程序逻辑。
这套排查顺序能覆盖绝大部分新手问题。
8. 常见问题与排查方法
下面按实际发生频率整理了一张排查表,建议收藏备用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 中文注释乱码 | 源文件是 GBK,VS Code 按 UTF-8 打开 | 观察右下角编码按钮 | 在工作区 settings.json 设置 "files.encoding": "gbk" |
| 中文显示乱码但编码已经是 GBK | 文件被工具错误转码,内容已损坏 | 用文本工具检查源文件编码 | 从原备份或版本库恢复文件,再按正确编码打开 |
| printf 串口正常但带中文的字符串全乱 | 源码编码和 PC 串口显示编码不一致 | 确认源码编码和串口助手编码 | 统一源码 UTF-8 后重新编译,或者设置串口助手为 GBK |
#include "stm32f1xx_hal.h" 报找不到 |
includePath 没有配置库目录 |
查看 c_cpp_properties.json |
添加 Core/Inc、Drivers/.../*.Inc 等路径 |
| IntelliSense 返回一堆无关错误 | defines 不全或缺少芯片宏 |
对比 Makefile 里的宏定义 | 把 STM32F103xB、USE_HAL_DRIVER 等宏填入配置 |
| 编译提示 make 不是内部或外部命令 | make 未安装或未加入 PATH | 终端执行 make -v |
安装 GNU Make,或将 make.exe 目录加入 PATH |
| 烧录提示 no target connected | SWD 接线错误、驱动未装、目标板没供电 | 检查 ST-Link 四个引脚、驱动状态 | 重新接线,安装驱动,按住复位键再烧录 |
| 编译成功但烧录后无现象 | 烧录文件不对或地址不对 | 确认烧录文件路径、芯片型号 | 检查 .elf 路径和芯片型号是否匹配 |
| 插件市场加载缓慢 | 网络连接不稳定 | 打开扩展面板观察加载状态 | 使用官方渠道重新搜索,或通过 .vsix 离线安装 |
| Cortex-Debug 连接失败 | OpenOCD 配置片段对应不上芯片型号 | 查看 OpenOCD 日志 | 确认 configFiles 里是 stm32f1x.cfg 还是 stm32f4x.cfg |
9. 最佳实践、总结与下一步学习方向
9.1 工程实践建议
经过前面几步,你已经拥有了一个能用的 VS Code STM32 开发环境。但要让这套环境在团队和长期项目中真正稳定,还需要注意几个工程习惯。
第一,把 .vscode 目录纳入版本控制。一个好的 .vscode 配置是团队共识的体现,而不是个人喜好。如果每个人都在本地各自设置编码和头文件路径,早晚会出现“我这边能编译,同事那边全是红波浪线”的尴尬局面。
第二,编码统一是团队底线。新工程尽量从第一天就用 UTF-8,老工程保持 GBK 也完全可以,但必须在 README 和 .vscode 配置里写明。最怕的是同一个工程里有的文件 GBK、有的文件 UTF-8,这会埋下很多隐性 bug。
第三,构建要脚本化。尽量不要让某个工程师记住“先点编译再点下载再点复位”这种流程。把构建和烧录写成任务,最好一个 Ctrl + Shift + B 就完成编译,一个命令完成烧录。这样新人接手时,成本会很低。
第四,路径问题提前避坑。工程目录里最好不要有中文和空格。VS Code 对中文路径的兼容性整体可以,但 Makefile、编译器、烧录工具和第三方脚本不一定都能处理。用一个简洁的英文路径,能省掉很多莫名其妙的错误。
第五,保持工具链可追溯。arm-none-eabi-gcc 的版本、OpenOCD 的版本、STM32CubeProgrammer 的版本,建议在团队内部统一,并在文档里记录。嵌入式编译器的版本差异有时候会带来非常隐蔽的代码行为变化。
9.2 总结与现实提醒
这篇内容从 VS Code 软件安装、插件选型,到编码配置、.vscode 文件设计,再到编译烧录验证和常见问题排查,已经覆盖了一个可用的 STM32 开发环境主链路。
需要承认的是,VS Code 方案并不适合所有人。如果你希望打开工程就编译、调试步骤完全交给 IDE,Keil 或 STM32CubeIDE 依然是效率最高的选择。但如果你开始接触真实项目、需要大量阅读源码、做代码评审和版本管理,VS Code 的现代工作流会明显更顺手。
下一步你可以继续深入了解:基于 Cortex-Debug 的断点调试、用 CMake 重构工程结构、FreeRTOS 感知调试,以及把编译流程接入持续集成。那部分内容会是这个系列的后续方向。
最后给你一个实用建议:更换开发环境时,不用急着删掉旧工具。先在 VS Code 里打开一个已有 Makefile 的 STM32 工程,把编译和烧录跑通一次,感受一下流程差异。这套环境不会让你的代码变少,但会让你更愿意打开工程写代码。
