0%

Keil 工程迁移到 VS Code + EIDE——智能小车项目前置准备

开篇

我在b站获取了up主“陈加油嗯”的 STM32F103C8T6 智能小车项目(蓝牙遥控 + 循迹 + 避障三合一),他是用 Keil5 + 标准外设库写的。但我平时的主力编辑器是 VS Code,所以目标很明确:

在 VS Code 里把这个 Keil 工程编译、烧录、跑起来,源代码一行不改。

折腾了半天,搜索了一些教程,最终用 EIDE 插件顺利跑通:导入工程 → GCC 编译 → ST-Link 烧录,全程图形化点击。这篇文章把完整流程和 4 个遇到的坑记录下来,以后任何 Keil 工程想迁移都能照搬。

一、为什么要从 Keil 搬到 VS Code

对比项 Keil5 VS Code + EIDE
编辑器体验 编辑器老、代码补全弱 现代编辑器,补全/跳转/主题好
编译器 ARMCC(Keil 专用) ARM GCC(开源,通用)
烧录 内置 支持 ST-Link/J-Link/OpenOCD
跨平台 仅 Windows Windows/Linux/Mac
版本管理 弱 原生 Git 集成
学习成本 工程文件是黑盒 构建过程透明可见

标准外设库(SPL)工程的结构非常规整,天然适合迁移:

1
2
3
4
5
6
7
工程目录/
├── User/ # main.c、中断处理
├── System/ # 延时、sys
├── HARDWARE/ # 各外设驱动(LED/电机/OLED/串口…)
├── Library/ # 标准外设库(SPL)
├── Start/ # 启动文件 + CMSIS 核心
└── Project.uvprojx # Keil 工程文件(配置的源头)

二、三条路线怎么选

把 Keil 工程搬到 VS Code,我研究后有三条路线:EIDE 插件、手写 CMake、ST 官方扩展。

1. 主路线:EIDE 插件(我选的)

EIDE 插件 手写 CMake
移植工作量 零(直接导入 .uvprojx) 写 CMakeLists + 链接脚本 + 换启动文件
操作方式 图形化按钮 命令行
宏/头文件路径 自动从 Keil 工程解析 手动照抄
适合人群 想快速跑通 想深入理解构建流程 / 做 CI

学习阶段选 EIDE 最快——它只是编译工具,不影响你读代码理解代码。CMake 方案留作进阶(做 CI、复杂工程时再用)。

2. 为什么不选 ST 官方扩展?

keysking的stm32教程中安装的是stm32Cube插件,为什么不用它,用第三方 EIDE? 关键区别在这里:

STM32 官方扩展 EIDE
出身 ST 官方 第三方
支持的芯片 仅 STM32 几乎所有单片机
支持的工程格式 仅 CubeMX 生成(.cproject) Keil(.uvprojx)、CubeMX、ESP-IDF…
支持的库 仅 HAL 库 标准库(SPL) + HAL 库都行
编译器 armclang / GCC GCC / ARMCC / IAR
导入别人现成的 Keil 工程 ❌ 不认 ✅ 一键导入

ST 官方扩展是ST 生态的延伸:它的世界只有”CubeMX 生成的 HAL 工程”这一种工作流。而我们的目标是导入别人用 Keil + 标准外设库写的现成工程,官方扩展根本不认这种格式——EIDE 是通用工程管理器,芯片、工程格式、编译器都”来者不拒”。

所以不是官方扩展不好,是它只管自己生态,管不了别人的 Keil 工程。而EIDE 是超集。

三、环境搭建

1. 安装 ARM GCC 编译器

EIDE 编译需要 ARM 交叉编译器(GCC 的 ARM 版本,替代 Keil 的 ARMCC)。

  • 下载:ARM 官方 GNU 工具链(搜索 “Arm GNU Toolchain Downloads”),Windows 版选 arm-none-eabi 目标
  • 安装:一路 Next,安装时勾选 “Add path to environment variable”(以后终端直接敲 arm-none-eabi-gcc)
  • 安装路径默认在:C:\Program Files (x86)\Arm GNU Toolchain arm-none-eabi\<版本>
    • ⚠️ 注意版本目录名是 14.2 rel1(空格),不是 14.2.rel1(点)

