用Keil MDK做嵌入式开发的老手,最近应该都有同一个感受:升级到5.37之后,AC5(ARMCC v5)编译器好像“凭空消失”了。打开以前的STM32、GD32、NXP老工程,一编译就是满屏的Toolchain not installed或者compiler not found,折腾半天才明白,原来从5.37开始,Keil官方默认只装AC6(armclang),而不把AC5(armcc)一起打包了。这篇文章就把5.37及之后版本里,AC5编译器最干净的安装方法、离线包选择、常见报错和迁移老工程时的注意事项一次讲清楚,给还在维护老项目、或者暂时不想折腾AC6的朋友一条能直接照抄的路。
1. 为什么5.37之后AC5不再默认安装
1.1 官方到底改了什么
Keil MDK从5.37版本开始,安装包结构出现了明显变化。过去我们装完MDK,打开安装目录,ARM\ARMCC\bin下面能直接看到armcc.exe、armasm.exe、armlink.exe这一套完整的AC5工具链。但5.37之后,装完默认只有ARM\ARMCLANG\bin,也就是AC6编译器(armclang)。MDK的安装器也特别“贴心”,如果你安装了旧版本的AC5,升级到5.37时它还会提示AC5已不再作为默认组件,让你手动处理。
有些新人到这里就开始慌,以为是安装过程出了问题,卸载重装好几次还是找不到AC5。其实这不是你操作的问题,这是Keil官方主动做的“断舍离”。ARM编译器v5.06 update 7(build 960)是AC5的最后一个版本,官方早已停止更新,后续的维护精力全部放在了AC6上。所以你从5.37开始找不到AC5,不是安装坏了,是它根本就没给你装。
1.2 哪些场合还离不开AC5
既然AC6是官方主推的编译器,为什么还要专门写一篇AC5的安装教程?因为现实项目里,AC5是“钉子户”一样的存在。
第一类,老工程不兼容。很多芯片厂家的标准外设库、早期SDK、老版本RTOS例程,都是基于AC5的语法和编译行为开发出来的。像STM32标准外设库(StdPeriph)、早期LPC17xx/LPC18xx的驱动,直接拿到AC6下编译,经常因为语法差异报一堆错,比如结构体初始化方式、编译器内建关键字、汇编文件格式等等。维护这种老工程,最稳的办法就是继续保持AC5编译环境,而不是花一两天时间去改代码。
第二类,部分国产芯片和中小厂商的SDK。不少国产MCU厂商的库函数代码,内部用了很多AC5风格的关键字,比如__asm、__forceinline、__align、__packed这些。虽然AC6也支持大部分,但遇到个别冷门关键字或内联汇编写法,编译直接报错。遇到这种情况,与其改库,不如给工程配上AC5,一分钟解决。
第三类,封装好的中间件。有些第三方闭源库只提供AC5编译好的静态库(.lib),没有源码,这种情况下你只能用AC5去链接。没了AC5,整个工程就跑不起来。
所以AC5不是“淘汰的思路”,而是很多存量项目和特定开发场景下的刚需。这也是为什么5.37之后,手动安装AC5成为一门必会的操作。
2. 安装AC5之前要准备什么
2.1 确认当前MDK版本和架构
在安装AC5之前,先确认你的Keil MDK版本。我这里的操作示例基于5.37、5.38、5.39这几个版本,整体流程一样,但个别细节(比如Pack Installer的界面文案)略有差异。
打开Keil MDK,点击菜单栏的Help -> About uVision,可以看到版本号。重点关注版本号是不是5.37及以上,如果是5.36及以下,那AC5一般默认就在,或者安装包直接包含,不太需要这篇文章。
还要注意操作系统位数。MDK安装路径默认是C:\Keil_v5,如果你的系统是64位,安装目录没什么特别要求;但如果你用了绿色版、精简版或者其他第三方打包的MDK,AC5安装路径可能被改掉,后面配置路径时要格外小心。
在动手之前,我强烈建议先关掉杀毒软件,至少把Keil安装目录加入白名单。AC5安装程序会在C:\Keil_v5\ARM\ARMCC底下注册很多可执行文件和动态库,某些杀毒软件会把armcc相关的DLL当成风险项拦掉,导致装完之后编译器不可用。这个坑我踩过一次,装完没有任何报错,但一编译就说找不到编译器,最后发现是被杀毒软件“隔离”了。
2.2 找到正确的AC5安装包
AC5编译器分两种方式获取:一种是通过MDK自带的Pack Installer联网下载安装,另一种是下载离线安装包手动装。两种方式我都会在下一节详细讲,这里先说清楚离线包的事情。
Keil官方的AC5最终版本是ARM Compiler 5.06 update 7 (build 960),这个版本号非常重要。网上有些老教程让你去下载5.06 update 5、5.06 update 6,这些旧版本虽然也能用,但既然有update 7,就一定要用这个最终版。它修复了update 6之前的一些已知问题,生成的代码稳定性更好。
离线包一般是一个压缩包或者可执行文件,文件名大概是ARM_Compiler_5.06_update_7.zip或者DS0000_ARM_Compiler_5.06_update_7.zip。这个包在Keil官网的“Legacy Product Downloads”页面可以找到,搜索的时候直接用“ARM Compiler 5.06 update 7 (build 960)”这个完整关键词。下载完之后,放在一个干净目录里,比如D:\tools\AC5,全程不要解压安装时把目录搞乱。
顺便提醒一句,网上有些“搬运版”AC5安装包,压缩包里的文件被改过,安装完之后可能带后门或者静默装其他软件。我一般只认准两个来源:Keil官网下载链接,以及芯片厂商官方技术社区里挂载的官方转存链接。从其他渠道下载的包,先比对一下文件大小,官网的包大小一般都在几百MB级别,如果下载下来一看只有几十MB,那基本可以删了。
3. AC5编译器的详细安装步骤
3.1 通过Pack Installer自动安装
这是最稳、最省事的方式,前提是网络状况正常,能访问Keil的下载服务。这个方法不需要你去官网找链接,也不用纠结解压到哪,几步就能完成。
第一步,打开Keil MDK的Pack Installer。在uVision界面里,菜单栏点Pack Installer图标(一个绿色方块图标),或者通过Tools -> Pack Installer打开。
第二步,在Pack Installer窗口里,找到左侧工具栏或者菜单里的Tools -> Manual Install ARM Compiler。这个选项的准确名字在不同的MDK版本里略有差别,有的叫Manage ARM Compiler,有的直接叫Manual Install ARM Compiler,但位置都在Pack Installer的菜单栏里。
第三步,点击之后会弹出一个对话框,里面列出了当前可安装的ARM编译器版本。选择ARM Compiler 5.06 update 7 (build 960),如果列表里没有,可能是你的MDK版本较新,界面上会有“Download”按钮,先点击下载,下载完成后会自动弹出安装程序。
第四步,安装程序会询问安装目录。这里我建议直接保持默认,也就是MDK的安装目录,比如C:\Keil_v5。安装完成后,AC5会被装到C:\Keil_v5\ARM\ARMCC,这是Keil默认识别路径,后面配置工程时省很多事。
在联网安装过程中,我发现一个规律:如果你公司网络对境外下载不友好,这一步很容易卡在“Downloading”然后超时。这时候不要反复重试,直接改用下一节的离线包方案,反而节省时间。
3.2 离线包手动安装与路径处理
离线包是很多开发者的救星,尤其当你帮同事装环境、或者给不能联网的客户部署开发环境时,这个方法最靠谱。
拿到离线包后,先解压或者直接双击运行。如果是一个zip压缩包,解压后里面会有类似install_armcc.bat或者setup.exe的安装脚本;如果是.exe文件,直接双击运行即可。运行方式有讲究,建议“右键 -> 以管理员身份运行”,因为AC5要写入MDK安装目录,普通权限下可能提示无权写入Program Files下的Keil_v5目录。
安装程序跑完之后,打开MDK的安装目录,重点确认目录结构。正常情况下,你的MDK根目录下应该同时存在ARM\ARMCC和ARM\ARMCLANG两个文件夹,分别对应AC5和AC6。如果ARMCC文件夹没生成,说明安装过程出了问题,可能是指定的安装目录不对。这时候手动把ARMCC文件夹放到C:\Keil_v5\ARM下,然后去环境变量相关配置里看看路径有没有被识别。不过手动挪目录属于“险招”,能不用就不用,尽量通过安装程序正常装到默认位置。
还有一个离线安装的常见场景:我手头有个5.06 update 7的安装包,但同事机器上的MDK主程序改过安装路径,装在了D:\Keil_v5_Pro。这种情况下,安装程序也能识别,但有个技巧,一定要在第一步选择“Customize”并手动指定到D:\Keil_v5_Pro\ARM,而不是让安装程序默认往C:\Keil_v5里塞。装完之后,打开MDK,在工程配置里手动添加一下编译器路径即可,下面会说。
3.3 安装后的确认与工程切换
装完AC5,怎么确认它真的被MDK识别了?这一步不能省,否则后面编译老工程很可能依然报错。
先打开一个工程,或者新建一个空工程,然后看左侧Project窗口,右键点击目标,选择Options for Target,快捷键是Alt+F7。在弹出的对话框里,切到Target标签页,最上方有一个ARM Compiler的选项框。
点开这个下拉框,正常情况下会看到一个类似Version 5.06 update 7 (build 960)的选项。选中它,然后点击OK。这时候再编译,如果AC5被正确识别,工程会用armcc来编译,而不是armclang。
但这里有个特别容易迷惑的地方。如果你的下拉框里只能看到Use default compiler version 6,看不到AC5选项,并不代表AC5没装成功,而是MDK的编译器注册列表里没有添加这个版本。解决办法是:在同样的Options for Target对话框里,点一下ARM Compiler下拉框旁边的“三个点”按钮(或者右键工程 ->Manage Project Items -> Folders/Extensions),在弹出来的ARM Compiler管理界面里选择Add another ARM Compiler,然后浏览到C:\Keil_v5\ARM\ARMCC\bin,选中armcc.exe或者直接选到ARMCC目录。添加成功后,这个版本就会出现在候选列表中。
同一台机器上,不同工程可以分别选择AC5和AC6,MDK是支持的,互不影响。这在实际项目中特别实用,比如公司有两套代码库,一套老一套新,都放在同一台电脑上,只要在每个工程里手动指定一下编译器版本就行。
4. 安装和使用AC5时常见的坑及排查方法
4.1 装完了还是提示“Version 5.06 update 7 (build 960)未安装”
这是我在社区里看见出现频率最高的报错,也是标题热搜里被问烂的那句话。出现这个报错,十有八九是AC5的安装路径和MDK的搜索路径对不上。
排查分三步。第一步,确认C:\Keil_v5\ARM\ARMCC\bin下有没有armcc.exe。如果没有,说明AC5根本没装上,回去重新安装。第二步,打开Options for Target -> Target,看ARM Compiler下拉框里有没有出现AC5选项,如果没有,用前文提到的“Add another ARM Compiler”手动添加。第三步,添加后还有报错,检查MDK版本。有些5.37的精简版、破解版可能阉割了编译器注册功能,这时候只能重装一次完整官方版本。
还有一个小概率情况:Windows系统环境变量里的PATH被某些软件改过,导致MDK找不到armcc的路径。这时候可以在系统环境变量里手动加一条C:\Keil_v5\ARM\ARMCC\bin,然后重启MDK,问题一般能解决。
4.2 编译时报告找不到头文件:core_cm3.h、stm32f10x.h等
这个问题看着像AC5没装好,其实往往是CMSIS和Device Family Pack(DFP)的版本问题。AC5本身只带编译器和标准库,芯片相关的头文件、启动文件来自两个地方:一是芯片厂商的PACK包,二是Keil自带的CMSIS库。
升级MDK之后,工程里的CMSIS路径可能因为版本变化被重置了,或者PACK包的版本和你工程的旧代码不匹配。解决办法:打开Options for Target -> C/C++标签,检查Include Paths是否还指向老路径。再打开Pack Installer,确认你需要的芯片PACK包(比如Keil::STM32F1xx_DFP)已经安装,并且版本号不要选太新。有些老工程在某些新版本DFP下头文件反而找不到,退回旧版DFP(比如1.1.0)能立刻解决问题。
这里分享一个经验:老工程尽量不要盲目升级DFP。芯片PACK包和IDE编译器是独立更新的,但新PACK包里的启动文件可能默认用AC6风格编写,老工程配AC5编译反而出问题。遇到底层头文件、启动文件报错的,先尝试把DFP版本回退到你项目当初开发时对应的版本,而不是追新。
4.3 编译不报错但链接时报一堆“Undefined symbol”
这个问题更隐蔽。AC5编译器和AC6编译器的宏定义规则有些差异。比如,老工程里可能用到了__CC_ARM、__GNUC__这类编译器识别宏,或者某个库函数的条件编译指令靠这些宏来开关。当编译器从AC5换成AC6,这些宏可能失效,导致某些配置项被跳过,最终链接时找不到对应的函数实现。
表现就是:编译阶段一路过,到了链接阶段突然冒出一堆Undefined symbol,比如SystemInit、HAL_UART_Init之类的。
排查思路:先在工程里全局搜索__CC_ARM,这个宏是AC5特有的,如果代码或启动文件里有它,说明这部分逻辑是为AC5写的。如果编译器切到AC6,__CC_ARM不定义,相关代码就不参与编译。解决方法是在Options for Target -> C/C++ -> Define里加上__CC_ARM=1,或者把条件编译改成兼容AC6的写法。反之亦然,当你用AC5时,如果某些代码依赖__CLANG_ARM宏,也需要手动添加。
4.4 报错“编译器未包含main类型”
看到“编译器未包含main类型”这句话时,很多人第一反应是编译器坏了,其实大概率是工程配置里选错了编译器选项,或者编译器路径指向了一个不存在的编译工具。这个报错一般出在Options for Target里选择了某一个“编译器版本”,但那个版本的安装路径指向了一个不含armcc的目录,比如你把ARM Compiler路径指到了ARMCLANG的bin目录,它当然没有“main”编译能力。解决办法很简单,把ARM Compiler路径重新指到ARMCC\bin,然后确认下拉框里选中的是AC5而不是AC6。
还有一种情况,这个报错出现在第三方IDE(比如VSCode的嵌入式插件)里,本质是工具链配置指向错误。如果你不在MDK里用,而是用VSCode做开发,那要检查c_cpp_properties.json或者tasks.json里的compilerPath,确保它指向armcc.exe的绝对路径。
5. AC5和AC6共存,以及老工程维护建议
5.1 共存配置注意事项
AC5和AC6共存是完全可以的,MDK从5.37开始就是按这个思路设计的。两者安装在不同目录,编译时互不干扰。
但要注意,同一个工程如果来回切换AC5和AC6,编译产物可能混在一起。我的习惯是给每个工程单独配置Output目录,在Options for Target -> Output里把Objects、Listings文件夹区分开,比如Objects_AC5和Objects_AC6。这样切编译器的时候不会把中间文件、链接映射文件搞成一团,排查问题也方便。
另外,使用AC5编译老工程时,我强烈建议在Options for Target -> C/C++ -> One ELF Section per Function这个选项上保持默认“off”,因为有些老代码依赖跨模块的弱符号和垃圾回收机制,开了一不小心链接出来就是几百KB的差量,没必要在某些资源不紧张的场景下折腾。
还有其他细节:如果你同时安装了几个版本的AC5,比如开发机上是5.06 update 6,客户机上装了update 7,那MDK的配置里要保证工程文件里的编译器版本号和实际安装的一致。工程文件(.uvprojx)里的<ArmAdsMisc>节点会记录编译器版本信息,但一般不用手动改,MDK打开工程时会自动匹配。
5.2 给还在维护老工程的开发者几点心得
说句掏心窝子的话,AC5最终版5.06 update 7已经非常成熟,稳定性极好,配合老库跑十几年的工程完全没问题。我这边好几个量产项目还在用AC5,只要芯片型号没变、编译器版本锁定,真的没必要为了“升级”而去升级。
但在新项目上,我建议你谨慎使用AC5。AC6这些年也越来越成熟,支持C11、C++11,还有更多的优化选项,代码紧凑程度比AC5强不少,新工程的竞争力肯定更高。说白了,AC5现在的定位就是“兼容存量”,而不是“面向未来”。
如果你计划在两三年内把老工程迁移到AC6,这里给一个排序建议:先把工程代码里的编译器和库版本固定下来,逐个模块对照__CC_ARM条件编译进行修改,先让整个工程在最低优化级别下通过AC6编译,再逐步提升优化等级。迁移过程中,AC5和AC6共存可以很好地兜底,哪个模块出问题就临时切回AC5编译,不至于让整个开发中断。
我个人在实际使用中感受最深的,反而是“固定版本”这四个字。做嵌入式开发的人都知道,一次“看似无关紧要”的升级,可能在半年后某个量产问题排查时给你挖一个天坑。AC5时代,之所以很多老工程师能闭着眼睛改代码,就是因为编译器行为足够稳定。安装AC5不是目的,守住工程的可复现性才是关键。所以,无论你是新装MDK 5.37、5.38,还是正在帮客户搭开发环境,把AC5装好并锁死版本,老项目就不会因为“编译器不对”而寸步难行。