本篇总结
介绍 Keil MDK 的编译流程(预处理/编译/汇编/链接)及各阶段生成的文件类型。
🔑 KEIL MDK
理解编译过程和文件类型有助于调试链接错误、优化代码体积和定位问题。本文详细讲解 MDK 的编译流程(预处理 → 编译 → 汇编 → 链接)以及各阶段产生的文件类型。
编译流程概述
MDK 的编译过程分为四个阶段:预处理(Preprocess)→ 编译(Compile)→ 汇编(Assemble)→ 链接(Link)。源文件(.c/.s)经过前三个阶段生成目标文件(.o),最后链接器将所有目标文件和库文件链接为可执行文件(.axf),再通过 fromelf 工具转换为 HEX 或 BIN 文件用于下载。
预处理(Preprocess)
预处理器处理源文件中的预处理指令(#include、#define、#ifdef 等),展开宏定义和头文件,条件编译选择代码分支,生成纯 C 代码。预处理后的文件通常以 .i 为扩展名。
常用预处理指令:
- #include:包含头文件
- #define:定义宏
- #ifdef / #ifndef:条件编译
- #pragma:编译器特定指令
编译(Compile)
编译器将预处理后的 C 代码翻译为汇编代码(.s 文件)。此阶段进行语法检查、语义分析、代码优化和中间代码生成。编译器会检查类型安全、未使用变量、潜在错误等,并生成警告和错误信息。
MDK 使用的编译器:
- ARMCC(ARM Compiler 5):传统编译器,兼容性好
- armclang(ARM Compiler 6):基于 LLVM,优化能力更强
优化级别:
- -O0:不优化(调试推荐)
- -O1:基本优化
- -O2:标准优化
- -O3:最大优化(发布推荐)
汇编(Assemble)
汇编器将汇编代码(.s 文件)转换为机器码,生成目标文件(.o 文件)。目标文件包含机器指令、数据、符号表和重定位信息,但地址尚未最终确定。
对于 .s 汇编源文件(如启动文件 startup_w55mh32.s),直接进入汇编阶段,无需编译。启动文件包含中断向量表、堆栈初始化和复位处理函数。
链接(Link)
链接器将所有目标文件(.o)和库文件(.lib)合并,解析符号引用,分配最终地址,生成可执行文件(.axf)。链接过程由分散加载文件(Scatter File,.sct)控制内存布局。
链接器主要任务:
- 符号解析:匹配函数和变量的定义与引用
- 地址分配:根据 .sct 文件将代码和数据分配到 Flash/SRAM
- 重定位:修正跳转和访问地址
- 生成输出:产生 .axf 可执行文件和 Map 文件
文件类型说明
| 扩展名 | 类型 | 说明 |
| .c | C 源文件 | 用户编写的 C 语言源代码 |
| .h | 头文件 | 函数声明、宏定义、类型定义 |
| .s | 汇编源文件 | 启动文件、中断向量表、底层汇编代码 |
| .i | 预处理文件 | 预处理后的 C 代码(中间产物) |
| .o | 目标文件 | 编译/汇编后的机器码,地址未确定 |
| .lib / .a | 库文件 | 多个目标文件的归档集合 |
| .axf | 可执行文件 | ELF 格式,含调试信息,用于仿真调试 |
| .hex | HEX 文件 | Intel HEX 格式,含地址信息,用于下载 |
| .bin | BIN 文件 | 纯二进制,无地址信息,需指定下载地址 |
| .map | Map 文件 | 内存映射,含符号地址、大小、段分布 |
| .sct | 分散加载文件 | 定义 Flash/SRAM 内存布局和段分配 |
| .uvprojx | 工程文件 | MDK5 工程配置,含编译选项、文件列表 |
HEX 与 BIN 文件的区别
HEX 文件是 Intel HEX 格式的文本文件,每行包含地址、数据和校验信息,可直接下载到指定地址。HEX 文件不连续的内存区域(如 Flash 和 SRAM)可分开描述。
BIN 文件是纯二进制文件,只有数据没有地址信息,下载时需指定起始地址。BIN 文件体积比 HEX 小,但不包含地址映射。
MDK 默认生成 .axf 文件后,通过 fromelf 工具转换为 .hex 文件。如需生成 .bin 文件,可在 Options → User → After Build 中添加 fromelf 命令:
fromelf --bin --output output.bin output.axf
常见问题
Q: 编译报错 "Error: L6218E: Undefined symbol"
链接器找不到函数或变量的定义。检查是否遗漏了 .c 源文件未添加到工程中,或函数声明与定义不匹配。查看 Map 文件确认符号是否存在。
Q: 编译通过但下载后程序不运行
检查启动文件(startup_w55mh32.s)是否正确包含。确认分散加载文件(.sct)的 Flash 起始地址和大小与芯片一致。查看 Map 文件确认 Reset_Handler 地址是否在向量表的正确位置。
Q: 如何减小代码体积
提高优化级别(Options → C/C++ → Optimization 设为 Level 3)。勾选 "One ELF Section per Function" 使链接器只包含实际调用的函数。使用 MicroLIB 替代标准 C 库(Options → Target → Use MicroLIB)。
Q: HEX 文件和 BIN 文件应该用哪个下载
HEX 文件包含地址信息,适合通过 J-Link/ST-Link 等调试器下载。BIN 文件体积更小,适合通过 Bootloader 或串口 ISP 下载。MDK 默认使用 HEX/AXF 文件通过调试器下载。
总结
本文详细讲解了 Keil MDK 的编译过程和文件类型:
- 编译流程:预处理(#include/#define)→ 编译(C→汇编)→ 汇编(汇编→机器码)→ 链接(合并目标文件→可执行文件)
- 源文件:.c(C源码)、.h(头文件)、.s(汇编/启动文件)
- 中间产物:.i(预处理后)、.o(目标文件,地址未确定)
- 最终产物:.axf(含调试信息的可执行文件)、.hex(Intel HEX,含地址信息)、.bin(纯二进制)
- 辅助文件:.map(内存映射)、.sct(分散加载,定义内存布局)、.uvprojx(工程配置)