4、烧录文件格式:Hex、S19、Bin、ELF 格式详解
做量产烧录这么多年,我见过太多工程师在文件格式上栽跟头了。有人拿着 ELF 文件直接往烧录器里塞,结果烧进去跑不起来;有人把 Hex 和 Bin 混为一谈,校验和怎么都对不上。说白了,选错格式,后面所有流程都是白搭。
今天咱们就把这四种格式彻底聊透。你搞清楚它们的区别,量产时就能少踩一半的坑。
4.1 四种格式的核心差异
先看一张总览表,心里有个底:
| 格式 | 全称 | 包含信息 | 是否可执行 | 量产常用度 |
|---|---|---|---|---|
| Hex | Intel HEX | 地址 + 数据 + 校验和 | 否 | ★★★★★ |
| S19 | Motorola S-record | 地址 + 数据 + 校验和 | 否 | ★★★★☆ |
| Bin | Binary | 纯二进制数据 | 否 | ★★★★★ |
| ELF | Executable and Linkable Format | 代码 + 数据 + 符号表 + 调试信息 + 重定位表 | 是 | ★☆☆☆☆ |
关键认知:量产烧录只看 地址 + 数据 + 校验。ELF 里那些符号表、调试信息,烧录器根本用不上,反而容易出问题。
4.2 Intel HEX 格式详解
Hex 是我个人最推荐的量产格式。它用 ASCII 文本表示二进制数据,每行都带地址和校验和,传输过程中出错了能及时发现。
一行典型的 Hex 记录长这样:
:10000000 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF F0
| | |_______________________________________________| |
| | 数据区(16字节) |
| 记录类型(00=数据) |
起始码 校验和
Hex 有几种记录类型,量产时最常用的是:
- 00 — 数据记录,烧录的主体
- 01 — 文件结束记录,告诉烧录器「到此为止」
- 04 — 扩展线性地址记录,处理 32 位地址时必用
- 05 — 启动地址记录,有些 MCU 需要它指定入口
我的经验:有一次客户给的 Hex 文件缺少 04 记录,烧录器默认从 0x0000 开始写,结果程序跑飞了。后来我养成了习惯——量产前先用工具检查 Hex 文件头,确保扩展地址记录完整。
4.3 Motorola S-record (S19) 格式
S19 和 Hex 本质上是同一类东西,都是文本格式、带地址、带校验。区别在于行首标识和校验算法。
S19 的行结构:
S1 13 0000 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF F0 7E
| | | |_______________________________________________| |
| | 地址长度(19字节) |
| 记录类型(S1=16位地址数据) |
记录类型标识 校验和
S19 的常见记录类型:
- S0 — 文件头,通常包含文件名或注释
- S1 — 16 位地址数据记录
- S2 — 24 位地址数据记录
- S3 — 32 位地址数据记录
- S5 — 记录计数
- S7/S8/S9 — 结束记录
我个人的习惯是:用 NXP/Freescale 的芯片时优先选 S19,因为他们的工具链对 S19 支持最好。用 ST/Infineon 的芯片,Hex 更通用。
4.4 Binary (Bin) 格式
Bin 格式最简单,也最「暴力」。它没有地址信息,没有校验和,就是纯粹的二进制数据流。
举个例子:如果你要烧录 64KB 的 Flash,Bin 文件就是 65536 个字节,从 0x0000 到 0xFFFF 依次排列。烧录器必须知道起始地址,否则数据全错位。
注意:Bin 文件不包含地址信息,所以量产时一定要在烧录工具里手动指定起始地址。我见过有人把 Bin 烧到 0x08000000 的 STM32 上,结果忘记改地址,程序直接写到了 0x00000000 的系统区——板子直接变砖。
Bin 格式的优势在于:
- 文件体积最小,传输快
- 解析简单,烧录器处理速度最快
- 适合大容量 Flash 的批量烧录
缺点是:一旦数据中间有空洞(比如地址不连续),Bin 文件会变得很大,因为空洞也要用 0xFF 填充。
4.5 ELF 格式——为什么量产不用它?
ELF 是编译器直接输出的格式,包含的信息非常丰富:
- 代码段(.text)
- 数据段(.data、.bss)
- 符号表(函数名、变量名)
- 调试信息(DWARF)
- 重定位表
听起来很强大对吧?但量产烧录时,这些信息反而是累赘。
原因有三:
- 烧录器只认地址和数据,ELF 里的符号表、调试信息它看不懂
- ELF 文件可能包含多个段,烧录器需要解析段表,容易出错
- 安全风险——ELF 暴露了函数名和变量名,别人拿到文件就能逆向你的代码结构
我的建议:开发阶段用 ELF 调试没问题。但到了量产环节,一定要把 ELF 转换成 Hex 或 Bin。这是量产安全的第一道防线。
4.6 如何选择正确的格式?
这个问题没有标准答案,但我可以给你一套决策逻辑:
简单总结一下我的选择原则:
- Hex — 通用性最强,90% 的烧录器和芯片都支持
- S19 — NXP/Freescale 生态首选,地址范围更灵活
- Bin — 追求速度或芯片只支持裸数据时用,但要小心地址
- ELF — 量产时别碰,除非你清楚自己在做什么
4.7 格式转换工具
实际项目中,我们经常需要在不同格式之间来回转换。下面是我常用的工具:
| 工具 | 支持转换 | 平台 | 我的评价 |
|---|---|---|---|
| objcopy (GNU) | ELF ↔ Hex/S19/Bin | Linux/Windows | 最强大,量产脚本首选 |
| srec_cat | Hex ↔ S19 ↔ Bin | 跨平台 | 格式转换瑞士军刀 |
| J-Flash | ELF/Hex/S19/Bin 互转 | Windows | 图形界面,适合调试 |
| STM32CubeProgrammer | ELF → Hex/Bin | Windows/Linux | ST 芯片专用,稳定 |
我最常用的是 objcopy,因为它可以集成到自动化脚本里。举个例子:
# ELF 转 Hex(保留地址信息)
arm-none-eabi-objcopy -O ihex firmware.elf firmware.hex
# ELF 转 Bin(纯二进制,需指定起始地址)
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
# Hex 转 Bin
arm-none-eabi-objcopy -I ihex -O binary firmware.hex firmware.bin
小技巧:用 objcopy 转 Bin 时,默认从 0x0000 开始。如果你的芯片 Flash 起始地址不是 0x0000(比如 STM32 是 0x08000000),记得加 --change-addresses 参数调整偏移。
4.8 量产前的格式验证清单
每次量产前,我都会过一遍这个清单,确保格式没问题:
- 校验和验证 — Hex/S19 每行都有校验和,用工具重新算一遍,确认没被篡改
- 地址范围检查 — 确认文件里的地址没有超出芯片 Flash 范围
- 空洞检查 — 检查数据是否有不连续的空洞,避免烧录器解析出错
- 文件完整性 — 确认文件有正确的结束记录(Hex 的 :00000001FF,S19 的 S7/S8/S9)
- 烧录后回读比对 — 烧完读出来和原始文件做 CRC 比对,这是最终防线
我曾经踩过的坑:有一次量产 5000 片,Hex 文件在传输过程中被截断了最后一行(结束记录)。烧录器没报错,但烧出来的芯片上电后随机死机。从那以后,我强制要求所有 Hex 文件在烧录前必须通过校验和验证,少一行都不行。
嗯,格式这块就聊到这儿。你只要记住:量产用 Hex,调试用 ELF,Bin 要小心,S19 看平台。下次咱们聊烧录协议和通信接口,那又是另一片天地了。