用Keil 5写代码的日子,我大概是忍到头了。作为一个天天跟ARM固件打交道的嵌入式开发,我早就在VSCode里写Python和上位机,偏偏嵌入式工程一开Keil 5,就退回二十年前的编辑器体验:补全弱、配色刺眼、没法用Git对比、看个函数定义都得手动翻半天。前阵子折腾了一周,总算把“VSCode + Keil Studio插件”这套ARM开发环境完全跑通了,编译、烧录、调试全流程都能在VSCode里搞定,Keil 5直接降级成“偶尔点点编译”的后备工具。这篇文章就是我这次搭建过程的完整记录,包括工具链怎么选、配置文件怎么写、哪些坑千万别踩,给想换编辑器又怕翻车的朋友一个可以照着抄的方案。
先说清楚这套方案到底解决什么问题:你不需要彻底卸载Keil 5,也不用扔掉现有工程,而是把“写代码、看代码、查代码”这件事移到VSCode里,底层编译还是用ARM官方的ARM Compiler 5/6工具链,烧录调试走CMSIS-DAP或J-Link。熟悉VSCode的工程师,尤其是被Keil编辑器折磨过的同学,这个方案能明显提升日常开发舒适度;学生党想统一开发环境、不想电脑上装一堆IDE的,也值得试试。前提是你愿意花半小时做一次性配置,接下来这篇文章就带你走完整个过程。
1. 为什么要从Keil 5换到VSCode + Keil Studio插件
1.1 Keil 5那些年让人血压升高的瞬间
先说说我为什么动这个念头。Keil 5本身是ARM嵌入式开发的老牌工具,稳定性没问题,各种单片机厂商的例程基本都是拿它打开的,教程也最多。可它的编辑器体验实在拉胯:代码自动补全基本等于没有,写结构体成员要靠自己记;字体渲染在高分屏上一言难尽;最关键的是如果你同时做上位机、Linux开发,只为一个单片机工程就得在Keil和VSCode之间来回切换,快捷键、编码习惯全都不一样,一天下来精神损耗特别大。
还有个小问题是工程文件管理。Keil的.uvprojx是XML格式,虽说不难解析,但你想用Git做代码审查、看某一行最新改动是谁提交的,Keil编辑器完全不支持。我试过用外部工具比对工程配置,那体验真是令人头大。实际上很多同事早就在VSCode里看代码,遇到编译问题再回Keil里点一下,等于把两个工具的优点硬缝合起来,但操作还是割裂的。
1.2 这套“新组合”到底是什么,靠谱吗
我最初也担心:VSCode + Keil Studio插件是不是某个开源爱好者做的半成品?查了之后才发现,这是ARM官方出的扩展包,在VSCode扩展市场里搜“Arm Keil Studio Pack”就能找到。它把CMSIS支持、设备树查看、嵌入式调试器、MDK调试支持这些能力整合到一起,配合VSCode的C/C++扩展和Cortex-Debug扩展,就能实现类似Keil MDK的完整开发闭环。
说白了,架构很简单:VSCode当“前端”,负责编辑、智能提示、Git、终端;后端还是ARM编译器(ARMCC或ARMCLANG)负责真正把C代码变成机器码;调试器部分通过Cortex-Debug连接J-Link、CMSIS-DAP这类调试探头。用个不太恰当的类比,Keil 5相当于一个“一体化集成灶”,什么都有但每个模块都一般;VSCode这套相当于“模块化厨房”,编辑器、编译器、调试器各干各的,通过插件把它们拼起来,拼好了反而更顺手。
1.3 什么人最适合这套方案
不是所有人都需要换,我先帮你判断一下。如果你只是照着教程点鼠标、看视频做开发,Keil 5用着没觉得难受,那不建议折腾,毕竟稳定压倒一切。但如果你满足下面任意一条,我建议投入半小时迁移:
- 日常大量阅读代码、做代码评审,需要好的符号跳转和全局搜索;
- 同时维护多个工程,希望统一用VSCode作为入口;
- 用Git管理固件源码,希望在编辑器里直接看Diff、提交记录;
- 被Keil编辑器的高分屏缩放折磨得视力下降;
- 团队协作时各人编辑器不同,想找到一个项目级统一配置方案。
我自己属于第一类,迁移完之后最明显的感受是:写代码时不会老惦记“这个有没有更高效的操作方式”,大脑专注度提高了不少。所以这套方案的定位不是“替代Keil”本身,而是“优化日常编码体验,保留原有编译调试能力”。
2. 环境准备清单:一次搞清楚要装哪些东西
2.1 最小工具链一览
搭建这个环境,你需要准备的东西比想象中少。我整理了一张最小清单,按安装顺序排列,这张表也是我后来重装电脑时的操作指引:
| 组件 | 作用 | 获取方式 |
|---|---|---|
| VSCode | 主编辑器,承载所有插件和IDE体验 | VSCode官网下载安装包 |
| Arm Keil Studio Pack扩展 | 官方集成插件,提供CMSIS、设备支持等能力 | VSCode扩展市场在线安装 |
| C/C++扩展 | 提供代码补全、符号跳转、语法高亮 | VSCode扩展市场在线安装 |
| Cortex-Debug扩展 | 连接调试探头,实现在线调试 | VSCode扩展市场在线安装 |
| ARM Compiler 5.06或6 | 真正的C编译器,负责编译出固件 | 安装Keil MDK后自带,或从ARM官网单独获取 |
| CMSIS DFP芯片支持包 | 包含芯片的头文件、启动文件、Flash算法 | Keil官网Pack Installer,或对应芯片厂商官网 |
这里最容易误解的点是:VSCode本身并不会编译ARM代码,它只是个“壳”。真正干活的还是ARM Compiler。所以网上有人说“不用装Keil了”,严格来讲不完全准确——如果你只是想写代码不编译,可以不装;但你要编译、要下载调试,还是得有编译器。我的做法是电脑上保留Keil MDK用于获取编译器文件,日常编辑全部在VSCode完成,等于把Keil“工具化”而不是“抛弃化”。这样最省心,兼容性也最有保障。
2.2 ARM编译器版本怎么选:AC5还是AC6
ARM编译器目前主流是两个大版本:ARM Compiler 5(AC5,典型版本5.06 update 7 build 960)和ARM Compiler 6(AC6,基于Clang架构)。这个选择很大程度上决定了你后续踩不踩坑。
AC5是传统ARMCC编译器,很多芯片厂商的老固件库、老例程都是用它开发的,如果你维护的是用了四五年的老工程、或者要编译厂商只提供了AC5支持的标准外设库,必须用AC5,强行用AC6会报一堆莫名其妙的语法错误和编译错误。AC6是基于Clang的新一代编译器,编译速度快、对C99/C11支持更好、报错信息更友好,新开的工程建议直接用AC6。但AC6有一个敏感点:它需要许可证,尤其是使用MDK时,通常跟你的MDK授权绑定;AC5在老版本里反而不需要单独的许可证,用起来省事不少。
我的建议是:初学者或老工程维护者,优先装AC5 5.06 update 7;新建项目且编译器选择自由,可以尝鲜AC6。在VSCode里其实可以同时配置两套构建任务,按需切换,这个后面讲配置文件时会提到。
2.3 芯片支持包DFP与CMSIS的作用
还有个容易被跳过但很重要的部分:CMSIS DFP(Device Family Pack)。它是什么?简单说就是芯片厂商(ST、NXP、Nuvoton等)为自家芯片做的“身份证+说明书大礼包”,里面包括芯片寄存器定义头文件、启动文件、链接脚本、Flash烧录算法。编译时编译器要靠它认识你的芯片型号和外设寄存器,调试烧录时也要靠它知道怎么操作Flash。
在Keil MDK里,这个DFP通过Pack Installer在线安装;用VSCode时,如果按我前面的方式装了Keil MDK,DFP也已经放在本地了,编译器、链接脚本都能找到。如果你不想装完整MDK,也可以去Keil官网下载对应的.pack文件,再用解压工具或专用工具解包到本地目录,然后把这个路径配置给VSCode的includePath。这个方式配置起来更麻烦,所以我个人不推荐新手用。老老实实装个MDK,哪怕只用它的编译器,也比你手工管理一堆头文件和启动文件省心。
3. 从零配置:VSCode环境搭建实操全程
3.1 安装VSCode并做基础设置
第一步没什么技术含量,去VSCode官网下载Windows版本,一路Next安装。但有几个细节值得注意:安装时尽量勾选“添加到PATH”,后面命令行操作会用到;扩展安装路径和用户数据最好保持默认,位置太偏后续排查问题很难受。装好后记得在设置里把自动保存打开,文件编码设为UTF-8,字体换成等宽字体比如Consolas或JetBrains Mono,这些都可以提升后续体验。
此外,如果你和我一样看中文菜单更舒服,可以装中文语言包(Chinese Language Pack),装完按提示重启即可。这一步不是必须,但误打误撞少看一些英文报错,心理压力会小很多。设置完保存主题,我建议用一个对眼睛友好的深色主题,毕竟VSCode里写一天代码,眼睛舒适度差别很大。
3.2 安装Keil Studio插件和调试扩展
打开VSCode的扩展市场,搜索“Arm Keil Studio Pack”,点安装。这个扩展包里其实包含了好几个子组件,比如CMSIS支持、Docker、Device Tree、MDK调试器等,一次性都装上。然后额外搜索安装两个必须的扩展:C/C++(微软官方出品,提供IntelliSense代码补全和调试适配)和Cortex-Debug(用来连接JLink、CMSIS-DAP进行嵌入式调试)。
装C/C++扩展时有个细节:第一安装完它会提示把微软的C/C++扩展设为代码补全提供者,建议选允许。Cortex-Debug那一步需要注意的则是,它依赖你电脑里有对应的调试服务器软件,比如你拿J-Link调试,要装SEGGER的J-Link驱动;拿DAPLink调试,则推荐用pyOCD。这些调试服务器VSCode插件本身不会自动安装,很多人卡在这。
3.3 编译器的两种获取方式
编译器这块是整套配置里的核心关键点。我前面提过最省事的方式是装好Keil MDK,因为装完之后C:\Keil_v5\ARM目录下就有ARMCC(AC5)和ARMCLANG(AC6)两个子目录,编译器全在里面。如果你确实不想装完整MDK,也可以去ARM官网注册账号,下载独立的ARM Compiler 5.06安装包,但这个过程要填一堆表,下载回来的还可能是命令行版,没有图形安装引导,对新手并不友好。
无论走哪条路,最终你要确认一件事:armcc.exe或armclang.exe的路径能被命令行访问到。我推荐在Windows系统环境变量里新建一个ARMCC_HOME,指向你编译器所在目录,再把编译器的bin目录加入PATH。比如我的路径是C:\Keil_v5\ARM\ARMCC\bin,就在PATH里加上这一条。这样之后VSCode的构建脚本只要引用${env:ARMCC_HOME}就能找到编译器和头文件,不用在多个配置文件里写死绝对路径,日后迁移也方便。
3.4 创建工程目录与核心配置文件
跑通这套环境的关键,是往项目里塞几个VSCode认识的文件:.vscode目录下的tasks.json、c_cpp_properties.json和launch.json。这三个文件分别负责构建任务、智能感知、调试配置,是整套环境的“粘合剂”。
先说tasks.json,它告诉VSCode按什么命令编译你的工程。一个小型工程可以直接调用armcc逐文件编译,但真实项目往往几十个源文件,手写太累。我建议在工程根目录放一个build.bat脚本,把所有编译链接命令放在一起,然后tasks.json只负责调用这个脚本。这样逻辑清晰,也方便以后跟CI集成。
下面是我当时配置的一个最小可用的build.bat片段,思路是:清空build目录、用armcc编译main.c和startup文件、用armlink链接成axf、最后用fromelf生成bin和hex。注意启动文件不规则路径时,命令行里要写全相对路径或绝对路径:
@echo off setlocal set BLDDIR=build if not exist %BLDDIR% mkdir %BLDDIR% armcc.exe -c --cpu Cortex-M4 -DSTM32F407xx ^ -I.\Inc -I.\Drivers\CMSIS\Include -I.\Drivers\CMSIS\Device\ST\STM32F4xx\Include ^ -o %BLDDIR%\main.o main.c armcc.exe -c --cpu Cortex-M4 ^ -o %BLDDIR%\startup_stm32f407xx.o Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\gcc\startup_stm32f407xx.s armlink.exe --ro-base 0x08000000 --entry Reset_Handler ^ %BLDDIR%\main.o %BLDDIR%\startup_stm32f407xx.o ^ -o %BLDDIR%\firmware.axf fromelf.exe --bin %BLDDIR%\firmware.axf --output %BLDDIR%\firmware.bin fromelf.exe --i32 %BLDDIR%\firmware.axf --output %BLDDIR%\firmware.hex echo Build Done!这个脚本能直接用,但真实工程要把源文件列表替换成你自己项目的文件清单。如果工程文件太多,更省力的方式是用Keil里导出的命令行编译参数,或者干脆让VSCode调用Makefile,这里不展开,先把基本流程跑通最重要。
再看看c_cpp_properties.json,这个文件决定了VSCode的智能提示能不能正确识别你项目里的头文件和宏定义。如果IntelliSense抽风、全是红色波浪线,基本都是这个文件里面includePath或defines没写对。示例配置:
{ "version": 4, "configurations": [ { "name": "ARM", "includePath": [ "${workspaceFolder}/Inc", "${workspaceFolder}/Drivers/CMSIS/Include", "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F4xx/Include" ], "defines": [ "STM32F407xx", "USE_HAL_DRIVER" ], "compilerPath": "C:/Keil_v5/ARM/ARMCC/bin/armcc.exe", "cStandard": "c11", "intelliSenseMode": "windows-gcc-x64" } ] }注意这里的compilerPath,如果你装的是AC6就换成armclang.exe。defines里填的宏必须跟项目实际编译时用的一致,否则代码里很多条件编译分支会显示未定义,看着难受。
launch.json是调试配置。我用Cortex-Debug加pyOCD,配置如下,关键是executable要指向带调试信息的.axf文件,device要写对芯片型号:
{ "version": "0.2.0", "configurations": [ { "name": "Flash and Debug", "cwd": "${workspaceFolder}", "executable": "${workspaceFolder}/build/firmware.axf", "request": "launch", "type": "cortex-debug", "servertype": "pyocd", "device": "stm32f407vg", "runToEntryPoint": "main" } ] }如果你用的是J-Link,把servertype改成jlink,然后记得要先在SEGGER的J-Flash里把驱动装好,这一点我在第5章避坑指南里会细讲。
4. 把固件跑起来:编译、烧录、调试一条龙
4.1 配置构建任务,一键编译固件
配置文件都放好后,回到VSCode,按Ctrl+Shift+B,如果之前没选择过默认构建任务,它会弹出任务列表让你选。选我们配置好的Build任务(通常是“Build (AC5)”或你自定义的label名),然后就开始编译了。编译输出会显示在集成终端里,armcc的错误信息会有Error/Warning编号,点击带行号的错误,VSCode能直接跳到对应代码位置。
这一步跑通意味着你已经成功一大半了。我印象最深的是第一次在VSCode里看到编译完成不加干预输出“Build Done!”时,那个心情简直像完成一次小型创业。不过一开始大概率不会这么顺,你会遇到armcc不在PATH中、找不到头文件、语法定义冲突等问题,具体排查看第5章。
4.2 生成HEX/BIN并完成烧录
编译结束后,build目录下会生成.axf、.bin、.hex三个产物。.axf是带调试信息的最终镜像,调试用;.bin和.hex是烧录用。如果你的构建脚本里调了fromelf生成hex/bin,这一步自动完成,不需要额外操作。如果你想在VSCode里直接把固件烧到板子上,两个常见路径:
一是用pyOCD命令行,适合手头是DAPLink/CMSIS-DAP探头的用户。安装好pyocd后,命令行执行pyocd flash -t stm32f407vg build/firmware.hex就能完成烧录。二是用J-Link的J-Flash命令行,如JFlash.exe -openprj你的工程.jflash -openbuild/firmware.hex -auto -exit,配置好J-Flash工程后一条命令搞定。无论哪种,我建议把烧录命令也写进tasks.json里,单独建一个“Flash”任务,这样在VSCode里按快捷键就能实现“build后直接烧录”的闭环,效率提升立竿见影。
4.3 在线调试和断点体验
调试这块,体验最像当年用Keil但界面现代得多。按F5进入调试会话,Cortex-Debug会自动启动调试服务器(比如pyOCD),连接目标板并加载固件。启动后你可以在main函数入口处有一个初始断点,这由launch.json里的runToEntryPoint控制,也可以手动在代码边上点红点打断点。
实际调试体验我要说实话:比Keil 5好不少,但也别抱“完美”期待。VSCode里能看局部变量、寄存器、调用栈,能鼠标悬停看变量值,多核心芯片也能查看各个核的状态;不过某些高级功能比如条件断点在不同编译器下的表现偶尔会有小瑕疵,数据观察窗口的刷新速度也没有专用IDE快。我用下来最大的感受是“足够用”,日常调试的基础功能都齐了,而且界面逻辑一致,不会像Keil那样连调试窗口拖个位置都费劲。
5. 避坑指南与常见问题速查
5.1 高频错误和排查办法
搭建过程中我踩了不少坑,也看了很多群里朋友遇到的神奇问题,整理成一张速查表,建议收藏,遇到问题按表索骥:
| 错误现象 | 原因 | 解决办法 |
|---|---|---|
| CreateProcess failed,命令含fromelf | PATH里没有fromelf.exe,或文件被占用 | 检查编译器路径是否加入PATH,关闭Keil释放占用 |
| 找不到armcc / armclang命令 | 环境变量没配好 | 确认ARMCC_HOME和PATH配置,重新打开VSCode |
| 编译时报AC6许可证错误 | AC6需要有效授权 | 临时切回AC5,或确认MDK授权在有效期内 |
| 中文路径或空格导致编译失败 | 工具链对路径字符敏感 | 工程目录只用英文、数字和下划线 |
| 大量红色波浪线但编译正常 | c_cpp_properties.json的includePath/defines配置不对 | 按工程实际头文件路径和宏定义修正 |
| 调试时找不到设备 | 调试探头驱动未安装,或servertype配置错误 | 装好J-Link驱动或pyOCD,核对芯片型号 |
我个人踩得最深的一个坑就是开头表格里提到的fromelf报错。当时是电脑同时开着Keil和VSCode,Keil工程文件还占用了build目录里的axf和hex,结果VSCode一侧编译完,执行fromelf生成hex时就是报告CreateProcess failed,一查才发现是Keil把输出文件锁住了。后来习惯是先关Keil,或者干脆把VSCode的构建输出目录和Keil的工程输出目录分开,问题再也没出现。
5.2 几个容易被忽略的细节遗留问题
除了上面的硬错误,还有几个属于“不报错但体验打折”的软坑,很多教程都不提:
第一是工程路径别带中文和空格,Windows下尤其明显,VSCode本身没问题,但编译器一见到中文路径就容易抽风,规则不是100%必现,但踩一次就浪费半天。第二是老的.uvprojx工程想用VSCode构建,别指望有插件一键导入。网上有些工具能转工程格式,但效果参差不齐。我的做法是看需要编译哪些源文件,直接把编译参数写到自己的build.bat里,一次配置一劳永逸。第三是如果你和我一一样还带着老工程用AC5,记得在c_cpp_properties里把intelliSenseMode设置成windows-gcc-x64,AC5自带的老语法有时候会让VSCode的智能感知误判,用这个模式兼容性最好。
另外关于调试,如果你的板载调试器是DAPLink,pyOCD装好后要确认芯片型号写对。比如STM32F407,识别编号是stm32f407vg,不是stm32f407或stm32f407ze,写错会直接提示找不到目标,这属于最容易忽略的细节。我当时在pyOCD官方文档里翻了半天才找到正确写法。
5.3 我沉淀下来的一些使用建议
最后分享几个用顺之后的习惯,算是我个人向的推荐。我在工程根目录放了三个脚本:build.bat(编译)、flash.bat(烧录)、build_and_flash.bat(一条龙),然后在tasks.json里分别注册成三个任务,绑定不同快捷键。这样日常开发几乎不碰鼠标:Ctrl+Shift+B触发构建,没错误再按一个键烧录,调试直接F5,整个流程跟Keil里点那排按钮一样顺,甚至更快。
代码补全方面,除了C/C++扩展,Arm Keil Studio Pack里的CMSIS支持也会提供芯片寄存器补全,配合写寄存器实际体验相当不错。我还开了VSCode的Git集成面板,每次提交前能直观看到修改内容,这种感觉在Keil里是没有的。
如果你想把环境配置分享给团队,只需把.vscode目录和那几个bat脚本一并提交到代码仓库,同事克隆下来就能用,前提是各自电脑上装好编译器和调试工具。这个配置文件的“可复制性”是我觉得比Keil更先进的地方,因为Keil的工程配置、魔术棒设置很难在多人之间优雅地做代码审查,但在VSCode这套配置里全是纯文本,一眼看出差异。
说实话,我刚折腾完的时候朋友都问:折腾这个值不值?我的回答是,如果你只是偶尔点开Keil写两行代码,那不值;但如果你像我一样每天有大量时间跟代码打交道,把编辑器换成自己顺手的,不仅效率提升,心情也放松不少。至少在我不需要被那个上世纪风格的编辑器气到勾选错位之后,改起Bug来都心平气和多了。
最后再说一个小技巧:在VSCode里同时打开多个工程当工作区,用Ctrl+Tab在项目间切换,编译任务按F1搜索任务名称就行。Keil里多个工程同时开有多痛苦,用过的人都懂。这套方案算不上完美,但它确实让我觉得嵌入式开发也可以有现代开发者工具该有的体验。