Keil5中AC5与AC6编译器共存及迁移实战指南
发布时间:2026/9/24 11:18:28 作者:尧图编辑部 阅读量:1,286

换了新电脑、装了新版的Keil5然后打开自己五年前的老工程一按编译“啪”地弹出一行红字Error: Not a genuine ARM Compiler或者无法找到编译器AC5。如果你点进 Options 的 Target 选项卡还会发现编译器下拉框里只剩个 AC6AC5不知道跑哪儿去了。再一搜社区一边有人说 AC6 是未来、早迁早省心一边有人说老工程千万别动 AC5越迁越炸。这种编译器版本混乱几乎是每个嵌入式开发者绕不过去的一关。先用一句话把这两兄弟说清楚AC5 是 ARM Compiler 5核心编译器叫 armccAC6 是 ARM Compiler 6核心编译器叫 armclang底层用的是 LLVM 和 Clang。这篇文章就把它们的原理差异、选型逻辑、怎么在 Keil5 里共存、怎么从 AC5 往 AC6 平稳迁这些事一次性讲透。内容主要面向使用 MDK-ARM 做 STM32、NXP、GD32 这类 Cortex-M 项目的开发者也适合被旧工程折磨、一编译就报错的新手参考。1. AC5与AC6到底是什么从工具链内核说起1.1 两代ARM编译器背后的技术路线AC5 是 ARM 收购 Keil 之后继续维护的一套闭源编译器它由 armcc 编译器、armasm 汇编器、armlink 链接器、armar 库管理器组成整套工具链的设计思路可以追溯到上世纪九十年代的 ARM SDT / ADS / RVCT 那一脉。AC6 则是 ARM 后来基于 LLVM/Clang 生态重新打造的新一代编译器。它把前端换成了 Clang中间层优化和后端生成交给 LLVM所以你在工程里看到armclang.exe这个名字其实就是 AC6 的编译驱动。AC6 的汇编器也不是老 armasm而是 LLVM 内置的集成汇编器链接器仍然复用 armlink但很多控制参数已经向 GCC/Clang 风格靠拢。这套区别带来一个非常直观的现象在 Keil5 里AC5 的版本号基本是 5.06 update 7、5.06 update 8 这类AC6 的版本号则是 6.16、6.18、6.21 这样。从 Keil MDK 5.36 开始官方默认安装包里只带 AC6AC5 需要自己额外补齐所以网上大量“无法找到编译器 AC5”的报错基本都是这个原因。1.2 编译器内核差异带来的“看得到”影响内核换了工程层面的表现差异非常明显我挑几个开发中一定会遇到的讲。第一是 C 语言标准支持。AC5 默认按 C90/gnu90 处理对 C99 支持也不算完整想用 C11 基本别想。AC6 默认是 gnu11C11、C11 甚至 C17 都有良好支持。最典型的就是for(int i 0; i n; i)这种在循环里声明变量的写法AC5 不开--c99选项编译期会报错或者警告AC6 直接写没有任何问题。第二是编译速度和优化能力。AC6 走 LLVM 架构多文件并行编译、链接时优化、对新内核指令调度的支持都比 AC5 强出一大截。大型工程 AC6 整体编译时间能比 AC5 短很多。代码体积上AC5 在 -O2 下表现不差但 AC6 做好配置后用 -Oz 或 -Os在很多场景下能把体积压到比 AC5 更小如果开发者完全不配置AC6 默认优化级别下代码也可能比 AC5 大。所以“AC6 一定比 AC5 大”这个说法是不准确的。第三是编译器的严格程度。AC6 继承了 Clang 的“严格执法”风格很多在 AC5 下只是 warning 的写法到了 AC6 会直接升级成 error。比如隐式函数声明、结构体对齐方式、#pragma指令拼写、内联汇编语法这些是老工程迁移时的重灾区。后面我会单独用一节专门讲迁移问题。2. 新旧项目怎么选先顺着这几条判断再动手2.1 什么时候继续用AC5如果一个项目符合下面几种情况我建议不要冒进切 AC6老工程且没有大量时间做回归验证。工程里用到了很多第三方的老库比如某些早期的电机控制库、加密库、老版本 GUI 库这些库作者已经不再维护在 AC6 下重新编译可能因为内联汇编写法不兼容而报错甚至链接阶段才炸出来。你要一个个改库源码风险和工作量都会成倍增加。芯片型号本身很老厂商已经停止更新底层驱动包。很多早期 Cortex-M0/M3 型号的 SVD 文件、startup 启动文件、标准外设库都是照着 AC5 的汇编器写的。AC6 内置汇编器对老式汇编语法的兼容性一般直接编译可能一堆奇怪报错。团队里其他人全部还在用 AC5或者客户验收时指定用某个 AC5 版本。这时候不要为了“技术先进”去折腾全组人的工具链工程维护的第一原则是不折腾能跑的东西。2.2 什么时候果断切AC6如果你遇到的是下面这些场景直接切 AC6 会更省心新项目、新芯片。尤其是 Cortex-M33、M55、M85 这类新内核很多新特性指令集在 AC5 上根本无法发挥厂商自己的 SDK 默认也是按 AC6 配置。项目需要使用 CMSIS-DSP、CMSIS-RTOS2、TrustZone 安全分区、ARMv8-M 相关的特性。这些新组件在 AC6 下的支持力度明显更好一些官方例程已经彻底放弃了 AC5。代码需要跨平台复用或者团队里有 Linux 下开发、需要和 GCC 编译结果做对比验证的同事。AC6 底层也是 Clang很多编译选项、预处理宏、__attribute__语法和 GCC 有互通性共享代码时能少踩很多坑。长期维护的通用代码库。AC5 基本已经停止功能更新正式支持期早就过了新版本 Keil 里它属于“遗留工具链”。你如果还想在这个项目上持续迭代三五年早点迁到 AC6后面能少很多环境问题。2.3 选型时要留意的坑不要只看“能不能编译通过”。有的工程在 AC6 下第一次编译就过了但运行起来发现浮点打印异常、中断优先级配置失效、结构体在通信协议里对不上。这些都是编译器数据模型和函数调用规则差异引起的运行时问题比编译错误更难排查。也不要因为报错太多就放弃。AC6 的报错信息其实是比 AC5 更规范的很多错误信息都能直接定位到具体行和原因。前期把老代码里的 C90 风格、隐式声明、非标语法清理干净之后后续维护反而舒服。我自己在实际项目中定过一条选型规则能用 AC6 的新工程坚决不用 AC5被迫维护老工程时保持 AC5 不动两个编译器原则上不混用。虽然 Keil 支持每个工程独立选编译器但同一个团队、同一套代码库里混用两代编译器很快就会出现一堆“我这儿能过你那儿不能过”的扯皮问题。3. 实操篇8分钟实现Keil5里AC5与AC6共存3.1 检查你当前Keil5到底装了什么编译器动手之前先看清楚手头环境。打开你的 Keil5随便打开一个 MDK 工程点击魔术棒图标Options for Target切到 Target 选项卡找到Code Generation区域里的ARM Compiler下拉框。如果下拉框里有Use default compiler version 5或者V5.06 update 7说明你的 Keil 还能识别到 AC5如果只有Use default compiler version 6和V6.x说明当前工程模式下只有一个 AC6。另一个查看位置是Project - Manage - Project Items - Folders/Extensions这里面会列出 Keil 全局识别到的编译器路径。正常情况下AC5 的安装目录是 Keil 安装根目录下的ARM/ARMCC文件夹AC6 的安装目录是ARM/ARMCLANG文件夹。你可以打开文件管理器直接看这两个文件夹是否存在。3.2 补装AC5的完整步骤如果你打开ARM目录发现只有ARMCLANG没有ARMCC那就证实了 AC5 缺失。解决方式两种第一种从 ARM 官网下载 Arm Compiler 5 的独立安装包安装时会自动把 ARMCC 文件夹写到 Keil 目录里第二种如果你手头还有一台装有旧版 Keil5比如 5.23、5.30 时代的电脑直接把它的C:\Keil_v5\ARM\ARMCC整个文件夹拷贝到新电脑的对应位置。拷贝老编译器这个办法实测是可行的因为 Keil 的编译器目录结构相对独立。拷完之后打开 Keil进入Project - Manage - Project Items - Folders/Extensions切到 Folders/Extensions 页签在ARM Compiler区域如果看不到 V5可以点击旁边按钮手动添加路径指向你刚拷贝进来的ARMCC文件夹。如果一切正常下拉框里就会多出 V5 的条目。装好之后强烈建议立刻关掉 Keil 重开一次确保 Keil 正确刷新了工具链列表。然后新建一个空白 STM32 工程在 Target 选项卡里把编译器切成 V5编译一下确认不再报“无法找到编译器 AC5”。3.3 工程切换与编译器选项配置当 AC5 和 AC6 都装好后每个工程可以在Options for Target - Target - ARM Compiler里独立切换。切换后在同一个 Target 选项卡的右下角会跟着切换代码生成选项的界面AC5 是经典的 C99 Mode、One ELF Section per Function 这些AC6 则是优化等级下拉框、GNU extensions 开关等选项风格明显不同。这里有一个很容易踩的坑切换编译器之后老工程在 C/C 选项卡里配置的头文件路径、宏定义、Misc Controls 不一定能直接兼容。比如 AC5 里用的--c99 --split_sections这类参数是传统写法AC6 里更推荐-stdgnu11 -ffunction-sections -fdata-sections。如果两套参数混在一起写很可能出现“未知命令选项”的报错。我自己一般在切换后会把 Misc Controls 里那些跟编译器强相关的参数清理一遍保留通用的 include 路径和宏定义即可。3.4 写一个最小验证工程为了确认编译器切换真的没问题建议做一个最小验证工程选好芯片型号添加一个空的 main 函数只做一件事用printf和延时翻转 LED。分别用 AC5 和 AC6 各编译一次确认两个编译器都能正常生成 hex 文件再把这个工程丢给其他同事验证工具链路径在他机器上也识别正常。这个“最小验证”的习惯非常有用。很多时候你迁移一个几百文件的大工程失败很难判断到底是编译器配置问题还是源文件语法问题。先用最小工程把工具链路打通后面再往工程里加文件报错的定位范围会小很多。4. 从AC5迁到AC6的踩坑实录4.1 C库、启动文件与分散加载切到 AC6 之后最先发现的往往不是语法错误而是底层启动和 C 库的问题。老工程的startup_xxx.s如果是旧版本使用老 armasm 语法写成AC6 的 armclang 内置汇编器未必认。解决方法是去对应芯片厂商的 Pack 包或者 GitHub 上找新版本的 startup 文件一般 CMSIS 5 及以上版本的启动文件都已经是 AC6 兼容写法。C 库方面AC6 默认使用的运行时库和 AC5 不完全一样最常见的一个链接错误是Error: L6218E: Undefined symbol __use_no_semihosting这是典型的 semihosting半主机模式问题。老办法是在 AC5 下写一个__use_no_semihosting函数来关闭半主机模式但到了 AC6 里这一套不一定生效。改用 GCC/Clang 风格会更直接__asm(.global __use_no_semihosting);或者干脆在工程里把微库 MicroLIB 打开很多嵌入式工程只需要 printf 重定向到串口用 MicroLIB 能省掉一堆 C 库链接问题。分散加载文件.sct也要检查。老的 scatter 文件里可能写了LOAD_REGION_START、__at这类 ARM 工具链特有表达式AC6 对这些表达式保留了一部分但有些写法已经不受支持。最简单的做法是把老 .sct 文件里与地址映射相关的部分保留凡是编译器相关的伪指令全部删掉让 Keil 按默认方式重新生成。4.2 关键字、内联汇编与编译器特性改写AC6 对关键字和编译特性的要求比 AC5 严格得多这一节是迁移工作量的主要来源。先看最典型的__packed。这种写法在 AC5 下是官方标准关键字但 AC6 更认__attribute__((packed))或#pragma pack(push, 1)。如果你直接拿老代码编译会看到大量type qualifier specified but not used或者结构体对齐相关的警告。同样的__align(4)、__at(address)这类关键字在 AC6 下推荐改成__attribute__((aligned(4))) __attribute__((section(.ARM.__at_0x08000000)))如果你原来用__attribute__((at(0x08000000)))指定变量地址换成 section 写法之后注意保证对应的分散加载文件里有足够的执行区空间否则链接器会在生成镜像时才报地址溢出这个错误不像编译错误那么好定位。内联汇编是另一个大头。AC5 支持传统的__asm { ... }块语法AC6 对汇编的支持更接近 GCC 风格比如__asm volatile(nop); __asm volatile(MRS %0, primask : r(reg));老代码里的__asm { MRS R0, PRIMASK }这类直接写在 C 文件里的汇编块在 AC6 下要么重写成 GCC 风格要么把汇编逻辑移到独立的.s文件里用函数接口调用。注意移动汇编到独立文件时函数命名和启动文件里的符号要保持一致否则链接时会出现undefined symbol。4.3 常见问题速查表把这两年帮人排查问题时碰到的高频问题整理成一张表照着查会快很多现象根本原因快速处理无法找到编译器 AC5新版 Keil 默认不带 AC5 编译器下载/拷贝 ARM Compiler 5并在 Manage Project Items 中指定路径ARM Compiler 下拉框只有 V6工程模板或安装包只注册了 AC6补装 AC5 或直接切换 V6 编译大量#pragma相关警告AC5 老式 pragma 在 AC6 不兼容改成__attribute__写法或删除无效 pragmafatal error: core_cm4.h file not foundCMSIS 文件版本过旧或路径不包含更新 Pack 包检查 Include Path 是否包含 CMSIS 目录启动文件报Unknown instruction老汇编语法与 armclang 汇编器不兼容替换为厂商新版本启动文件__use_no_semihosting未定义AC6 C 库的半主机关断开法不同用__asm(.global __use_no_semihosting);或开启 MicroLIB运行后 printf 输出乱码浮点格式化或者重定向函数不兼容确认是否勾选 Use MicroLIB检查 fputc 重定向实现Target 里 Xtal 时钟项变灰芯片型号设置限制了时钟参数去 Device 页重置芯片型号检查外部晶振配置宏提示编译器未包含main类型工程类型或工具链选择和芯片不匹配重新创建正确的 MDK 工程把 C 文件加进去而不是硬改模板代码能编译但运行 HardFault结构体对齐、汇编访问权限变化或优化引入问题逐模块检查结构体 packed 属性降低优化等级做二分定位这张表里最后两行特别提醒一下。编译器未包含main类型说穿了不是编译器本身的问题更多是工程模板不干净或者拿一个其他工具链生成的工程目录强行在 Keil5 里打开这时候直接新建一个正确芯片型号的 MDK 工程把源文件重新添加进来最省事。而 HardFault 的问题表面上和编译器版本没有直接关系但 AC6 对未对齐访问、数据对齐规则更敏感老代码里一个没加 packed 的结构体AC5 下可能刚好没触发问题AC6 下就现场事故别只顾着查硬件。最后再分享一点工具链管理的个人建议从 AC5 迁到 AC6 这件事千万不要抱着“一把梭”的心态直接拿大工程试。我一般习惯先花小半天时间做三件事备份老工程、更新 CMSIS 芯片包、用最小工程验证新工具链。然后把老工程的源码分类先把外设驱动和底层 BSP 编译过再逐步加中间层和应用代码每加一层都编译一次。这样出了问题能快速判断是哪一部分源码不兼容而不是面对几百个报错傻眼。另外无论你最终决定用 AC5 还是 AC6建议在项目根目录放一个 README明确写上“本工程编译器使用 AC5 5.06 update 7”“本工程基于 MDK 5.37 AC6 6.18”这类信息。嵌入式项目团队协作最怕的就是环境不一致工具链固定下来很多莫名其妙的编译问题不会出现。踩过几次坑之后我的真实体会是AC6 不是洪水猛兽AC5 也不是情怀代名词。它们只是 ARM 给开发者留下的两代工具箱各有适用场景。搞清楚了背后的原理再按项目情况理性选择你就能从“编译器版本混乱”里彻底脱身。