news 2026/9/28 4:51:41

Keil MDK 5.37恢复ARMCC V5攻略:解决老工程编译报错与AC5/AC6切换问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Keil MDK 5.37恢复ARMCC V5攻略:解决老工程编译报错与AC5/AC6切换问题

升级完Keil MDK 5.37,打开老工程一编译,满屏红色报错,第一反应是代码出问题了,结果折腾半天发现根子根本不在代码上——是5.37默认把编译器换成了ARM Compiler 6(AC6),而你手里的老项目还是按ARMCC V5(AC5)的语法写的。这个坑我踩过,身边不少同事也踩过,所以在网上找了半天资料、试了好几遍,总算把AC5完整地加回了Keil里。这篇就把整个来龙去脉、操作步骤、以及切换后的隐藏坑一起讲清楚,给正被编译报错折磨的人一份可以照着做的方案。

先说明一点,这篇针对的是已经装了MDK 5.37、又必须继续用ARMCC V5编译老工程的情况。如果你是新项目,没历史包袱,我其实更建议直接用AC6,没必要折腾,但老项目迁移成本高、第三方库还依赖AC5的时候,手动加回V5就是唯一省事的路。

1. 升级5.37后第一波报错:罪魁祸首是编译器被“调包”了

1.1 报错表面现象和真实原因

最典型的报错场景是:打开之前的工程,点Build,然后出现一批让人摸不着头脑的红色信息。常见的几种表现:

  • 编译过程一闪而过,然后报*** Target Build Failed,但看不出具体是哪个文件出错。
  • 大量语法层面的报错,比如expected ';'、unknown type name、use of undeclared identifier,而且报错位置全是以前根本不会出错的老代码。
  • 链接阶段报undefined symbol,但明明函数定义了,声明、包含关系都对。
  • 更迷惑的是,有时候会报命令找不到,比如标题热词里出现的*** error: createprocess failed, command: 'c:\keil_v5\arm\armcc\bin\fromelf...——这是编译器路径根本无法执行。

第一种、第二种和第四种看着像代码问题、路径问题,真实根源却是同一个:MDK 5.37把默认编译器切到了AC6,而你工程里的编译选项、语法细节还是AC5时代的。AC6的前端是Clang,对代码的解析规则、关键字支持、内联汇编语法,和AC5差别非常大。老代码在AC5下“合法”的写法,拿到AC6下可能直接就成了编译错误。

1.2 为什么5.37开始默认不带AC5了

ARM从MDK 5.36开始就把AC5从默认安装里拿掉了,5.37更是彻底。原因有两个层面。

一是ARM的战略重心早就转向了AC6。AC6基于LLVM架构,由ARM和LLVM社区共同推进,新架构支持、新特性、性能优化都往AC6上倾斜,而AC5处于只维护不更新的状态。ARM官方的支持周期早就进入倒计时,在5.37里,AC5不再是“预装组件”,而是变成了“可选/遗留组件”。

二是Keil的组件管理器(Pack Installer)里虽然还能看到ARM Compiler 5的条目,但它不再随MDK主程序一起安装。很多从老版本升级上来的用户没意识到这点,装完5.37直接打开工程就编译,自然报错。这属于典型的“升级容易降级难”问题。

1.3 怎么快速确认当前用的是AC5还是AC6

不用等报错再去猜,动手编译前可以先看一眼编译器设置。

在Keil里打开目标工程,进入Options for Target(快捷键 Alt+F7),切到Target标签页。看到Arm Compiler这一栏,如果下拉框显示的是Use default compiler version 6之类,说明当前用的是AC6;如果显示V5.06 update 7 (build 960),说明是AC5。另外在编译输出窗口第一行也会打出一段信息,类似*** Using Compiler 'V5.06 update 7'或*** Using Compiler 'V6.xx',这个最直接。

我遇到的情况是,5.37装完后Arm Compiler下拉列表里根本没有V5可选,只有V6。换句话说,不是切换的问题,是AC5压根没装进系统里。这就引出了下一步:怎么把AC5弄回来。

2. ARMCC V5与V6的差异到底差在哪,为什么老项目离不开AC5

2.1 编译器本质差异:armcc和armclang