验证安装(新开终端):

1
2
arm-none-eabi-gcc --version
# arm-none-eabi-gcc.exe (Arm GNU Toolchain 14.2.Rel1 (Build arm-14.52)) 14.2.1

2. 安装 EIDE 插件

VS Code 扩展市场搜 Embedded IDE(cl.eide),或命令行安装:

1
code --install-extension cl.eide

EIDE 需要 .NET 运行时(首次启动会自动检测,缺了会提示安装)。

3. 指定编译器路径(关键)

EIDE 默认不知道 GCC 装在哪,在 VS Code 用户设置(settings.json)里配置:

1
2
"EIDE.ARM.GCC.InstallDirectory": "C:\\Program Files (x86)\\Arm GNU Toolchain arm-none-eabi\\14.2 rel1",
"EIDE.ARM.Option.AxfToElf": true

四、导入 Keil 工程

第 1 步:VS Code 打开工程目录

VS Code → 文件 → 打开文件夹,选中包含 Project.uvprojx 的那个目录(不是打开 .uvprojx 文件本身,是打开它所在的文件夹)。

💡 先给工程目录起个纯英文名:比如 D:\projects\car\。这一步很重要,坑④会解释为什么——中文路径会导致链接失败,现在养成习惯最省事。

第 2 步:找到 EIDE 面板

安装 EIDE 插件后,左侧活动栏会出现一个新的图标(嵌入式芯片图案,⚠️ 不是资源管理器那个文件图标)。点击它,EIDE 面板就展开了。

第 3 步:导入 Keil 工程

  1. 在 EIDE 面板顶部找到 “导入工程” 按钮(Import),点击
  2. 弹出菜单选择 **”Keil”**,再选择 “Keil ARM project (.uvprojx)”
    • 这里有两个选项:**”ARM 还是 C51?”**——答案是 ARM。Keil MDK 就是 ARM 版本(Microcontroller Development Kit),我们的 STM32F103 是 Cortex-M3 内核,属于 ARM;C51 是给 8051 单片机用的,选错后面全乱
  3. 在弹出的文件选择框中,选中工程目录下的 Project.uvprojx,确认

第 4 步:等待自动解析,EIDE 在做”配置搬运”

导入瞬间 EIDE 会只读解析 Project.uvprojx(Keil 工程文件本质是 XML,记录了全部配置),自动生成:

从 uvprojx 读出的内容 EIDE 的处理
源文件清单(40 个 .c/.s) 构建列表
头文件路径(IncludePath) 编译选项
宏定义(Defines) 预处理器定义
芯片型号(STM32F103C8) 芯片支持包

这个过程只读,不会破坏原 Keil 工程——uvprojx 原封不动,原工程随时还能用 Keil 打开。EIDE 只是生成自己的一套工程配置(.eide/ 目录),两边共存互不干扰。

第 5 步:安装推荐扩展(可选)

导入完成后 VS Code 右下角可能弹窗推荐扩展(来自 dan-c-underwood 等的),直接点安装——ARM 汇编语法高亮、C/C++ 等。

第 6 步:切换编译器为 GCC

导入后 EIDE 默认沿用 Keil 工程的编译器设置 AC5(ARMCC)。注意:即使电脑上装了 Keil5,EIDE 也不知道 ARMCC 装在哪——它需要额外的路径配置(EIDE.ARM.ARMCC5.InstallDirectory 指向 Keil 的 ARM\ARMCC 目录),没配置的话此时点编译会报:

1
无法找到编译器 'AC5' 的位置![path]: 'null\bin'

(null\bin 就是”路径没配置”的痕迹)

