尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

armcc、armclang、arm-none-eabi-gcc 三款嵌入式编译器怎么选?

armcc、armclang、arm-none-eabi-gcc 三款嵌入式编译器怎么选? 1. 选编译器之前先回答这3个问题最近在好几个嵌入式交流群和社区帖子里都看到有人在问同一件事armcc、armclang 和 arm-none-eabi-gcc 到底该选哪个而且提问的人从刚入门的学生到做量产项目的工程师都有。说实话这个问题放在五六年前根本不需要纠结那时候 Keil MDK 默认就是 armccSTM32CubeIDE 还没普及GCC 只是少数 Linux 玩家的选择。但现在不一样了Arm Compiler 6 全面接管GNU 工具链铺天盖地旧项目又堆了一堆 AC5 时代的代码三选一就成了一个绕不过去的选择题。我不打算直接甩一个“选 XX 就完事了”的结论因为没有一种编译器能适配所有项目。我先带你把这三个名字背后的事情理顺再对照自己的项目情况做判断。整个选择的逻辑本质上就围绕三个问题展开。1.1 你是在给哪个芯片、哪个 IDE 开发这个问题直接决定你的工具链入口。如果你用的是 Keil MDK那么你眼前要么是 AC5armcc要么是 AC6armclang这两个都嵌在 Keil 的安装包里。如果你用的是 STM32CubeIDE、IAR 的某种替代方案、或者 VS Code 命令行脚本大概率逃不开 arm-none-eabi-gcc。还有一小部分人用的是 IAR 的编译器但它不在今天的讨论范围内我们只聚焦标题里的三款。芯片厂商的 SDK 也会影响选择。比如 ST 的 HAL 库在 CubeMX 生成的工程里默认会按照编译器类型自动切分支HAL 库源码里有一堆#if !defined(USE_FULL_LL_DRIVER)、__GNUC__、__CC_ARM、__ARMCC_VERSION这样的宏判断。不同编译器预定义宏不一样同样的代码可能走完全不同的逻辑分支。所以你不想第一天就陷入宏定义的泥潭最好跟着官方主推的工具链走。1.2 项目是新的还是从旧代码迁移这个问题最容易被忽略但往往才是最致命的。如果你手上有一个运行了三五年的老产品代码是 AC5 环境下写的里面用了很多只有 armcc 才支持的语法和底层扩展那强行切到 armclang 或 GCC 绝对不是换个编译器按钮那么简单。轻则编译报错一堆重则运行行为悄然变化尤其是结构体对齐、位域分配、指针别名这类细节。反过来如果是全新项目没有任何历史包袱那我建议你从第一天就选一个站在未来方向的工具链。2024 年 Arm 已经正式停止 AC5 的技术支持Keil 也从 MDK 5.37 之后把 AC6 作为默认编译器。也就是说你现在新装的 Keil默认就是 armclang。如果你非要用 AC5还得去 Pack Installer 里单独把老编译器翻出来。新项目继续用 armcc 不是不行但等于从起跑线上就背了一个“逐渐被淘汰”的债。1.3 你对编译器的优化策略了解多少很多人以为只要点一下 Build编译器就会自动生成最优代码其实远没这么简单。armcc 有它自己的优化思路armclang 继承了 LLVM 那套优化管线GCC 则有-O0、-Os、-O2、-O3外加各种自定义 flag。同样是跑一个 Cortex-M4 上的 DSP 算法三套工具链编出来的结果可能差 20% 的性能或者 30% 的体积。所以在做选择之前最好先问自己一个问题我对这套工具链的优化选项、内存布局控制、启动文件适配、链接脚本编写到底了不了解如果你之前只会点编译按钮那么换哪个编译器对你的代码质量提升都有限。反过来如果你对某一套工具链的脾气摸得很透那它就是你当前项目最合适的答案。我见过不少工程师项目换到 STM32CubeIDE 之后抱怨 GCC 生成的代码比 Keil 大了不少追根溯源是没开-Os也没用newlib-nano默认的-O0当然又大又慢。这属于工具没用好不是工具本身不行。2. armcc、armclang、arm-none-eabi-gcc三兄弟的区别这三个名字听起来有点像但它们的技术血统完全不同。armcc 是 ARM 公司自家的经典编译器armclang 是 ARM 基于 LLVM 和 Clang 重新打造的新一代编译器arm-none-eabi-gcc 则是开源社区和 ARM 合作维护的 GNU 工具链。你把它们的关系理解成“老前辈、接班人、外来的和尚”就差不多了。2.1 armccAC5经典但老去的 Keil 编译器armcc 是 Arm Compiler 5 的核心编译器Keil MDK 里叫 AC5。它最早可以追溯到 20 多年前的 ARM ADS、RVCT 时代在老一代嵌入式工程师手里流传极广。很多教科书、开发板例程、高校实验指导书里写的还是armcc这也导致不少刚入行的同学以为 Keil 只有这一种编译器。AC5 的优势在于和 Keil 的老生态深度绑定工程配置简单编译速度快对老旧 ARM7、ARM9、Cortex-M3 的兼容性没人能比。很多老工程师换了三家公司写代码的风格还是当年 AC5 留下的那套比如直接用__irq、__attribute__((section(xxx)))这类关键字。但 AC5 的问题也很明显它不支持完整的 C11 和 C14对 ARMv8-M 新架构、Cortex-M23/M33/M55 这些新内核的支持很勉强而且 ARM 官方已经停止维护。如果你还拿着 AC5 去怼新的带 TrustZone 的 MCU很多安全扩展特性根本用不上。所以不是 AC5 不好是它的任务已经完成了该退休。2.2 armclangAC6LLVM 血统的 Arm Compiler 6armclang 是 Arm Compiler 6 的核心编译器Keil 里叫 AC6。它把 LLVM 的 Clang 前端和 LLVM 优化器、Arm 自家的后端链接到了一起从 2016 年前后开始逐渐替代 AC5 的位置。到了 MDK 5.37 之后AC6 成为默认编译器。armclang 在标准支持上有明显优势支持 C11、C14对 C17 也有部分支持同时又能保持 Arm 架构底层的深入优化。它生成的代码在不少场景下比 AC5 小而且对新的 ARMv8-M 架构支持完整包括 TrustZone、低功耗中断、DSP 扩展这些新特性。如果你用 Keil MDK 开发新芯片我建议优先考虑 AC6。不要觉得 armclang 就是“另一个编译器”而已它的语法检查比 AC5 严格得多很多在 AC5 下能蒙混过关的写法到 AC6 这里直接报错。典型的例子是函数声明缺失、隐式类型转换、结构体指针的强制转换AC6 会给出明确的 warning 甚至 error。这其实是个好事逼着你把代码写规范。2.3 arm-none-eabi-gcc开源生态里的公认标准arm-none-eabi-gcc 是 GNU Arm Embedded Toolchain 的编译器名字里的 n-t-eabi 分别代表无操作系统、嵌入式应用二进制接口。它是目前整個嵌入式开源生态里最通用的编译器STM32CubeIDE、PlatformIO、VS Code Arm GNU Toolchain 这些主流开发环境全都基于它。GCC 的优势首先是免费、开源、更新快。ARM 官方和各大芯片厂商都会面向 GCC 做适配像 STM32 的标准外设库、HAL 库、DSP 库在 GCC 下的兼容性做得非常好。其次是可定制性极高你能通过各种编译选项、链接脚本、启动文件把整个构建过程完全掌控在手里适合做自动化构建、持续集成。GCC 的缺点是相对分散。它不像 Keil 那样一个 IDE 装完就全齐了你需要自己配编译器路径、连接脚本、烧录工具、调试器。刚入门的人第一次在 VS Code 里配 GCC 环境多半会被一个launch.json和一个tasks.json折磨几个小时。但一旦你把这套流程跑通效率是真的高。3. 从5个维度做横评不要只迷信“编译器”很多人选编译器只看编译出来的代码大小这是个误区。编译器的性能是要放在整个开发流程里看的包括标准支持、许可成本、调试工具、生态适配任何一个环节掉链子都可能让你从“省心”变成“闹心”。下面我按五个维度做一个横向对比每一个都是我实际踩过坑之后总结出来的。3.1 标准支持谁卡住了 C99、C11、C14对于嵌入式开发来说C 语言标准支持直接影响你能用什么语法。AC5 对 C99 的支持都不算完整更不要提 C11 和 C14。很多做音频库、算法库的同事想把标准库里的新特性用起来发现 AC5 编译不过只能自己手写替代函数非常痛苦。AC6 和 arm-none-eabi-gcc 对 C11 的支持都比较完整C14 也基本可用。如果你的项目是 C 编写或者从上游仓库拉下来的第三方库用到了较新的语法那基本只能在 AC6 和 GCC 之间选。纯 C 项目虽然标准差异影响不大但编译器的警告系统和静态检查能力差别还是很明显的AC5 基本等同于没有静态分析。编译器C标准支持C标准支持主要使用者armcc (AC5)C99 部分C03 为主老 Keil 项目armclang (AC6)C11 完整C14 完整Keil 新项目arm-none-eabi-gccC11/C17 完整C17 部分CubeIDE、VS Code3.2 代码大小与运行效率的实测差异关于代码大小网络上有各种测试数据但不同测试条件结果差别很大。我的实测经验是在 Cortex-M4 上跑同样的 STM32 HAL 工程AC6 开-Oz或-Os和 GCC 开-Os的代码体积整体非常接近差距一般在 2% 到 5% 以内。AC5 的代码在同样优化等级下老旧的编译器优化策略通常比 AC6 和 GCC 大 5% 到 10%。运行效率方面AC6 和 GCC 各有胜负。LLVM 后端在循环展开、向量化方面表现更好GCC 则在分支预测、内联决策上有自己的一套经验。对你来说性能差异远没有工程配置差异影响大。如果代码没跑满主频编译选项选-Os就够了完全没必要为了那点性能去折腾换编译器。3.3 许可成本与获取难度licensing 这个话题比较敏感但也是选型时的硬指标。armcc 和 armclang 都需要授权好在 Keil MDK 自带评估版你可以用 Keil 的免费版本配合 AC5/AC6 编写不超过一定代码量的程序很多个人学习和山寨项目就是这么玩的。但商业产品必须购买正版Arm Compiler 的授权费用并不便宜对小团队来说是不小的成本。arm-none-eabi-gcc 是完全免费的从 ARM 官网直接下载没有功能限制没有代码量限制。GCC 的这一优势让它在创业公司、个人开发者、开源硬件社区里占据绝对主导。对于在乎法务风险的小团队选 GCC 能省掉一笔不算少的预算。3.4 调试、链接、补丁生态的完整度调试体验上Keil 对 AC5 和 AC6 的支持天衣无缝断点、变量监视、寄存器视图、RTX 操作系统感知一站式解决。GCC 配合 OpenOCD arm-none-eabi-gdb VS Code调试体验已经非常接近商用 IDE但初次配置需要一些耐心。链接方面AC5 和 AC6 用的都是 ARM 自家的 armlink链接脚本语法是分散加载文件scatter file很多人不熟悉。GCC 用的是 GNU ld链接脚本是.ld文件网上资料和芯片厂家的模板一抓一大把。我个人认为 GCC 的.ld文件比 scatter file 更直观因为你可以在链接脚本里用通配符和宏控制 section 的排布而 scatter file 的语法相对僵化。3.5 对 RTOS、STM32Cube、FreeRTOS 等上下游的支持这块我只能用四个字总结都做得好但细节不同。ST 官方在 STM32Cube 里同时维护 Keil 和 GCC 的工程模板FreeRTOS 官方仓库也分别提供 ARM_CM3、ARM_CM4F 等针对不同编译器的移植文件。真正让你意识到生态差异的是那些第三方库。很多冷门芯片的开源驱动只写了 GCC 的移植Keil 用户要自己改类型重定义、宏定义反过来一些国产芯片厂家给的老例程只支持 Keil AC5你用 GCC 去编译就会遇到一堆语法不兼容的错误。所以选择编译器本质上是选择和你要用的芯片厂家的技术生态站在哪一边。4. 实战安装与 VS Code、Keil、STM32CubeIDE 串联理论讲再多不如实际装一遍。这里我把三套工具链的获取方式、安装路径、常见坑一次性说清楚特别是和 VS Code 配合使用时的路径配置问题属于所有新手和不少老手都会踩的雷区。4.1 下载和安装 arm-none-eabi-gccarm-none-eabi-gcc 的下载页面是 ARM 官方的 GNU Arm Embedded Toolchain你会看到一堆版本号比如 10.3-2021.10、12.2-2023.12。选择时注意两点一是选 ARM 官方编译的 Windows/Linux 安装包别去不明来源的网盘下载防止被塞进恶意脚本二是要选-win32.exe或-linux-x86_64.tar.bz2这类后缀正确的版本ARM 官网偶尔会在显眼位置推送AArch64版本那是给 Arm 架构服务器用的别下错了。安装路径建议直接放到一个纯净目录比如C:\arm-gnu-toolchain避免路径里有空格和中文。然后重点来了把C:\arm-gnu-toolchain\bin加进系统环境变量 PATH。不加 PATH 虽然也能用但 VS Code 扩展、CMake、Makefile 里调用arm-none-eabi-gcc时就要写全路径很容易因为路径输错而编译失败。如果你在 Windows 环境下配合 VS Code 使用推荐装 C/C 扩展、Cortex-Debug 扩展再配合 Makefile 或 CMake。Cortex-Debug 需要你指定arm-none-eabi-gdb的路径没配置 PATH 的话这里又是个坑。我用这套组合取代 Keil 做日常开发已经两年多了编译、烧录、调试全部一个 UI 完成体验很顺。4.2 在 Windows 上安装 AC5 与 AC6 时注意的点Keil MDK 安装好之后AC5 和 AC6 并不一定同时可用。新版本 MDK 默认带 AC6AC5 要在 Pack Installer 里手动勾选 “ARM Compiler 5.06 update 7” 之类的版本包。如果你打开一个老工程编译器下拉框里找不到你想要的 ARMCC十有八九是 Compiler Pack 没装全。安装的时候还要注意杀毒软件。Keil 的编译器会生成临时文件、动态链接库杀毒软件经常在编译瞬间把 fromelf.exe 或 armcc.exe 隔离。如果出现莫名其妙的编译失败先把 Keil 安装目录加进杀毒软件白名单再以管理员身份运行 Keil。这个问题在国产安全软件上表现得特别明显我在同事电脑上亲眼见过编译 10 次有 6 次报CreateProcess failed关了安全软件之后一次就好。另外Keil 的默认安装路径是C:\Keil_v5尽量不要改成带中文或空格的目录。虽然新版 Keil 对路径兼容性比老版好但底层调用 ARMCC 的时候老版编译器和 fromelf 工具对长路径、特殊字符的处理仍然不稳定。4.3 把 GCC 接入 VS Code 和嵌入式编译脚本VS Code 已经成了嵌入式开发的重要 IDE 之一但它的核心优势不在编辑而在于通过命令行工具链和构建脚本自由组合。使用 GCC 工具链的标准做法是用 Makefile 或 CMake 描述编译过程VS Code 的 tasks.json 负责调用 make 或 cmakelaunch.json 负责调用 gdb 和 OpenOCD。一个最简单的编译命令长这个样子arm-none-eabi-gcc -mcpucortex-m4 -mthumb -mfloat-abihard -mfpufpv4-sp-d16 \ -Os -ffunction-sections -fdata-sections \ -I./inc -DSTM32F407xx \ -DF_CPU168000000L \ -Tstm32f407vgt6_flash.ld \ -o build/main.elf main.c system_stm32f4xx.c这里的关键参数我拆开解释一下。-mcpu指定芯片内核-mthumb表示用 Thumb 指令集-mfloat-abihard告诉编译器硬件浮点单元可用并启用硬件 FPU 指令-mfpu是浮点单元类型选错的话芯片直接 HardFault。-ffunction-sections和-fdata-sections配合链接阶段的--gc-sections可以把没用到的函数和变量丢弃显著减小最终固件体积。链接脚本stm32f407vgt6_flash.ld通常由芯片厂家提供也可以自己写。你需要确保 FLASH 起始地址、RAM 起始地址、堆栈大小跟芯片的硬件对应。如果你忘了指定链接脚本GCC 会调用默认链接脚本编译出来的程序十有八九跑不起来。5. 迁移踩坑与常见报错排查不管你是从 AC5 跳到 AC6还是从 Keil 跳到 GCC迁移过程中都会遇到各种奇奇怪怪的报错。这一节我挑几个出现频率最高的场景按“现象 → 原因 → 解决”的方式给你梳理一遍。5.1 把代码从 AC5 切到 AC6 常犯的错误最常见的是老代码里用了__forceinline、__inline、__align、__packed这些 AC5 特有的关键字。AC6 基于 Clang对这些关键字的支持非常有限经常直接报 unrecognized。解决办法是可以改用标准的 C 关键字比如__attribute__((always_inline))、__attribute__((aligned(4)))、__attribute__((packed))或者在文件开头加上兼容层宏定义。第二个高发问题是隐式函数声明。AC5 对于没有声明就直接调用的函数只是给 warning而 AC6 默认把它升级成 error。之前在 AC5 下能编译通过的工程切到 AC6 后会突然冒出来十几条implicit declaration of function错误。这个只能老老实实补头文件、补函数声明没有捷径。第三个问题是结构体对齐的差异。AC5 在某些场合对结构体成员的排列有自己的规则AC6 更贴近 C 标准。如果你的结构体要从通信协议里直接映射字节流非常容易因为成员对齐不同而出错。解决办法是显式使用packed属性并且不要依赖编译器默认排列。5.2 KEIL 报错 createprocess failed...armcc/bin/fromelf 的解决方案这个报错在热词里出现了属于 Keil 里最经典的代表性错误之一。完整报错类似*** Error: CreateProcess failed, Command: C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --text ... -o ...axf现象是编译、链接都没问题但最后生成 bin 文件或 hex 文件的阶段失败。从我的经验看排名第一的原因根本不在 Keil 本身而是杀毒软件或 Windows Defender 在 fromelf.exe 执行时把它拦截了。解决方法是把C:\Keil_v5目录加入杀毒排除列表然后以管理员身份重新运行 Keil。第二个常见原因是路径问题。如果工程路径包含中文、空格或者过长fromelf 在调用时很容易因为路径解析失败而报 CreateProcess failed。解决办法是把工程文件夹移到纯英文短路径比如D:\Embedded\Projects\Demo。第三个原因是 ARMCC 编译器包版本不完整。我遇到过用户某天突然打开了别人发来的工程然后报这个错结果发现对方用的 AC5 版本没装。到 Pack Installer 里把对应的 ARM Compiler 版本装上问题就解决了。5.3 GCC 环境下的浮点、newlib 和链接脚本问题GCC 环境最常被问到的一个是 printf 输出浮点数不工作一个是程序不进 main。前者我已经在参数部分提过需要-u _printf_float否则 newlib 默认不包含浮点输出代码以节省空间。不进 main 的情况绝大多数是启动文件或者链接脚本没配对。很多新手拿到一个陌生芯片的startup_xxx.s不知道要修改向量表也不知道把__etext、__data_start__、__bss_start__这些符号和链接脚本对上全盘照抄另一个芯片的模板最后的症状就是复位后跳到一个空地址。还有一个小但非常典型的坑是sbrk和_exit没有实现。GCC 的 newlib 依赖底层系统接口你需要在 start 阶段实现_sbrk用于堆内存分配实现_write用于输出。很多人在裸机上用 malloc、printf忘了补这些底层函数结果程序跑起来随机崩溃。5.4 我现在一口气整理的正确切换顺序如果你现在正打算把一个工程从一个工具链切换为另一个工具链我的建议是不要直接在旧工程上狂改而是按以下顺序走先把旧工程的代码完整备份同时在版本管理工具里打一个 tag。用新工具链建一个空工程把源文件和头文件目录逐个加进来先保证能够编译通过。编译之后逐个消除 warning因为 warning 往往能暴露出你之前没注意的隐患。检查链接脚本和启动文件确认 RAM、FLASH、堆栈分配和芯片绑定一致。最后跑功能验证尤其注意串口打印、浮点运算、结构体数据结构转换、中断向量表这些敏感模块。不要幻想一步到位。一个成熟的嵌入式工程迁移我通常给团队留至少两到三天的测试时间。6. 不同人群的最终选择建议做了这么多对比最后落到每个人身上选择其实并不复杂。我是按人群来说的你可以直接对号入座。6.1 学生、竞赛、入门优先选 STM32CubeIDE 或 Keil 自带默认编译器还在学习阶段或者参加蓝桥杯嵌入式、电赛这类竞赛的同学最好的选择不是纠结编译器而是先把芯片的工程框架跑起来。如果学校实验室统一用 Keil那你就用 Keil 默认的 AC6不要去动 AC5。如果你自己装环境我更推荐 STM32CubeIDE它内置了 arm-none-eabi-gccCubeMX 生成工程、烧录、调试全链路一体也是最接近行业真实工作流的。对于初学者我不建议一开始就去折腾 VS Code 配 GCC因为你会很容易把时间耗在工具链的安装配置上而不是学习代码本身。等你能独立完成一个带外设驱动、有中断、能通信的完整项目后再跳出 IDE 去研究底层构建收获会更大。6.2 做产品、量产、车载等高可靠项目根据芯片原厂主推来选如果项目是商业量产尤其是车载、医疗、工控这类对可靠性和合规有要求的领域我的建议是“原厂主推谁你就用谁”。芯片厂家推荐 Keil AC6你就用 AC6厂家提供 GCC 的固件和自动化测试脚本你就用 GCC。这类项目通常会有功能安全认证的需求比如 ISA/IEC 61508。编译器的资质认证往往要花费大量精力和费用GCC 作为开源软件在部分认证体系中反而比商业编译器更难走流程因为你需要对整个开源工具链进行验证和确认。相反Arm 官方对 AC5/AC6 都提供了对应安全认证包这正是很多车载项目仍然选择 Keil 的原因。6.3 长期开源/Linux/VS Code 阵营认准 arm-none-eabi-gcc如果你偏爱 Linux 开发、用 CMake 管理代码、在 CI 服务器上跑自动化编译arm-none-eabi-gcc 是唯一合理的选择。Keil 的 AC5/AC6 虽然也能在命令行下调用但授权模型和构建脚本的灵活性远不如 GCC。GCC 的编译命令天然兼容 make、CMake、Docker、Jenkins整个开源生态里的一切工具都能接上。我用 GCC 结合 VS Code 开发嵌入式 Linux 和裸机项目的这些年最明显的感受是“一切皆可脚本化”。代码风格检查、静态分析、单元测试、覆盖率统计全部能串进流水线。这一点是商用 IDE 很难比得上的。6.4 如果你还是不确定我建议你做一个两天小实验选择困难症的解药永远是亲手试一试。你可以挑一个比较熟的小功能比如串口接收数据、控制 LED 呼吸灯分别用 AC6 和 arm-none-eabi-gcc 建两个工程各写一遍。对比生成固件的大小跑起来比较一下响应时间再把两套工程的构建流程走一遍。两天的实验做完你心里自然会有答案。不用怕选错编译器本质上只是工程构建的一个环节。真正重要的是你对芯片的理解和对代码底层的掌控力。就算选了一个不顺手的只要代码规范、模块清晰后面迁移的成本也是完全可控的。我个人在实际操作中的体会是工具链之争远没有网上说的那么玄乎。AC5 适合处理老代码AC6 适合在 Keil 生态里长期发展GCC 适合追求开源和自动化的场景。最怕的不是选哪一个而是明明选好了却又在项目中途反复横跳。选定一套把它吃到透你的开发效率一定会超过那些天天纠结编译器的人。
返回列表