AC5的编译器命令是armcc,在Keil安装目录下对应ARM/ARMCC/bin;AC6的编译命令是armclang,对应ARM/ARMCLANG/bin。这俩不仅是名字不同,内部架构完全不同。

armcc是ARM传统的闭源编译器,几十年沉淀,针对ARM芯片做了极端优化,很多老工程、老芯片的启动代码、寄存器定义、内联汇编都围绕它编写。armclang则是基于LLVM的Clang前端,和GCC、Clang生态同源,语法解析更加标准化,但也有自己的脾气,对未定义行为、隐式声明等问题的容忍度低很多。

这里打一个不是特别精确但容易理解的比方:AC5像是一位熟悉老厂房的老师傅,你用以前约定俗成的暗号指挥他,他心领神会;AC6像是一位只认国家标准的年轻工程师,你不按规范跟他说话,他就“拒绝执行”。代码注释、宏定义、#pragma、内联汇编这些,在老代码里经常带着“暗号”色彩,AC6不吃这套。

2.2 最常见的编译不兼容点

具体到代码层面,AC5迁移AC6的典型报错集中在这么几类:

  • 关键字差异。AC5支持__irq、__asm、__forceinline、__packed、__align这类编译器专属关键字,AC6不一定完整兼容,或者需要改成__attribute__((interrupt("IRQ")))、__attribute__((always_inline))这类GNU风格写法。
  • 内联汇编。AC5使用__asm配合ARM/Thumb汇编语法,AC6使用__asm volatile配合GNU汇编语法。寄存器约束、操作数写法几乎完全不同,老代码里大段内联汇编基本等于重写。
  • #pragma处理。AC5里常用的#pragma pack(push, 1)、#pragma anon_unions,AC6有些要改成#pragma pack(push, 1)(这个支持),有些则直接不支持,比如anon_unions就得移除或改写法。
  • C标准支持程度。AC5默认偏向C90/自定义扩展,AC6默认更靠近C11/ GNU扩展。以前“不报错就算了”的隐式函数声明、旧式函数定义,AC6可能直接报warning甚至error。
  • 编译选项和优化选项。AC5的--cpu、--apcs、-Ospace、-Otime和AC6的-mcpu、-mfloat-abi、-Oz、-Os完全是两套命名。工程文件里保存的编译器选项无法直接映射,切换编译器后Keil会丢弃或忽略一部分选项,间接引发不可预期的问题。

2.3 为什么老项目短期“离不开”AC5

既然AC6这么“正规”,那大家都应该迁移过去才对,为什么非要折腾AC5?真实情况是:

  • 第三方的闭源库、老版本协议栈可能直接用AC5编译方式分发,没有源码,只能继续用AC5才能链接。
  • 项目启动代码、startup_xxx.s汇编文件可能是AC5风格的,AC6环境下汇编器(armasm vs armclang内置汇编器)语法兼容不完整。
  • 老工程师多年的代码习惯、底层驱动都揉在AC5语法里,迁移成本不是一两天能消化的。
  • 芯片厂商的不少老例程(比如一些工业级MCU的原厂库)只验证过AC5编译,升级到AC6后出问题,厂商也没有精力更新。
  • 项目的编译脚本、CI流程可能还直接调用armcc,全部改造成AC6需要连带改流水线。

所以,“加回AC5”不是逆向操作,而是嵌入式开发里非常现实的兼容性诉求。这也是这篇文章存在的意义。

3. 手把手把ARMCC V5请回Keil:从下载到切换的完整路径

3.1 资源获取:官方和本地两种合规途径

先说资源来源。ARMCC V5现在不会自动出现在MDK 5.37里,但获取途径是合法、官方的,有两种常见方式。

方式一:从Keil官网的历史版本(Previous Versions)页面下载旧版MDK安装包。

Keil官网的Downloads页面往下翻,有一块“Previous Versions”或者叫“Legacy versions”的区域,可以下到更早的MDK安装文件,比如MDK 5.36、5.35、5.34等。这些旧版本的安装包是完整自包含的,里面带AC5编译器。下载安装后,在你的Keil安装目录(比如C:\Keil_v5\ARM\ARMCC)下就能找到完整的 ARM Compiler 5。

