首页 / 资讯中心 / 文章详情

Windows下ARM裸机开发:gcc-arm-none-eabi-7-2018-q2-update工具链配置与实战

Windows下ARM裸机开发:gcc-arm-none-eabi-7-2018-q2-update工具链配置与实战 ★ FEATURED ARTICLE
简介gcc-arm-none-eabi-7-2018-q2-update-win32.zip 是 Arm 官方发布的 GNU 嵌入式工具链 Windows 版本面向从事 Arm Cortex-M 与 Cortex-R 系列处理器开发的嵌入式工程师、高校学生及竞赛选手用于 C、C 与汇编语言的交叉编译、链接与调试解决 Windows 平台下缺少原生 Arm 嵌入式编译环境的问题。压缩包共 5291 个文件约 124.79MB以 html 文档、h 头文件、a 静态库、specs 配置、o 目标文件及 hpp 头文件为主另含 exe 可执行程序、py 脚本、ld 链接脚本、pdf 手册与 makefile 等覆盖编译器、标准库、链接器与文档的完整目录结构。目前已有 1077 人学习下载。解压后可直接获得 arm-none-eabi-gcc、g、gdb 等工具及 libstdc 等运行库便于搭建裸机或 RTOS 工程、编写启动文件与链接脚本并借助随附文档排查编译链接问题适合作为嵌入式开发与课程实验的基础工具链。1. 拿到 gcc-arm-none-eabi-7-2018-q2-update-win32.zip 之后为什么老版本工具链反而更值得留一手如果你在 Windows 上做 ARM Cortex-M 裸机开发大概率见过这个文件名gcc-arm-none-eabi-7-2018-q2-update-win32.zip。它不是最新版官方早就把主线推到了 10、12、13 甚至更高但很多量产项目、教学实验箱、老旧的 Makefile 工程仍然锁死在这个 2018 年第二季度的版本上。原因很现实新版工具链默认开启的某些优化、链接脚本行为、甚至 newlib 的 syscall 桩函数签名都可能让一个跑了五年的工程突然编译不过或者运行跑飞。这个压缩包解压即用不需要安装程序不写注册表放在 D 盘某个目录里把 bin 加进 PATH 就能开工。它适合谁适合手里有现成 ARM 工程、不想折腾工具链迁移的嵌入式工程师也适合刚接触交叉编译、想先跑通一个最小 LED 闪烁程序的新手。接下来我会按“解压后怎么配、编译一个最小工程、链接脚本怎么改、常见报错怎么查”的顺序把这个 zip 的落地路径拆开讲。2. 解压与 PATH 配置把 arm-none-eabi-gcc 变成随手可用的命令2.1 解压位置的选择与目录结构确认这个 zip 解压后通常得到一个名为gcc-arm-none-eabi-7-2018-q2-update的文件夹里面包含bin、lib、share、arm-none-eabi等子目录。我一般会把它放在D:\tools\gcc-arm-none-eabi-7-2018-q2-update路径里不要有中文和空格否则 Makefile 里引用$(GCC_PATH)时容易出玄学问题。解压完成后先确认bin目录下有没有arm-none-eabi-gcc.exe、arm-none-eabi-objcopy.exe、arm-none-eabi-size.exe这三个关键可执行文件。如果用的是 Windows 自带的解压偶尔会把长路径截断导致bin下文件不全这时候换 7-Zip 重新解压一次。# 在 Git Bash 或 CMD 中进入解压目录检查关键文件 cd /d/tools/gcc-arm-none-eabi-7-2018-q2-update/bin ls arm-none-eabi-gcc.exe arm-none-eabi-objcopy.exe arm-none-eabi-size.exe上面命令列出三个文件如果都有输出说明解压完整。arm-none-eabi-gcc.exe是编译器驱动负责调用 cc1、as、ldobjcopy用来把 ELF 转成 bin 或 hexsize用来查看 Flash 和 RAM 占用。缺少任何一个后续步骤都会卡住。2.2 把 bin 目录写进 PATH 的两种方式临时用的话在 CMD 里执行set PATHD:\tools\gcc-arm-none-eabi-7-2018-q2-update\bin;%PATH%这个窗口关掉就失效。长期用的话我习惯在系统环境变量里新建一个GCC_ARM_PATH值为D:\tools\gcc-arm-none-eabi-7-2018-q2-update\bin然后在Path里追加%GCC_ARM_PATH%。这样做的额外好处是 Makefile 里可以直接写CC : $(GCC_ARM_PATH)/arm-none-eabi-gcc换电脑时只改一个变量。# 验证 PATH 是否生效 arm-none-eabi-gcc --version # 预期输出类似 # arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622如果提示“不是内部或外部命令”说明 PATH 没写对或者解压目录下的bin名字被改过。注意版本号里的7.3.1 20180622是这个工具链的内部版本和文件名里的7-2018-q2对应看到这个输出就说明工具链本身没问题。2.3 用 Makefile 固定工具链前缀在工程根目录的 Makefile 里我一般会显式指定工具链前缀而不是依赖 PATH 里的默认arm-none-eabi-。这样做的好处是同一台机器上可以并存多个版本切换工程时不会互相干扰。# Makefile 片段固定工具链路径 GCC_ARM_PATH : D:/tools/gcc-arm-none-eabi-7-2018-q2-update/bin CC : $(GCC_ARM_PATH)/arm-none-eabi-gcc AS : $(GCC_ARM_PATH)/arm-none-eabi-as OBJCOPY : $(GCC_ARM_PATH)/arm-none-eabi-objcopy SIZE : $(GCC_ARM_PATH)/arm-none-eabi-size # 编译选项Cortex-M4硬件浮点优化等级 -Og 方便调试 CFLAGS : -mcpucortex-m4 -mthumb -mfpufpv4-sp-d16 -mfloat-abihard \ -Og -g3 -Wall -ffunction-sections -fdata-sections LDFLAGS : -T linker.ld -Wl,--gc-sections -Wl,-Mapoutput.map这里-mcpucortex-m4和-mthumb指定指令集-mfpu和-mfloat-abi决定浮点单元用法如果芯片是 Cortex-M3 或 M0要去掉浮点相关选项。-ffunction-sections和-fdata-sections配合--gc-sections可以删掉未引用的函数和变量对 Flash 紧张的片子很关键。-Og是调试友好优化比-O0生成的代码小又不像-O2那样打乱断点位置。3. 编译一个最小 Cortex-M 工程从启动文件到 LED 闪烁3.1 启动文件与链接脚本的最小集合一个能跑的最小工程至少需要启动文件汇编、链接脚本、主程序 C 文件、Makefile。启动文件负责初始化栈指针、复制 data 段、清零 bss 段、跳转到 main。链接脚本决定 Flash 和 RAM 的起始地址与大小。下面是一个针对 128KB Flash、20KB RAM 的通用模板。/* linker.ld最小链接脚本 */ MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 128K RAM (rwx) : ORIGIN 0x20000000, LENGTH 20K } SECTIONS { .text : { KEEP(*(.isr_vector)) *(.text*) *(.rodata*) } FLASH .data : { *(.data*) } RAM AT FLASH .bss : { *(.bss*) *(COMMON) } RAM }ORIGIN和LENGTH必须和芯片手册一致写错会导致程序下载后不运行。.isr_vector段用KEEP防止被 gc-sections 删掉中断向量表必须放在 Flash 最开头。.data段用AT FLASH表示加载地址在 Flash、运行地址在 RAM启动文件里的复制循环依赖这个设定。3.2 主程序与编译命令主程序里只做一件事把某个 GPIO 置高再置低循环产生方波。这里不依赖任何厂商库直接操作寄存器方便验证工具链本身是否正常。/* main.c最小 LED 闪烁寄存器地址为示例 */ #include stdint.h #define RCC_AHB1ENR (*(volatile uint32_t *)0x40023830) #define GPIOA_MODER (*(volatile uint32_t *)0x40020000) #define GPIOA_ODR (*(volatile uint32_t *)0x40020014) static void delay(volatile uint32_t count) { while (count--) { __asm volatile (nop); } } int main(void) { RCC_AHB1ENR | (1 0); // 使能 GPIOA 时钟 GPIOA_MODER ~(3 10); // 清 PA5 模式位 GPIOA_MODER | (1 10); // PA5 设为输出 while (1) { GPIOA_ODR ^ (1 5); // 翻转 PA5 delay(200000); } }编译命令用 Makefile 驱动核心是arm-none-eabi-gcc把.c和.s编译成.o再用arm-none-eabi-ld或直接由 gcc 驱动链接最后用objcopy生成.bin。# 手动编译一遍确认工具链各环节正常 arm-none-eabi-gcc -mcpucortex-m4 -mthumb -Og -g3 -c main.c -o main.o arm-none-eabi-gcc -mcpucortex-m4 -mthumb -c startup.s -o startup.o arm-none-eabi-gcc -mcpucortex-m4 -mthumb -T linker.ld -Wl,--gc-sections \ main.o startup.o -o output.elf arm-none-eabi-objcopy -O binary output.elf output.bin arm-none-eabi-size output.elfarm-none-eabi-size会输出 text、data、bss 三列text 加 data 就是 Flash 占用data 加 bss 就是 RAM 占用。如果 text 超过 128K 或者 databss 超过 20K链接阶段就会报 region overflow这时候要么换芯片要么开优化、删代码。3.3 用 OpenOCD 或 pyOCD 下载验证编译出output.bin之后用调试器下载到芯片。常见做法是 OpenOCD 配合 CMSIS-DAP 或 ST-Link。下面是一个 OpenOCD 命令示例配置文件按实际调试器选。# 启动 OpenOCD 服务接口和芯片配置按实际替换 openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg \ -c program output.elf verify reset exitprogram后面跟 ELF 文件OpenOCD 会自动解析地址并烧写verify做校验reset让芯片复位运行。如果报 “unable to find target”说明 target 配置文件选错如果报 “flash write failed”检查 Flash 起始地址和链接脚本是否一致。下载后如果 LED 不闪先量 PA5 电压有方波说明工具链和代码都没问题没方波就回头查时钟使能和模式寄存器。4. 避坑与排查老版本工具链在 Windows 上的五个血泪经验4.1 现象编译报 “cannot find -lc” 或 “nosys.specs not found”原因解压不完整或者 PATH 指向了错误的bin目录导致 gcc 找不到配套的lib和specs文件。这个工具链的lib在解压目录的arm-none-eabi/lib下不在bin里。解决确认arm-none-eabi-gcc -print-sysroot输出的路径存在并且该路径下有lib目录。如果 sysroot 为空说明 gcc 被移动过位置重新解压到固定目录不要单独把bin拷出来用。4.2 现象链接时报 “undefined reference to _exit” 或 “_sbrk”原因newlib 的桩函数没有实现。裸机环境没有操作系统printf、malloc等函数依赖_write、_sbrk、_exit等系统调用桩。解决在工程里加一个syscalls.c实现空桩函数或者链接时加--specsnosys.specs。我一般直接加-specsnosys.specs -specsnano.specs前者提供空桩后者用 newlib-nano 减小体积。# 在 LDFLAGS 里追加 LDFLAGS -specsnosys.specs -specsnano.specs -u _printf_float-u _printf_float是让 nano 版本支持浮点打印不加的话printf(%f)输出为空。注意这个选项会增加几 KB Flash空间紧张时慎用。4.3 现象程序下载后不运行复位就进 HardFault原因链接脚本里的 Flash 起始地址和芯片实际不符或者中断向量表没有放在最开头。有些芯片有 bootloader 偏移比如从0x08004000开始。解决核对芯片手册的 Flash 映射修改linker.ld里的ORIGIN。同时确认启动文件里.isr_vector段被链接脚本的KEEP保留并且排在.text最前面。用arm-none-eabi-objdump -h output.elf查看段分布确认.isr_vector地址等于 Flash 起始地址。4.4 现象浮点运算结果不对或者进不了浮点中断原因-mfloat-abi和芯片实际浮点单元不匹配。hard表示用硬件 FPU 传参soft表示用整数寄存器传参softfp是折中。如果芯片没有 FPU 却写了hard链接能过但运行会跑飞。解决Cortex-M4F 用-mfpufpv4-sp-d16 -mfloat-abihardCortex-M3 和 M0 去掉这两个选项或者写-mfloat-abisoft。不确定的话先查芯片型号里有没有 “F” 后缀。4.5 现象Makefile 在 Windows 下报 “make: *** No rule to make target”原因路径分隔符和大小写问题。Windows 的 CMD 不区分大小写但 Makefile 里的文件名区分。另外如果用了 Git Bash 的 make路径要用/d/tools/...而不是D:\tools\...。解决统一用正斜杠/工具链路径写成D:/tools/gcc-arm-none-eabi-7-2018-q2-update/bin。如果还是报错用make -d看它到底在找哪个文件通常能发现是某个.o的依赖路径写错了。5. 进阶技巧用这个老版本工具链做固件体积审计与版本锁定5.1 用 size 和 nm 做固件体积审计arm-none-eabi-size只给总量想知道哪个函数占最多 Flash用arm-none-eabi-nm按符号大小排序。# 列出占用最大的 20 个符号 arm-none-eabi-nm --size-sort --print-size output.elf | tail -n 20输出里T表示代码段D表示已初始化数据B表示 bss。如果发现某个printf相关符号占了好几 KB可以考虑换成轻量日志库或者关掉-u _printf_float。我一般会在 CI 里加一条规则如果 text 段比上个版本增加超过 2KB就报警防止不知不觉把 Flash 撑爆。5.2 锁定工具链版本把 zip 和校验和一起归档老版本工具链最大的价值是“不变”。但 zip 文件本身可能被重新打包所以我会在归档时记录 SHA256并在 Makefile 里加一个检查目标。# 计算 zip 的 SHA256 sha256sum gcc-arm-none-eabi-7-2018-q2-update-win32.zip # 输出示例a1b2c3... gcc-arm-none-eabi-7-2018-q2-update-win32.zip把哈希值写进toolchain.sha256在 Makefile 里加check-toolchain: echo 检查工具链版本... $(CC) --version | grep -q 7.3.1 20180622 || \ (echo 工具链版本不匹配请使用 7-2018-q2-update exit 1)这样换电脑或者新人入职时make check-toolchain一跑就知道环境对不对省去“为什么你编译能过我编译不过”的扯皮。这个习惯我坚持了三年每次项目交接至少省半天排查时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站