我的操作:在 EIDE 面板顶部工具栏找到编译器选择框(此时显示 “AC5”),点击弹出下拉,选择 **”GCC”**(GNU Arm Embedded Toolchain)。切换后 EIDE 会读取我们在第三章配置的 EIDE.ARM.GCC.InstallDirectory 路径,弹出确认框时点确定即可。image-20260810193532935

另一个选择:如果你想让 EIDE 直接用 Keil 的 ARMCC 编译,也可以在 VS Code 设置里配置 EIDE.ARM.ARMCC5.InstallDirectory 指向 Keil 安装目录。但推荐用 GCC——开源免费不依赖 Keil 安装、命令行通用、构建过程透明,本文后面的坑也都是按 GCC 写的。

判断标准:AC5/AC6 是 Keil 的编译器(ARMCC),GCC 是 ARM 官方工具链。EIDE 下拉框里显示哪个就点哪个,关键是让 EIDE 能找到对应编译器的真实路径。

第 7 步:认识 EIDE 面板

切换完成后,EIDE 面板展开成这样的结构:

1
2
3
4
5
📁 stdperiph (Target 1)
├── 项目资源 # 源文件树(点击可查看/添加/删除文件)
├── 芯片支持包 # 芯片型号、CPU 类型、链接脚本
├── 构建配置 # 链接脚本路径、烧录配置 ← 坑④和烧录都在这
└── C/C++ 属性 # 预处理器定义、编译选项 ← 坑①在这

到这里导入就完成了。接下来点编译按钮(锤子图标),会依次踩到坑①~④——别慌,都是工具链差异的小修补,下面逐个解决。

五、坑 ①:编译报错 “Please select first the target STM32F10x device”

标准外设库的 stm32f10x.h 第 96 行有个设备检查:必须定义芯片型号宏,否则 #error 直接拒绝编译:

1
2
3
#if !defined(STM32F10X_LD) && !defined(STM32F10X_MD) && ...
#error "Please select first the target STM32F10x device used in your application"
#endif

Keil 工程里只有 USE_STDPERIPH_DRIVER 一个宏(uvprojx 里没写型号宏),EIDE 原样搬过来了 → 编译全报错。

解法:EIDE → C/C++ 属性 → 预处理器定义,改为:

1
USE_STDPERIPH_DRIVER;STM32F10X_MD
  • STM32F10X_MD = Medium Density 中容量 = F103C8/CB 系列(HD 是高容量 F103ZE,LD 是低容量 F103C6)
  • ⚠️ 分号分隔,不能有任何空格,否则报 cannot have whitespace on either side of '=',这里我直接点修改,老是报这个错误,然后我删掉原本的定义,重新添加,就不报错了,很神奇的bug。

这一个宏修好后,后面连带的 TIM4_IRQn、USART1_IRQn 未定义错误也全消失了——它们都是没定义型号宏导致的连锁反应。

六、坑 ②:启动文件汇编语法不兼容

39 个 C 文件全过了,栽在启动文件上:

1
2
Error: bad instruction `stack_size EQU 0x00000400'
Error: bad instruction `__vectors DCD __initial_sp'

原因:Keil 工程里的 startup_stm32f10x_md.s 是 ARMCC 汇编语法(EQU/AREA/DCD/PROC 是 Keil 汇编器的指令),而 EIDE 用的是 GNU 汇编器(arm-none-eabi-as),两者语法不通用。

解法:换成 GNU 语法的启动文件。ST 官方标准外设库里其实自带 GNU 版(RIDE7 工具链版),长这样:

1
2
3
4
5
.syntax unified
.thumb
.word _sidata @ 数据段起始
.word _sdata
...

从 GitHub 的 STM32F103 GCC 模板仓库(如 0x0E86/STM32F103C8T6)拉一份即可,文件名不变,直接覆盖 Start/startup_stm32f10x_md.s。原文件先备份(改名 .bak),哪天回 Keil 还能还原。

