1. 为什么新版MDK里找不到AC5编译器
如果你最近从官网下载了MDK 5.37或更新的版本,装完之后打开以前的老工程,大概率会看到一个让人血压升高的报错:Cannot find ARM Compiler 5,或者编译时直接提示Armcc.exe not found。这不是你的工程坏了,也不是芯片包没装,而是Keil从某个版本开始,默认只带ARM Compiler 6(AC6),把AC5从安装包里彻底拿掉了。
这件事的背景其实不复杂。AC5基于的是老一代的ARMCC编译器,AC6则是基于Clang/LLVM架构的新一代工具链。ARM官方推AC6推了很多年,理由是编译效率更高、对C99/C11标准支持更好、优化能力更强。但问题在于,大量存量工程——尤其是STM32标准库工程、GD32的早期例程、还有各种芯片原厂提供的参考代码——都是基于AC5写的。这些代码放到AC6下编译,轻则报一堆warning,重则直接编译失败,因为AC6对语法的严格程度和AC5完全不是一个量级。
所以现实情况就是:你不得不在新版MDK里把AC5单独装回来。这件事本身不难,但坑点集中在几个地方——安装包从哪来、装到哪个目录、怎么让MDK识别到、以及装完之后工程怎么切换。我前后在不同机器上装过不下十次,下面把完整流程和踩过的坑一次性讲清楚。
注意:AC5编译器属于ARM官方已停止维护的组件,本文仅讨论在合法授权环境下为兼容旧工程而进行的本地配置操作,请确保你的MDK持有有效许可证。
2. 装AC5之前必须搞清楚的几件事
2.1 AC5和AC6到底差在哪,为什么老工程非它不可
先把这个说透,不然后面切换编译器的时候你心里没底。
AC5的核心组件是armcc.exe,AC6的核心组件是armclang.exe。两者在编译行为上的差异主要体现在三个层面:
第一是语法宽容度。AC5对隐式类型转换、未声明函数、旧式K&R风格函数定义这些东西相对宽容,很多老代码里写了int foo()这种空参数列表的函数定义,AC5顶多给个warning,AC6直接报error。STM32标准库里的stm32f10x.h里面大量使用了__IO、__O这类宏定义配合位域操作,AC6对某些位域赋值的检查也更严格。
第二是内联汇编的处理方式。AC5用的是__asm关键字加ARM风格的汇编语法,AC6改成了__asm volatile加GNU风格。如果你工程里有直接操作寄存器的内联汇编代码,切到AC6基本要重写。
第三是链接脚本和分散加载文件。AC5用的.sct分散加载文件格式,AC6虽然也支持,但对某些语法细节的处理不一样,尤其是涉及__at绝对地址定位的时候。
所以结论很明确:如果你的工程是2018年以前创建的,或者用的是芯片原厂提供的标准库模板,老老实实装AC5,别跟自己较劲去改代码适配AC6,那个工作量远大于装一个编译器。
2.2 哪些MDK版本需要手动装AC5
不是所有MDK版本都需要折腾这一步。根据我的实际测试:
| MDK版本 | 是否自带AC5 | 说明 |
|---|---|---|
| MDK 5.36及以前 | 自带 | 安装包里直接包含AC5,无需额外操作 |
| MDK 5.37 | 不带 | 最后一个默认不带AC5的版本,需要手动装 |
| MDK 5.38 / 5.38a | 不带 | 需要手动装,且安装方式有变化 |
| MDK 5.39及以上 | 不带 | 需要手动装,推荐用独立安装包方式 |
你可以打开MDK,点Project -> Manage -> Project Items,在Folders/Extensions标签页里看ARM Compiler下拉框,如果里面只有Use default compiler version 6而没有version 5的选项,那就说明你需要手动装。
2.3 安装包从哪来,别乱下
这是第一个大坑。网上搜"AC5编译器下载",出来的结果五花八门,很多是捆绑了乱七八糟东西的。正确的做法是:
AC5编译器的独立安装包,ARM官方把它放在MDK的Legacy Support页面里。你可以在ARM Developer官网的MDK下载区域找到ARM Compiler 5的独立安装程序,文件名通常类似ARM_Compiler_5.06u7.exe或者ARMCompiler5_5.06u7_Setup.exe。版本号方面,5.06u7是最后一个稳定版本,也是兼容性最好的一个,建议直接用这个版本。
如果你手头有旧版MDK 5.36的完整安装包,也可以从里面提取。具体做法是把安装包解压(用7-Zip右键解压,不要直接运行),在解压出来的目录里找到ARM\ARMCC文件夹,这个就是AC5的完整目录,直接复制出来备用。
提示:不要从任何第三方"资源站"下载所谓的"AC5绿色版"或"破解版编译器",这类文件极大概率被篡改过,编译出来的固件可能存在难以排查的随机问题。
3. 手把手把AC5装进MDK并让工程用起来
3.1 独立安装包的安装路径选择
假设你已经拿到了ARM_Compiler_5.06u7.exe这个安装包,双击运行。安装向导会问你装到哪个目录,这一步非常关键。
默认路径通常是C:\Program Files (x86)\ARM_Compiler_5.06u7,但我建议你改成MDK安装目录下的ARM文件夹里。比如你的MDK装在D:\Keil_v5,那就把AC5装到D:\Keil_v5\ARM\ARM_Compiler_5.06u7。
为什么要这样?因为MDK在查找编译器时,会优先扫描自己安装目录下的ARM文件夹。你装到那里,MDK基本能自动识别。装到别的地方虽然也能手动指定,但多一步操作就多一个出错的可能。
安装过程很快,大概十几秒就完了。装完之后去那个目录看一眼,应该能看到bin文件夹,里面有armcc.exe、armlink.exe、armar.exe这几个核心文件。如果bin文件夹是空的,说明安装包有问题,换一个重新装。
3.2 在MDK里注册编译器路径
装完之后打开MDK,按下面的步骤操作:
- 点菜单栏
Project -> Manage -> Project Items - 在弹出的窗口里选
Folders/Extensions标签页 - 找到
ARM Compiler这一栏,点右边的...按钮 - 在弹出的对话框里,点
Add按钮,然后浏览到你刚才安装AC5的目录,选中bin文件夹的上一级目录 - 确认之后,
ARM Compiler下拉框里应该就能看到ARM Compiler 5的选项了
这里有个细节:如果你装的是5.06u7,下拉框里显示的可能就是ARM Compiler 5或者ARMCC 5.06u7,不同MDK版本显示的文字略有差异,但意思一样。
如果点了Add之后下拉框里还是没出现AC5,检查两个地方:一是你选的目录对不对,必须是包含bin文件夹的那一级;二是MDK是不是以管理员权限运行的,有时候权限不够会导致注册表写入失败。
3.3 单个工程切换编译器的正确姿势
编译器注册好了,接下来是让具体工程用上AC5。注意,这个设置是按工程保存的,不是全局的。也就是说你每个老工程都要单独设一次。
操作路径:
- 打开你的工程
- 点
Project -> Options for Target(或者直接点工具栏那个魔术棒图标) - 在
Target标签页里,找到ARM Compiler下拉框 - 从下拉框里选
Use default compiler version 5或者直接选ARM Compiler 5 - 点
OK保存
然后重新编译整个工程(点Rebuild,不要只点Build)。如果之前AC6编译产生的中间文件还在,建议先Project -> Clean Targets清一下,避免残留的.o文件干扰。
编译输出窗口里如果看到Armcc.exe相关的命令行,就说明AC5已经生效了。如果还是报Cannot find ARM Compiler 5,回到3.2节检查路径注册。
3.4 批量工程迁移的偷懒办法
如果你手头有几十个老工程要迁移,一个个点太费劲。有个取巧的办法:MDK的工程文件.uvprojx本质是个XML文件,你可以用文本编辑器批量替换。
具体做法是:先用一个工程按上面的步骤设好AC5,然后打开这个工程的.uvprojx文件,搜索<ArmCC>或者<Compiler>相关的标签,找到类似<uAC6>0</uAC6>这样的字段。这个字段的值如果是0表示用AC5,是1表示用AC6。你把这个字段的值批量替换到其他工程文件里就行。
不过这个操作有风险,改之前务必备份。而且不同MDK版本生成的.uvprojx结构可能有差异,替换之前先对比两个文件的差异,确认字段名一致再动手。
4. 装完之后那些让人抓狂的报错怎么破
4.1 "Cannot find ARM Compiler 5"反复出现
这个报错最常见,原因通常有三个:
第一种情况是路径注册了但MDK没刷新。解决办法是关掉MDK再重新打开,有时候MDK的配置缓存需要重启才能生效。
第二种情况是安装目录里有中文或空格。AC5的某些组件对路径中的非ASCII字符处理有问题,如果你的MDK装在D:\软件\Keil_v5这种路径下,建议换个纯英文路径重装。空格倒还好,但中文路径是绝对不行的。
第三种情况是注册表残留。如果你之前装过AC5又卸载了,注册表里可能还有旧路径的记录,MDK读到了错误的路径。解决办法是打开注册表编辑器,定位到HKEY_CURRENT_USER\Software\Keil\...下面跟ARM Compiler相关的项,把旧的删掉,然后在MDK里重新添加。
4.2 编译通过但链接报错"L6915E"
这个报错的全称是L6915E: Library reports error: __use_no_semihosting was requested, but _ttywrch was referenced。听起来很吓人,其实原因很简单:你用了AC5,但工程里某个地方引用了半主机模式的库函数,而你又没正确配置。
解决办法是在你的main.c或者专门的retarget.c里加上这段代码:
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 6000000) #pragma import(__use_no_semihosting) struct __FILE { int handle; }; FILE __stdout; FILE __stdin; void _sys_exit(int x) { x = x; } void _ttywrch(int ch) { ch = ch; } int fputc(int ch, FILE *f) { return ch; } #endif这段代码的作用是告诉AC5不要用半主机模式,同时把标准输入输出重定向到空实现。如果你需要串口打印,把fputc改成往串口寄存器写数据就行。
4.3 老工程里的内联汇编报错
AC5和AC6的内联汇编语法完全不同。AC5用的是:
__asm { MOV R0, #0x01 MOV R1, #0x02 ADD R2, R0, R1 }而AC6要求写成:
__asm volatile ( "MOV R0, #0x01\n" "MOV R1, #0x02\n" "ADD R2, R0, R1\n" );如果你切到AC5之后,原来在AC6下能编译的汇编代码报错了,说明你之前已经把代码改成AC6风格了。这时候要么把代码改回AC5风格,要么就继续用AC6。我的建议是,如果工程里内联汇编不多,改回AC5风格更省事,因为AC5的语法更直观,调试也方便。
4.4 编译速度突然变慢
从AC6切回AC5之后,你会发现编译速度明显下降,尤其是大工程,全编译可能要等好几分钟。这是正常的,AC5的编译效率确实不如AC6。几个缓解办法:
- 开启MDK的并行编译:
Project -> Options for Target -> Output里勾选Browse Information旁边的Parallel Build,然后设置线程数。不过AC5对并行编译的支持有限,效果不如AC6明显。 - 把不常改动的文件编译成库:比如标准库的
.c文件,编译一次生成.lib,之后工程里直接链接库文件,不用每次重新编译。 - 增量编译:只改动的文件重新编译,别动不动就
Rebuild。
5. 几个容易被忽略但很要命的细节
5.1 芯片包版本和AC5的兼容性
有些新版芯片包(比如STM32F4的2.17.0版本)里面的启动文件是用AC6语法写的,你切到AC5之后会报一堆汇编错误。解决办法是换用旧版芯片包,或者手动把启动文件里的AC6语法改成AC5能识别的。
具体来说,AC6的启动文件里会有IMPORT、EXPORT、PROC、ENDP这些伪指令,AC5也支持,但某些条件编译的写法不一样。如果你不想折腾,直接去芯片原厂官网下载旧版芯片包,比如STM32F4的2.15.0版本,那个是AC5时代的产物,兼容性最好。
5.2 优化等级设置要重新调
AC5和AC6的优化选项不是一一对应的。AC6的-O1大概相当于AC5的-O1,但AC6的-O2和AC5的-O2行为差异很大。切到AC5之后,建议把优化等级先设成-O0或者-O1,确保功能正常之后再逐步提高。
尤其是涉及volatile变量和中断服务程序的代码,AC5在高优化等级下可能会做出一些出人意料的优化。我遇到过一次,一个中断标志位在-O3下被优化掉了,导致中断响应异常,降到-O1就正常了。
5.3 调试器配置不受影响但要注意
切换编译器不影响调试器配置,J-Link、ST-Link该怎么用还怎么用。但有一点要注意:AC5生成的调试信息格式和AC6略有不同,如果你之前用AC6调试时保存了.axf文件,切到AC5之后要重新编译生成新的.axf,否则调试时变量查看和断点设置可能会错乱。
另外,如果你用的是J-Link,某些老版本的J-Link驱动对AC5生成的调试信息支持更好,反而对AC6的支持有问题。所以切到AC5之后,如果调试反而更顺畅了,别惊讶,这是正常现象。
5.4 工程文件备份的时机
在切换编译器之前,一定要备份整个工程目录。我见过太多人切完编译器发现编译不过,想切回去结果工程文件已经被改乱了,最后只能从版本控制里重新拉。
备份的时候注意,不只是备份.uvprojx文件,整个工程目录都要备份,包括.uvoptx、.uvguix这些配置文件,以及Objects、Listings这些输出目录。虽然输出目录可以重新生成,但有时候里面的.crf文件对调试信息有影响,一起备份更保险。
6. 关于AC5长期使用的现实建议
AC5毕竟是停止维护的组件,长期来看,迁移到AC6是迟早的事。但在当下,如果你的工程规模不大、交付压力紧,用AC5快速把东西做出来是更务实的选择。
我的建议是分两步走:短期内用AC5保证项目进度,同时在新项目或者不紧急的旧项目上逐步尝试AC6迁移。迁移的时候不要一次性全改,先在一个分支上把编译错误一个个解决,跑通之后再合并回主线。
另外,如果你用的是GD32或者国产芯片,很多原厂提供的例程至今还是AC5工程。这种情况下,装AC5几乎是唯一的选择,因为原厂不会为了你一个人去更新例程。我装AC5最频繁的场景就是处理GD32的工程,每次换电脑都要重新装一遍,流程已经熟得不能再熟了。
还有一个细节:AC5的许可证和MDK的许可证是分开的。MDK的社区版许可证不覆盖AC5,你需要有MDK的专业版许可证才能合法使用AC5。如果你用的是评估版,编译出来的代码有32KB的限制。这一点在商业项目里要特别注意,别到时候产品都量产了才发现许可证有问题。
最后说一个我自己的习惯:每次装完AC5,我会在MDK的Pack Installer里把常用的芯片包也一并更新到兼容AC5的版本,然后把整个D:\Keil_v5目录打包成一个压缩包存到移动硬盘里。下次换电脑或者重装系统,直接解压就能用,省得重新走一遍安装流程。这个习惯帮我省了至少几十个小时的重复劳动,推荐你也试试。