方式二:从同事/另一台机器的旧版MDK安装目录里直接拷贝ARMCC文件夹。

ARMCC V5是独立目录,直接拷贝到本机也能用。具体来说,源机器上C:\Keil_v5\ARM下的ARMCC整个文件夹,复制到本机C:\Keil_v5\ARM\ARMCC,理论上是可行的。我实际验证过,只要能保证目录完整、版本一致(推荐5.06 update 7),Keil能正常识别。

注意:不要在网上下载来路不明的所谓“绿色版ARMCC”,里面文件不完整、被第三方改动过,容易踩各种诡异报错。官方历史版本安装包或者可信同事的完整拷贝,才是可靠路径。

网上流传最广的ARMCC V5最终版本是5.06 update 7 (build 960),这也是AC5的最后一个更新版本。我用的是这个版本,稳。

3.2 目录结构和安装位置说明

拿到ARMCC文件夹后,要先理解Keil的目录结构,避免放错位置。

Keil MDK的默认安装路径一般是C:\Keil_v5。在ARM目录下,并列放着多个工具链目录。5.37安装后常见的有:

C:\Keil_v5\ARM\ARMCLANG\ # AC6编译器 C:\Keil_v5\ARM\PACK\ # 芯片支持包 C:\Keil_v5\ARM\CMSIS\ # CMSIS相关文件 C:\Keil_v5\ARM\Flash\ # 下载算法文件

而AC5对应的目录是C:\Keil_v5\ARM\ARMCC。如果这个文件夹不存在,Keil的编译器下拉列表里自然没有V5选项。把完整的ARMCC文件夹放到这里,是让Keil识别到AC5的前提。

3.3 具体操作步骤

整理一份可以照着做的完整流程:

  1. 备份当前工程。改编译器之前,用Git或者复制一份工程文件夹,避免操作失误导致工程文件损坏。这个动作建议养成习惯,编译环境的调整比改代码更容易引入隐蔽问题。

  2. 获取ARMCC V5目录。按照3.1里的方式拿到ARMCC文件夹,最好先确认里面有bin、include、lib这三个子目录,缺少任何一个都不能正常工作。bin里有armcc.exe、fromelf.exe、armasm.exe等核心程序。

  3. 放置目录。把ARMCC文件夹放到C:\Keil_v5\ARM\下。如果Keil装在其他盘,找到对应位置,比如D:\Keil_v5\ARM\ARMCC。

  4. 重启Keil。关闭所有工程后重新打开MDK。Keil在启动时会扫描ARM目录下的编译器,识别到ARMCC目录后,Options for Target -> Target -> Arm Compiler下拉框里就会出现V5.06 update 7 (build 960)之类的选项。

  5. 切换编译器。打开工程,进入Options for Target -> Target标签页,把Arm Compiler从V6下拉项切换成V5。如果你的工程里某些子目标配置不同,要逐个配置项检查,不能只改主目标。

  6. 重新编译。先Rebuild(重新编译全部),观察输出信息。如果顺利,会看到编译器信息变成*** Using Compiler 'V5.06 update 7',同时不再有AC6那批语法报错。

还有一种情况:下拉框里已经有V5,但默认是V6,你只需要切换即可,不需要重新放置目录。这种情况多出现在旧版MDK升级到5.37但保留下来的旧工程配置里。

3.4 切换后立即遇到 “createprocess failed” 怎么处理

这一步专门说一下createprocess failed。很多人把ARMCC目录放好后,切换V5,编译时还是报类似这样的错:

*** error: createprocess failed, command: 'c:\keil_v5\arm\armcc\bin\fromelf.exe --text -c --output=... '

出现这个错有几个可能:

  • fromelf.exe确实不在ARMCC的bin目录下——检查目录完整性。
  • Keil UV4的路径配置还残留旧设置,导致调用了错误路径下的fromelf。可以进入Options for Target -> User标签页,查看After Build/Rebuild里的用户命令,看是否手写了绝对路径,包含旧路径的就改掉。
  • 文件路径里有中文或空格(尤其是路径中带有中文用户名),Keil对这类路径的解析不稳定。直接把Keil安装到纯英文路径下最省心。
  • 杀毒软件拦截了fromelf.exe执行。个别安全软件会把Keil的fromelf当可疑程序处理,在杀毒软件里把C:\Keil_v5\ARM\ARMCC\bin和C:\Keil_v5\ARM\ARMCLANG\bin加入白名单。

实际排查时可以先用命令行手动执行一遍报错里的命令,看是命令本身有问题、路径找不到,还是程序被杀毒拦截。手动能执行,再回Keil里编译,基本就能定位到是Keil侧还是系统侧的问题。

4. 编译器版本切换成功,不代表编译一定通过:旧项目常见兼容坑

4.1 成功切换后仍可能遇到的编译错误清单一览

切换回AC5后,AC6那批语法报错消失了,但有些工程还会遇到属于AC5时代特有问题的报错。我整理了几个高频场景。

场景一:工程文件里残留AC6选项

如果工程是在5.37下用AC6配置过,又切回AC5,Keil可能会把部分AC6专属的编译选项原样传给armcc,armcc不认,就会报类似:

Error: L3900U: Unrecognized option '--target=arm-arm-none-eabi'

解决办法是清理工程选项。选中工程,在Options for Target -> C/C++ (AC5)标签页里,把Misc Controls文本框里AC6相关的参数删掉;同理在Asm标签页、Linker标签页也检查一遍。如果手动清理麻烦,可以新建一个空的AC5工程,把源码添加进去,重新配置,比在旧工程上修补更干净。

场景二:分散加载文件(sct)不兼容

AC6的链接器是armlink,AC5的链接器也是armlink,但分散加载文件的语法细节有差异,尤其是使用#! armcc注释头或依赖编译器预处理的写法。此外,老工程里如果写的是AC5风格的scatter文件,比如LR_IROM1 0x08000000 0x00100000 {},切换到AC5时反而会恢复正常;但如果是从AC6工程转回来的,注意AC6的armlink接受更严格的段描述语法,AC5可能无法处理某些IF、FOR预处理指令外的写法。

场景三:CMSIS/芯片头文件版本不匹配

Keil 5.37的Pack Manager会把CMSIS、Device相关文件更新到较新版本,新版本CMSIS头文件里的某些定义、宏可能针对AC6优化,放在AC5下反而编译不过。报错往往在core_cm4.h、system_stm32f10x.c这些官方文件里。

最有效的处理方式是:把工程使用的Device Pack降级到和工程时代匹配的版本。比如STM32F103工程,以前用Keil::STM32F1xx_DFP@2.3.0编译正常,装5.37后被更新到更新版本,就在Pack Installer里把版本降回去。具体操作是Pack Installer左侧找到Device,右侧Versions区域选择旧版本,点Install。

4.2 从AC6改回AC5后,建议顺手做的三件事

即使编译通过了,我也建议做下面三件事,能少走很多弯路。

第一件:关闭AC6新增的优化选项残留。

在Options for Target -> C/C++ (AC5)标签页的Optimization下拉框,确认是你要的等级。AC5的-O0、-O2和AC6的-O0、-O2语义并不完全相同,最好到工程里实际验证运行效果,尤其是涉及时间关键代码的时候。

第二件:统一团队成员的Keil版本。

如果你把工程交给同事,他用的Keil版本、Pack版本和你不一致,很可能在他那里编译报错。建议在工程根目录写一个README或者在版本管理里注明:Keil版本、ARMCC版本、Device Pack版本、是否启用了AC5。这看起来是小事,但团队协作里节省的时间非常多。

第三件:把工程用到的库文件路径确认一遍。

AC5的库文件搜索路径是ARMCC/lib,AC6是ARMCLANG/lib。如果你之前手动添加过某个.a、.lib路径,里面写的是ARMCLANG路径,切回AC5后链接器会报找不到库。检查Options for Target -> Linker -> Misc controls和Libraries里的内容,确保是ARMCC路径。

4.3 验证编译结果的几个关键点