判断启动文件是什么语法:看到 .syntax/.word/.section 是 GNU 版;看到 EQU/AREA/DCD 是 ARMCC 版。

七、坑 ③:老 CMSIS 与 GCC 14 的兼容问题

编译 core_cm3.c 时报汇编错误:

1
Error: registers may not be the same -- `strexb r0,r0,[r1]'

原因:2011 年的老版 CMSIS 内联汇编写死了寄存器(ARMCC 的写法),GCC 14 的寄存器分配检查更严格,目标寄存器与源寄存器相同直接报错。

解法:替换为 GCC 兼容版 core_cm3.c/core_cm3.h(同上,模板仓库里有现成的)。新版里 GCC 分支用标准约束模板写法:

1
__ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value));

这种编译器差异的问题,stm32f10x.h 等库文件本身一般兼容(内部有 #if defined(__GNUC__) 分支),只有老汇编和 CMSIS 内核文件需要换。

八、坑 ④:中文路径导致链接失败

编译全过,链接报:

1
ld.exe: cannot open linker script file ...1.STM32F1????...v1.1/...stm32f103c8.lds: Invalid argument

注意是 Invalid argument(不是 No such file)—— Windows + GCC 的经典编码问题:链接器收到中文路径时编码损坏(????),文件明明存在却打不开。编译能过是因为 EIDE 编译用相对路径,而链接脚本存的是绝对路径,就踩雷了。

解法:工程目录必须全英文。把工程复制一份到纯英文路径(如 D:\stm32project\smartcar\stdperiph\),重新导入即可。

这条经验很重要:嵌入式工程路径永远用纯英文,中文路径在 OpenOCD 烧录、调试器、脚本里还会继续捣乱,趁早搬走。

九、编译 & 烧录

配置完以上四点,点编译,输出:

1
2
3
4
5
6
[ INFO ] start compiling (jobs: 16) ...
[ INFO ] start linking ...
Memory region Used Size Region Size %age Used
FLASH: 13792 B 64 KB 21.04%
RAM: 1452 B 20 KB 7.09%
[ DONE ] build successfully !

链接时出现的 _close is not implemented and will always fail 等 warning 是裸机环境的正常提示(没有文件系统),无视即可。

烧录前把烧录配置从默认的 JLink 改为 ST-Link(构建配置里),第一次用会提示自动下载 STM32CubeProgrammer 烧录工具。接好 ST-Link(SWD 四线:3.3V/SWDIO/SWCLK/GND),点烧录按钮:

1
2
3
4
STM32CubeProgrammer v2.8.0
Device: STM32F103C8T6 Flash: 64 KB
Download verified successfully ✅
RUNNING Program... Start operation achieved successfully

烧录验证通过,程序跑起来了。

十、总结:迁移 Checklist,可直接照搬

以后任何 Keil 标准库工程迁移,按这个清单走:

1
2
3
4
5
6
7
8
9
□ 1. 装 ARM GCC(勾选加 PATH),settings.json 配 EIDE.ARM.GCC.InstallDirectory
□ 2. 装 EIDE 插件,导入 .uvprojx(选 ARM 不是 C51)
□ 3. 编译器切换为 GCC
□ 4. 预处理器定义补芯片型号宏:USE_STDPERIPH_DRIVER;STM32F10X_MD
□ 5. 启动文件换 GNU 语法版(备份原文件)
□ 6. core_cm3.c/h 换 GCC 兼容版(老 CMSIS + GCC14 才需要)
□ 7. 工程放纯英文路径(中文路径链接必挂)
□ 8. 构建配置指定链接脚本(.lds,64K Flash/20K RAM 等按芯片规格)
□ 9. 烧录配置选 ST-Link/J-Link,接线烧录

核心思路:EIDE 帮你自动完成 90% 的配置搬运(源文件、宏、路径都从 uvprojx 解析),剩下 10% 是工具链差异的修补——启动文件语法、CMSIS 版本、编码问题,都是换文件/换路径的事,你的业务代码一行都不用动。


参考资料: