| 上一篇 | 下一篇 |
|---|---|
| #define 和 typedef 的区别 |
目 录
- [编译文件( .bin 和 .hex ,包括 .elf 和 .axf )](#编译文件( .bin 和 .hex ,包括 .elf 和 .axf ))
-
- [1)编译的交付文件:.hex 与 .bin 的区别](#1)编译的交付文件:.hex 与 .bin 的区别)
-
- [① 两种文件的本质](#① 两种文件的本质)
- [② 为什么 .bin 必须额外指定起始地址](#② 为什么 .bin 必须额外指定起始地址)
- [③ 表格对比](#③ 表格对比)
- [2)编译的原始产物:.elf 和 .axf](#2)编译的原始产物:.elf 和 .axf)
- [3)Keil LOAD / J-Flash / ... 烧录的是哪个文件](#3)Keil LOAD / J-Flash / ... 烧录的是哪个文件)
编译文件( .bin 和 .hex ,包括 .elf 和 .axf )
这四种格式的文件都是程序编译的结果,只不过程序编译的原始产物是 .elf 和 .axf ,而 .bin 和 .hex 是经过处理的
1)编译的交付文件:.hex 与 .bin 的区别
① 两种文件的本质
-
.bin 文件 是最朴素的格式:它就是程序在 Flash 里逐字节的二进制镜像,没有任何额外信息。文件里第 0 个字节就对应芯片 Flash 起始位置的第 0 个字节,第 N 个字节就是第 N 个字节,仅此而已。它不知道自己应该被烧到哪里,也不知道自己有多大段、跳过多少空洞。
-
.hex 文件 (特指 Intel HEX 格式)则是一种带地址信息的 ASCII 文本格式。文件内容是一行行的记录,每行长这样:
:10010000214601360121470136007EFE09D2190140其中
:是起始符,10是本行数据长度(16 字节),0100是本行数据的目标地址,00是记录类型(数据记录),后面跟着 16 字节数据,最后是校验和。还有专门的"扩展线性地址记录"用来表达高 16 位地址,从而支持 4GB 寻址空间。换句话说,hex 文件自带地址信息,而且可以表达不连续的地址段(比如代码在 0x08000000、配置数据在 0x0801F000,中间的空洞不会浪费文件体积)。
② 为什么 .bin 必须额外指定起始地址
原因就在于上面的对比里:bin 文件丢掉了地址信息。烧录器(JFlash、ST-Link Utility 等)拿到一个 bin 文件时,它面对的只是一串字节,根本不知道该把这串字节写到 Flash 的哪个物理地址。是写到 0x08000000(STM32 主 Flash 起点)?还是 0x08020000(某个 Bootloader 后面的 App 区)?还是 0x1FFF0000(系统存储区)?文件本身没有任何线索。
所以使用 bin 文件烧录时,必须在烧录工具里手动告诉它起始地址,烧录器才能把第 0 字节对齐到正确的位置往后写。而 hex 文件不需要这一步------打开 JFlash 加载 hex,地址信息已经在文件里了,工具直接按记录里的地址写就行。
注意:------------------
当使用 bin 烧录单片机的时候,一个常见场景是 Bootloader + App 的双区设计 :比如说 App 链接脚本里把起始地址设成 0x08010000,编译出来的 hex 自动带这个地址,烧录直接就位;但如果是 bin,必须记住 "这个 bin 是 App 的,要烧到 0x08010000",否则就会把 App 烧到 Bootloader 区把 Boot 覆盖掉。
③ 表格对比
| 维度 | .bin | .hex |
|---|---|---|
| 文件大小 | 小(纯二进制) | 大约是 bin 的 2~3 倍(ASCII 编码) |
| 是否含地址 | 否 | 是 |
| 是否含校验 | 否 | 每行有校验和 |
| 是否支持空洞 | 否(空洞要填充) | 是(可表达不连续段) |
| 烧录易用性 | 必须手动指定地址 | 直接烧 |
| 适用场景 | OTA 升级、量产烧写器、Bootloader 接收 | 开发调试、JFlash/JLink 烧录 |
bin 的优势在"小而纯":OTA 升级时通过串口、网络传输,bin 体积小、解析简单,固件接收端拿到字节流直接往 Flash 里刷就行;量产烧录机也偏好 bin,因为速度快、流程简单。hex 的优势在"自描述":开发调试时谁也不想每次都去查地址,hex 拖进 JFlash 直接点烧录就完事了。
2)编译的原始产物:.elf 和 .axf
编译器真正直接生成的其实是 ELF (GCC)或 AXF(Keil)文件,这俩才是"信息最全"的格式:除了代码和地址,还包含调试符号、段表、符号表等。J-Link 的 JLinkExe、OpenOCD 都能直接烧 elf。
但 .elf/.axf 体积大、格式复杂,不适合单纯的烧录场景,所以实际烧写 Flash 时,几乎所有人都是用从它转出来的 .hex 或 .bin。工具链在编译完成后会自动帮你转(Keil 在 Output 选项卡勾一下就行,Makefile 工程加一行 objcopy 命令),整个过程是透明的。
比如用 objcopy 从 elf 转出 hex 和 bin :
bash
arm-none-eabi-objcopy -O ihex app.elf app.hex
arm-none-eabi-objcopy -O binary app.elf app.bin
转成 hex 时地址信息保留下来了,转成 bin 时地址信息就被丢掉了------这正是为什么 bin 烧录需要手动补回地址的根本原因。
3)Keil LOAD / J-Flash / ... 烧录的是哪个文件
Keil 里点 LOAD 时下载的具体格式,取决于在 Options for Target → Utilities → Settings 里配置的下载算法和工具,但本质上 Keil 并不是把 .hex 文件"传"给单片机,Keil 编译链接后,Keil 默认生成的是 .axf(ARM Executable Format,本质是 ELF 文件),里面既包含可执行的机器码段(代码、常量、初始化数据),也包含调试信息、符号表、段地址等元数据。
点 LOAD 时,Keil 调用的是调试器驱动(比如 ST-Link、J-Link、CMSIS-DAP 的 Flash 编程算法),它会:
- 从
.axf里解析出各个 LOAD 段(代码段、只读数据段、已初始化数据段的初值),以及它们应该被烧到的物理地址; - 把这些二进制数据通过调试器(SWD/JTAG)按页写进 MCU 的片内 Flash;
- 顺便把
.axf里的调试符号留在 PC 端,供单步调试、查看变量用。
所以严格说,下载到单片机里的既不是 .axf 也不是 .hex,而是从 .axf 中抽取出来的纯二进制机器码 (相当于 .bin 的内容),按地址写进 Flash。.hex 只是同样这份二进制数据的另一种带地址的文本封装格式而已。
.hex 文件什么时候才有用呢?当你想脱离 Keil、用别的工具(比如 ST-Link Utility、J-Flash、量产烧录器)去烧写时,就需要在 Options for Target → Output 里勾上 Create HEX File ,让 Keil 额外生成一份 .hex 给那些工具用。Keil 自己 LOAD 的时候并不依赖它。
总结:
- Keil LOAD 时用的是
.axf,烧进 Flash 的是从.axf抽出来的机器码二进制;.hex是给外部烧录工具准备的可选副产品。 - JFlash 一般是直接将 .bin 文件烧录到 mcu 的 flash 中。