编译通过不等于万事大吉,建议至少验证以下三点:

  • 编译日志里确认编译器路径。打开Build Output窗口,最顶部的信息会写明用的是哪个编译器。确认是V5.06 update 7而不是其他。
  • 查看MAP文件里的段布局。链接成功后打开.map文件,看一下ROM、RAM占用是否符合预期。AC5和AC6对代码段、数据段的默认对齐方式有差异,RAM占用如果比原来大或者小,都要留个心眼。
  • 下载后跑一遍基础功能。别只烧进去看个现象,重点跑一遍中断、外设初始化、通信协议模块。编译器版本不同,代码行为可能有微妙变化,尤其是未初始化变量、隐含的字节对齐问题。

5. ARMCC V5和V6的选项对照,以及什么时候该认真考虑迁移

5.1 常用选项对照表

实际操作中,经常要在AC5和AC6之间转换选项,整理一张常用对照表,方便参考。

功能描述ARMCC V5 (AC5)ARM Compiler 6 (AC6)
CPU类型指定--cpu=Cortex-M4-mcpu=cortex-m4
浮点ABI--fpu=FPU5/--fpu=SoftVFP-mfloat-abi=hard -mfpu=fpv5-sp-d16
优化等级(时间优先)-Otime-O2/-O3
优化等级(空间优先)-Ospace-Os/-Oz
关闭优化-O0-O0
设置包含路径-I.-I.
定义宏-Dxxx-Dxxx
编译成只读代码--split_sections-ffunction-sections -fdata-sections
链接时去掉未用段配合--remove配合--gc-sections
生成依赖文件--depend=xxx.d-MMD -MF xxx.d
汇编器armasmarmclang内置汇编器
反汇编/格式转换工具fromelffromelf(路径不同)

AC6里的-ffunction-sections -fdata-sections和链接器--gc-sections经常配套使用,作用是删除未使用的函数和数据,减小代码体积。AC5侧的对应方案是编译时--split_sections,链接时在分散加载文件或Linker选项里开remove段处理。新手常犯的错是两个编译器选项混用,导致Keil配置面板里出现“Unrecognized option”,所以这里单列出来。

5.2 什么时候“加回AC5”是过渡方案,什么时候是长期方案

如果只是为了让老工程在MDK 5.37下继续编译,加回AC5后就能一直用,很多商业项目在生产维护期就是这么跑的。AC5虽然停止更新,但它本身已经足够稳定,在经典Cortex-M芯片上不会有“不能用”的问题。对这类项目,加回AC5就是长期方案。

但如果你维护的是新项目、新芯片、要长期迭代,那加回AC5只能算过渡。原因也简单:新芯片的Device Pack、CMSIS版本对这些新芯片的支持越来越偏向AC6,部分新特性、新外设库只验证过AC6编译。在新芯片上硬用AC5,遇到问题时可能孤立无援。

我做的一个判断标准是:看项目的芯片型号和启动文件。如果是五年前甚至十年前的芯片、老启动文件、老外设库,AC5就是最佳选择;如果芯片是近两年新出的、厂商SDK也是新出的,乘早规划AC6迁移,别把AC5当作逃避迁移的借口。

5.3 如果迟早要迁移AC6,建议的渐进路线

给未来准备迁移AC6的读者一个渐进式建议,不需要一夜之间全部改完:

  1. 先让工程在AC5下恢复可用(就是本文讲的操作),保证当前工作不受影响。
  2. 建一个分支,复制工程为AC6版本,在分支里尝试迁移,记录所有报错和修复方法。
  3. 优先迁移语法层面的兼容,比如__irq改写成__attribute__((interrupt)),内联汇编改写成GNU语法,#pragma anon_unions移除,这些是硬骨头,也是收益最大的一步。
  4. 逐个模块验证,每迁一个模块就编译运行一次,不要等全部改完再验证,否则部署问题时有可能会互相干扰。
  5. 最后更新CMSIS和Device Pack版本,确认AC6下的ARMCLANG工具链能正确编译官方文件。

迁移完成后,AC5也不是马上删除,保留在Keil的ARMCC目录里,遇到老工程维护随时切回。

6. 写在最后:关于ARMCC V5和MDK 5.37共存的一点个人经验

这篇的实操方法看着不长,实际上每一步都踩过坑。最后分享几条自己的体会:

第一,老工程升级MDK之前,先截图或者记下原工程的编译器版本、Device Pack版本、编译选项,这是你回滚和排查的第一手依据。我见过太多人升级前什么都没记录,报错后连“之前用哪个版本编译的”都说不清楚。

第二,ARMCC V5放在C:\Keil_v5\ARM\ARMCC后,最好不要动它的文件夹名字和内部结构。Keil识别编译器除了看目录,还会校验内部文件版本号,如果你改了名或者精简了文件,即便能识别也可能在某些场景下报错。

第三,遇到莫名其妙的编译问题,先把杀毒软件退掉或者加白名单,再怀疑代码。这个顺序反了会浪费大量时间。Keil本身是老牌工具,但它的编译子进程很多,杀毒软件对这类频繁调用外部程序的软件确实容易误判。

第四,如果真的要用AC5编译新工程,建议在工程配置里显式选择AC5,不要依赖“Use default compiler version”这种默认设置。显式选项能让工程在不同电脑上更可控,也方便别人接手时快速理解环境。

最后分享一个工程实践:我们团队现在维护的工业控制项目就卡在第三方协议栈上,那个协议栈只能AC5编译,所以我一直保留一台装MDK 5.36的构建机专门出正式版本,个人开发机则用5.37加本文这套AC5补全方案。两边环境隔离,长期稳定运行,新项目则逐步往AC6走。这是一个挺实用的折中策略,你可以评估一下是否适合自己团队。

希望这篇能帮你把Keil MDK 5.37加回ARMCC V5这件事一次搞定。如果你在操作过程中遇到这里没提到的报错,建议先按这个顺序排查:确认ARMCC目录完整、确认编译器下拉框已经显示V5、确认工程选项里没有残留AC6参数、确认杀毒软件没拦截,大部分问题到不了代码层面就解决了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 4:49:09

高温适配器本质是热-电-力-化四场耦合的微尺度工程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 4:47:07

编程世界的未来:程序员的职业发展前景

在这个数字化时代, 程序员扮演着非常重要的角色, 他们不光在创造应用程序、网站还有软件,他们还在推动着技术的前沿方向, 并且也在解决复杂问题, 关于程序员的职业前景, 这是让人觉得兴奋的, 是充满了各种机会和潜力的。1. 计算机编程一直以来都属于需求非常高的领域…

作者头像 李华
网站建设 2026/9/28 4:47:03

使用python,写一个自动浏览文章的脚本

它是一种可以让网页实现自动点击的工具软件, 这款工具能够同时监控页面里的内容有没有发生变化, 如果有变化的话, 它会给出来报警和提示, 这种软件主要用来搞广告点击或者是为了提高一个网页的点击率, 它能支持链接进行点击操作, 也能支持Js广告的点击操作, 还能支持Ifrem的点击…

作者头像 李华
网站建设 2026/9/28 4:46:48

Python怎么运行程序?一分钟读懂

它<, 一种被广泛使用的高级编程语言>以其易于学习和灵活性而闻名。<, 在数据科学、网络开发、自动化以及科学计算等各个不同的领域之中它都扮演着关键的重要角色。>但是, 对于许多人来讲, 在开始深入探索关于编程的世界之前, 充分了解如何正确运行程序显得尤为至关…

作者头像 李华
网站建设 2026/9/28 4:45:31

初级1_python概述

目录 应用领域 语言优势 语言缺点 下载和安装 应用领域 语言优势 解释型语言&#xff1a;一边解释&#xff08;或翻译&#xff09;一边运行。胶水语言&#xff1a;因为 Python 能通过丰富的接口和模块&#xff0c;把 C/C、Java 等不同语言写成的组件方便地“粘”在一起协同…

作者头像 李华
网站建设 2026/9/28 4:44:54

强化学习RL学习(未完待续)

1.什么是强化学习强化学习是一类解决序列决策问题的计算方法。一个智能体&#xff08;Agent&#xff09;处在某个环境&#xff08;Environment&#xff09;中&#xff0c;在每个时刻观察环境的状态&#xff08;State&#xff09;&#xff0c;据此选择一个动作&#xff08;Actio…

作者头像 李华