1. 为什么要在 VS Code 里打开 Keil 工程1.1 一个老生常谈的痛点搞嵌入式开发的人尤其是做 STM32、GD32、NXP 或者瑞萨 RA 系列的朋友对 Keil MDK 这个 IDE 的感情大概都是又爱又恨。爱的是它确实稳定编译器、调试器、器件支持包一条龙烧录和仿真基本不会出什么幺蛾子恨的是它的编辑器体验放在今天实在有点跟不上节奏——代码补全慢半拍、主题老旧、Git 集成基本等于没有、多文件跳转卡顿写个稍微大点的工程就感觉在跟编辑器较劲。于是很多人就动了一个念头能不能用 VS Code 来写代码用 Keil 来编译和调试答案是可以的而且这条路已经被无数人走通了。核心思路其实就一句话——VS Code 负责编辑体验Keil 负责工具链和调试。两者通过工程文件、编译脚本或者调试配置桥接起来各取所长。这个方案适合谁适合已经装了 Keil MDK或者 Keil C51并且工程能正常编译的人适合对 VS Code 有一定使用基础、会装插件会改配置文件的人也适合那些想把手里的老工程逐步迁移到更现代工作流、但又不想推倒重来的团队。如果你是完全的新手连 Keil 工程都没建过那我建议先把 Keil 那一套跑通再来看这篇内容。1.2 三种主流打开方式先搞清楚你要哪一种“VS Code 打开 Keil”这句话其实很模糊因为不同人的诉求差别很大。我把它拆成三种典型场景你对号入座场景你的真实需求推荐方案复杂度只想用 VS Code 写代码编辑舒服编译调试还是回 Keil直接打开工程文件夹 C/C 插件低想在 VS Code 里一键编译编辑和编译都在 VS Code调试回 Keil配置 tasks.json 调用 Keil 命令行中想全流程都在 VS Code编辑、编译、下载、调试一条龙Cortex-Debug 外部工具链高大部分人其实停在第一、第二种就够用了。第三种虽然爽但配置成本高而且一旦工程里用了 Keil 特有的东西比如某些器件包、RTX 组件、AC5 编译器迁移起来会很痛苦。我个人的建议是先做第一种稳定之后再上第二种第三种看项目情况再说。2. 动手前的准备工作2.1 确认你的 Keil 能正常编译这一步千万别跳过。很多人 VS Code 里折腾半天编译报错最后发现是 Keil 本身工程就有问题。先在 Keil 里把工程完整编译一遍确保 0 error 0 warning至少 0 error能正常生成 axf 或者 hex 文件。记住你的工程路径路径里尽量不要有中文和空格这是嵌入式工具链的老毛病了Keil 命令行对中文路径的支持一直不太行。顺便记一下你用的 Keil 版本和编译器版本。打开 KeilHelp → About 能看到版本号比如 MDK 5.38、MDK 5.41 这种。编译器版本在 Project → Options for Target → Target 标签页里能看到是 AC5 还是 AC6。这个信息后面配置命令行编译的时候要用到因为 AC5 和 AC6 的调用方式不一样。2.2 VS Code 侧的基础配置VS Code 官网下载安装这个不用多说装完之后至少需要这几个插件C/C微软官方那个提供代码补全、跳转、语法高亮是核心。Cortex-Debug如果你打算在 VS Code 里调试这个必须有。Chinese (Simplified) Language Pack看个人习惯不影响功能。装完 C/C 插件之后最关键的一步是配置c_cpp_properties.json让 VS Code 知道去哪里找头文件。这个文件在工程目录下的.vscode文件夹里如果没有就手动建一个。很多人打开 Keil 工程之后满屏红色波浪线就是因为这个文件没配好。2.3 关于 Keil 命令行工具的位置Keil 安装目录下有几个关键的可执行文件后面配置编译任务全靠它们UV4.exeKeil uVision 的主程序支持命令行调用。UV4所在目录通常在C:\Keil_v5\UV4\。编译器在C:\Keil_v5\ARM\ARMCC\bin\AC5或者C:\Keil_v5\ARM\ARMCLANG\bin\AC6。注意不同版本 Keil 的安装路径可能不一样有的装在C:\Keil有的装在D:\MDK以你实际安装位置为准。配置的时候用绝对路径别用相对路径省得后面出问题。3. 方案一纯编辑模式五分钟搞定3.1 直接打开工程文件夹这是最简单的方式。VS Code 里 File → Open Folder选中你 Keil 工程所在的文件夹打开就行。你会看到一堆.c、.h、.uvprojx、.uvoptx文件。.uvprojx就是 Keil 的工程文件VS Code 不认识它但没关系我们只是拿它当个普通文件夹用。打开之后 VS Code 会自动扫描目录但这时候代码补全基本是废的因为找不到头文件。接下来就要配c_cpp_properties.json。3.2 配置头文件路径消除红色波浪线在工程根目录建.vscode文件夹里面新建c_cpp_properties.json内容大概长这样{ configurations: [ { name: Keil, includePath: [ ${workspaceFolder}/**, C:/Keil_v5/ARM/ARMCC/include, C:/Keil_v5/ARM/CMSIS/Include, C:/Users/你的用户名/AppData/Local/Arm/Packs/** ], defines: [ STM32F103xB, USE_HAL_DRIVER ], compilerPath: C:/Keil_v5/ARM/ARMCC/bin/armcc.exe, cStandard: c99, cppStandard: c11, intelliSenseMode: windows-gcc-arm } ], version: 4 }这里有几个点要解释一下。includePath里的${workspaceFolder}/**表示递归包含工程目录下所有文件夹这样你自己的头文件就能被找到。后面几条是 Keil 自带的库路径和器件包路径。器件包路径那个AppData/Local/Arm/Packs是 Keil 装 Pack 的默认位置如果你装到别的地方了改成你自己的。defines里放的是宏定义这个必须跟 Keil 工程里的保持一致。怎么知道 Keil 里定义了哪些宏打开 KeilProject → Options for Target → C/C 标签页看 Define 那一栏把里面的内容抄过来。这一步很关键宏定义不对条件编译的代码就会显示错误波浪线照样满屏。compilerPath指向 Keil 的编译器这样 IntelliSense 能更准确地推断类型。intelliSenseMode选windows-gcc-arm是因为 Keil 的 ARMCC 跟 GCC 比较接近用这个模式补全效果最好。3.3 实测效果与局限配好之后重启一下 VS Code红色波浪线应该会消掉大部分。代码跳转、补全、查找引用这些功能都能用了写代码的体验直接上了一个台阶。我实测下来一个中等规模的 STM32 工程跳转响应基本是秒开比 Keil 里快不少。但这个方案有个明显的局限编译和调试还是得回 Keil。你在 VS Code 里改完代码要切回 Keil 按 F7 编译再按 CtrlF5 下载。来回切换虽然不算麻烦但总归不够丝滑。如果你能接受这个那到这一步就可以收工了。实操心得c_cpp_properties.json里的路径用正斜杠/别用反斜杠\Windows 下虽然有时候也能识别但正斜杠更稳。另外路径里如果有空格整个路径要用引号包起来。4. 方案二在 VS Code 里一键编译4.1 用 tasks.json 调用 Keil 命令行这个方案的核心是让 VS Code 通过任务Task去调用 Keil 的命令行编译。Keil 的UV4.exe支持这样的调用格式UV4.exe -b 工程文件.uvprojx -o 输出日志.txt-b表示 build编译-o指定日志输出文件。还有-r表示 rebuild重新编译全部-c表示 clean清理。知道这几个参数就够了。在.vscode文件夹里新建tasks.json{ version: 2.0.0, tasks: [ { label: Keil Build, type: shell, command: C:/Keil_v5/UV4/UV4.exe, args: [ -b, ${workspaceFolder}/你的工程名.uvprojx, -o, ${workspaceFolder}/build_log.txt ], group: { kind: build, isDefault: true }, problemMatcher: [] }, { label: Keil Rebuild, type: shell, command: C:/Keil_v5/UV4/UV4.exe, args: [ -r, ${workspaceFolder}/你的工程名.uvprojx, -o, ${workspaceFolder}/build_log.txt ], problemMatcher: [] }, { label: Keil Clean, type: shell, command: C:/Keil_v5/UV4/UV4.exe, args: [ -c, ${workspaceFolder}/你的工程名.uvprojx, -o, ${workspaceFolder}/build_log.txt ], problemMatcher: [] } ] }把你的工程名.uvprojx换成你实际的工程文件名。配好之后按CtrlShiftB就能触发编译VS Code 会调起 Keil 的命令行去编译编译结果输出到build_log.txt里。4.2 让编译错误显示在 VS Code 里上面那个配置有个问题编译错误只写进了日志文件VS Code 的“问题”面板里看不到。要解决这个得配problemMatcher让 VS Code 去解析日志。Keil 的编译错误格式大概是这样的..\Src\main.c(45): error: #20: identifier xxx is undefined我们可以写一个正则去匹配它problemMatcher: { owner: cpp, fileLocation: [autoDetect, ${workspaceFolder}], pattern: { regexp: ^(.*)\\((\\d)\\):\\s(error|warning):\\s(.*)$, file: 1, line: 2, severity: 3, message: 4 } }把这个替换掉原来的problemMatcher: []。这样编译完之后错误和警告就会出现在 VS Code 的问题面板里双击能直接跳到出错的行体验就完整了。4.3 一个更省事的做法用批处理包装直接调UV4.exe有个小坑它是 GUI 程序命令行调用的时候有时候会弹窗而且返回值不太规范。我一般会写一个批处理文件包一层echo off C:\Keil_v5\UV4\UV4.exe -b %~dp0你的工程名.uvprojx -o %~dp0build_log.txt type %~dp0build_log.txt exit /b %errorlevel%存成build.bat放在工程根目录然后tasks.json里直接调这个 bat 就行。这样做的好处是编译日志会直接打印到 VS Code 的终端里不用再去开文件看而且errorlevel能正确传递方便判断编译成功还是失败。注意事项Keil 命令行编译的时候如果工程正在 Keil GUI 里打开着可能会报文件被占用的错误。所以用命令行编译之前先把 Keil 关掉。这是个很常见的坑我第一次配的时候就被坑了半天。5. 方案三全流程在 VS Code 里搞定5.1 这个方案适合什么人如果你追求的是“打开 VS Code 就能写代码、编译、下载、调试全程不碰 Keil”那就要上 Cortex-Debug 这套方案了。它的原理是绕开 Keil 的 IDE直接用 ARM 的工具链arm-none-eabi-gcc 或者 Keil 的 armclang编译然后用 OpenOCD 或者 pyOCD 通过调试器ST-Link、J-Link、DAPLink下载和调试。这个方案的优势很明显跨平台、可脚本化、能接 CI/CD、Git 管理干净。但代价也不小需要重新配编译脚本Makefile 或者 CMake需要配调试器工程里如果用了 Keil 特有的东西还得改。适合新项目或者愿意花时间折腾的老项目。5.2 工具链的选择与安装编译器有两个选择用 Keil 自带的 armclang或者用开源的 arm-none-eabi-gcc。前者跟 Keil 编译结果一致后者生态更好、跨平台。我一般推荐后者因为社区资料多遇到问题好查。arm-none-eabi-gcc 可以从 ARM 官网下载也可以装 STM32CubeCLT 或者用包管理器装。装完之后把bin目录加到系统 PATH 里命令行敲arm-none-eabi-gcc --version能出版本号就说明装好了。调试器方面ST-Link 用 OpenOCD 或者 ST-Link GDB Server 都行J-Link 用 JLinkGDBServer。这些工具装好之后Cortex-Debug 插件通过配置文件去调用它们。5.3 配置 launch.json 实现一键调试.vscode/launch.json是调试配置的核心一个典型的 STM32 OpenOCD ST-Link 配置长这样{ version: 0.2.0, configurations: [ { name: Debug (OpenOCD), type: cortex-debug, request: launch, servertype: openocd, cwd: ${workspaceFolder}, executable: ${workspaceFolder}/build/你的工程名.elf, device: STM32F103C8, configFiles: [ interface/stlink.cfg, target/stm32f1x.cfg ], svdFile: C:/path/to/STM32F103.svd, runToEntryPoint: main, preLaunchTask: Build } ] }几个关键字段解释一下。executable指向编译出来的 elf 文件这个文件得先编译出来。device填你的芯片型号。configFiles是 OpenOCD 的配置文件interface那个指定调试器类型target那个指定芯片系列。svdFile是寄存器描述文件配了之后调试的时候能看到外设寄存器的值非常有用可以从芯片厂商官网或者 Keil 的 Pack 里找。preLaunchTask指定调试前先执行哪个任务一般就是编译任务这样按 F5 就能自动编译加调试。5.4 编译脚本的迁移这一步是最费时间的。Keil 工程里的编译选项要翻译成 Makefile 或者 CMakeLists.txt。需要关注的东西包括源文件列表、头文件搜索路径、宏定义、优化等级、链接脚本、启动文件。如果你不想手写有几个偷懒的办法。一是用 STM32CubeMX 生成 Makefile 工程然后把源文件挪过去。二是用cubemx2makefile之类的工具转换。三是直接用 PlatformIO它对 STM32 的支持很好配置文件写起来比裸 Makefile 简单得多。实操心得迁移的时候先把编译跑通再管调试。编译不过的时候重点检查三个东西——启动文件选对没有.s文件跟芯片型号要匹配、链接脚本对不对.ld文件里的内存布局要跟芯片一致、宏定义全不全尤其是STM32F103xB这种器件宏。这三个地方错了编译能过但跑不起来或者根本链接不过。6. 常见问题与排查技巧6.1 编译相关的问题问题一命令行编译报“cannot open source input file”这个基本就是路径问题。检查tasks.json里的工程路径对不对工程文件名有没有写错。还有一种可能是工程里引用了相对路径的文件而命令行执行时的工作目录不对。解决办法是在tasks.json里加options: { cwd: ${workspaceFolder} }明确指定工作目录。问题二编译成功但 VS Code 问题面板没显示错误检查problemMatcher的正则写对没有。Keil 的错误格式在不同版本里可能略有差异有的是error:有的是error #20:。可以先把build_log.txt打开看看实际格式再调整正则。另外fileLocation的设置也很关键设成autoDetect一般能处理相对路径。问题三AC5 和 AC6 混用导致报错Keil 从 MDK 5.37 开始默认用 AC6 编译器但很多老工程还是 AC5 的配置。如果你在命令行编译时报一堆语法错误先确认编译器版本。在tasks.json里可以通过-t参数指定 target但更简单的办法是直接在 Keil 里把工程切到对应的编译器版本命令行会跟着走。6.2 调试相关的问题问题一OpenOCD 连不上目标板先检查硬件连接ST-Link 的 SWDIO、SWCLK、GND、3.3V 四根线接对没有。然后检查configFiles里的接口配置和芯片配置对不对。如果用的是山寨 ST-Link有时候需要降速可以在 OpenOCD 配置里加adapter speed 1000试试。还有一种情况是芯片被读保护了需要先解锁。问题二调试时断点打不上断点打不上通常是编译优化的问题。把优化等级调到-O0再试。另外确认 elf 文件是最新编译出来的路径没写错。如果用的是-flto链接时优化断点也会不准调试阶段先关掉。问题三SVD 文件加载后看不到外设寄存器SVD 文件的芯片型号要跟实际芯片完全匹配。STM32F103 有好几个变体SVD 文件也不一样。加载之后如果外设列表是空的多半是文件不对。可以从 Keil 的 Pack 目录里找对应的 SVD路径一般在Keil_v5/ARM/Packs/Keil/STM32F1xx_DFP/x.x.x/Device/Include/下面。6.3 常见问题速查表现象可能原因排查方向满屏红色波浪线头文件路径没配检查 c_cpp_properties.json宏定义相关的代码报错defines 不全对照 Keil 的 C/C 标签页命令行编译无输出UV4 路径错或工程名错手动在 cmd 里跑一遍编译报文件被占用Keil GUI 开着关掉 Keil 再编译调试连不上接线或配置错检查 SWD 接线和 configFiles断点不生效优化等级太高改成 -O0跳转找不到定义IntelliSense 没索引完重启 VS Code 或重建索引7. 我踩过的坑和几条实在建议先说一个最容易被忽略的点Keil 工程里的文件分组和 VS Code 的目录结构是两回事。Keil 里你可以把不同目录的文件分到一个 Group 里但 VS Code 是按实际目录树显示的。所以打开工程之后你看到的目录结构可能跟 Keil 里完全不一样别慌这是正常的。找文件用CtrlP按文件名搜比在目录树里翻快得多。第二个坑是编码问题。Keil 默认用 GB2312 编码VS Code 默认用 UTF-8。打开老工程的时候中文注释全是乱码。解决办法是在 VS Code 设置里搜files.encoding改成gb2312或者在工程根目录的.vscode/settings.json里加files.encoding: gb2312。但更好的做法是把工程统一转成 UTF-8一劳永逸只是转的时候注意别把代码里的中文字符串搞坏了。第三个坑是路径大小写。Windows 下路径不区分大小写但 Keil 命令行有时候会抽风。我遇到过工程文件名是MyProject.uvprojx但命令行里写成myproject.uvprojx就报找不到文件的情况。所以路径和文件名严格按实际的大小写来写别偷懒。再说几条实在建议。如果你只是想让编辑体验好一点方案一足够了别折腾命令行编译省下来的时间够你多写几百行代码。如果你确实需要命令行编译先把 bat 文件在 cmd 里跑通再往 tasks.json 里搬这样出问题好定位。如果你要上全流程方案做好花一两天时间折腾的心理准备而且最好拿一个新工程练手别拿正在交付的项目开刀。最后分享一个提高效率的小技巧在 VS Code 里装一个Bookmarks插件在关键代码位置打上书签用快捷键在书签之间跳转。嵌入式代码经常要在中断服务函数、初始化代码、主循环之间来回跳有了书签能省不少滚动的时间。这个插件跟 Keil 工程配合用体验很好。
