4. 烧录文件格式:Hex、Bin、ELF 与校验机制

做量产烧录这么多年,我见过太多因为文件格式搞错导致的批量报废。有一次,产线反馈某批次芯片烧录后全部无法启动,排查了一整天,最后发现是烧录工具把 Hex 文件当 Bin 文件解析了——地址偏移全乱,程序直接跑飞。从那以后,我要求团队每个人必须把文件格式的底层逻辑吃透。

这一章,我们就来彻底搞懂 Hex、Bin、ELF 这三种文件格式,以及它们背后的校验机制。你想想看,如果连烧录进去的是什么都不清楚,怎么保证量产质量?

4.1 Hex 文件格式解析

Hex 文件,全称 Intel HEX,是一种用 ASCII 文本表示的二进制数据格式。它最大的特点就是包含了地址信息。我个人习惯把 Hex 文件看作是「带地址的二进制数据快递单」——每个数据包都写清楚了要送到哪个地址。

4.1.1 Hex 文件结构

Hex 文件的每一行都以冒号(:)开头,格式非常固定:

:10010000214601360121470136007EFE09D2190140
:100110002146017E17C20001FF5F16002148011928
:10012000194E79234623965778239EDA3F01B2CAA7
:100130003F0156702B5E712B722B732146013421C7
:00000001FF

每一行由以下几个字段组成:

字段 长度 说明
起始码 1 字节 固定为冒号 :
数据长度 1 字节 本行有效数据的字节数
地址 2 字节 数据在存储器中的起始地址
记录类型 1 字节 00=数据, 01=文件结束, 02=扩展段地址, 04=扩展线性地址
数据 N 字节 实际要烧录的二进制数据
校验和 1 字节 前面所有字节(不含冒号)的补码和

关键点:记录类型 04(扩展线性地址)用于 32 位地址空间。当芯片的 Flash 地址超过 64KB 时,必须用这个类型来指定高 16 位地址。我遇到过有人用老旧的 Hex 解析库,不支持类型 04,结果烧录到 0x08010000 地址的数据全丢了。

4.1.2 校验和计算

Hex 文件的校验和算法很简单:对本行所有字节(从数据长度到数据末尾)求和,取低 8 位,然后用 0x100 减去这个值。

// 以第一行为例:10 01 00 00 21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
// 求和:0x10 + 0x01 + 0x00 + 0x00 + ... + 0x19 + 0x01 = 0x1C0
// 取低8位:0xC0
// 校验和 = 0x100 - 0xC0 = 0x40

实用技巧:量产时,我建议在烧录工具中增加 Hex 文件预校验功能。如果某行校验和错误,直接报错停止,不要等到烧录完才发现数据是坏的。我曾经在某个项目中,因为 Hex 文件在传输过程中被截断,导致最后几行校验和全错,幸亏预校验及时发现了问题。

4.2 Bin 文件格式解析

Bin 文件,就是纯粹的二进制数据,没有任何地址信息。它是最「裸」的烧录文件格式。说白了,Bin 文件就是内存或 Flash 的原始镜像,从起始地址开始,一个字节挨着一个字节。

4.2.1 Bin 文件的特点

  • 无地址信息:烧录时必须由工具或用户指定起始地址
  • 无校验机制:文件本身不包含校验和,需要外部校验
  • 体积最小:没有冗余的文本格式开销,比 Hex 文件小约 30%-50%
  • 解析最快:直接按字节读取,无需解析 ASCII 转二进制

注意:Bin 文件没有地址信息,这意味着如果烧录工具配置的起始地址错了,整个程序都会跑飞。我曾经在产线上看到过,有人把 Bin 文件的起始地址设成了 0x08000000,但实际应该从 0x08001000 开始烧录,结果 Bootloader 被覆盖,芯片直接变砖。

4.2.2 Hex 与 Bin 的对比

对比项 Hex 文件 Bin 文件
包含地址 是 否
文件大小 较大(约 2 倍) 较小
解析速度 较慢 极快
校验机制 内建行校验 无,需外部 CRC
适用场景 开发调试、小批量 大批量量产

4.3 ELF 文件与烧录文件的转换

ELF(Executable and Linkable Format)是编译器生成的中间文件,包含了调试信息、符号表、段信息等。它不能直接用于烧录,必须转换成 Hex 或 Bin。

为什么会这样?因为烧录器只关心「在哪个地址写什么数据」,而 ELF 文件里还包含了「这个变量叫什么名字」「这个函数在源码的第几行」等调试信息。这些信息对芯片运行毫无意义。

4.3.1 转换工具链

我常用的转换工具是 objcopy,它是 GNU 工具链的一部分:

# ELF 转 Bin
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin

# ELF 转 Hex
arm-none-eabi-objcopy -O ihex firmware.elf firmware.hex

# 指定起始地址转 Bin
arm-none-eabi-objcopy -O binary --gap-fill 0xFF firmware.elf firmware.bin

重要参数:--gap-fill 0xFF 用于填充地址空洞。如果代码段之间有未使用的地址区域,objcopy 会用 0xFF 填充。我建议量产时统一使用 0xFF 填充,因为大多数 Flash 在擦除后就是 0xFF,这样烧录速度更快。

4.3.2 转换中的常见问题

  • 地址空洞:如果链接脚本中有未使用的地址区域,Bin 文件会包含这些空洞,导致文件体积异常增大。我曾经见过一个只有 32KB 代码的项目,生成的 Bin 文件却有 1MB,就是因为链接脚本中有一个 0x100000 的空洞。
  • 段对齐:某些架构要求代码段必须按 4 字节或 8 字节对齐,转换时要注意保持对齐。
  • 调试信息丢失:从 ELF 转成 Hex/Bin 后,调试信息就没了。如果需要调试,必须保留 ELF 文件。

4.4 校验和与 CRC 机制

量产烧录中,数据完整性是最基本的要求。校验和和 CRC 就是用来保证「烧进去的数据和文件里的数据完全一致」。

4.4.1 简单校验和

最简单的校验方式,就是把所有数据字节相加,取低 8 位或低 16 位。Hex 文件用的就是这种算法。

uint8_t calc_checksum(uint8_t *data, uint32_t len) {
    uint32_t sum = 0;
    for (uint32_t i = 0; i < len; i++) {
        sum += data[i];
    }
    return (uint8_t)(sum & 0xFF);
}

但这种算法有个致命缺陷:如果数据中同时出现两个字节的错误,且一个增加 1、另一个减少 1,校验和会保持不变。说白了,它只能检测奇数个位的错误。

4.4.2 CRC 校验

CRC(循环冗余校验)就可靠多了。它通过多项式除法生成校验值,能检测出几乎所有常见的错误模式。

量产中最常用的 CRC 算法:

算法 多项式 校验值长度 适用场景
CRC-8 0x07 1 字节 小数据块、简单校验
CRC-16 0x8005 2 字节 中等数据量、通信协议
CRC-32 0x04C11DB7 4 字节 固件完整性校验

我的经验:量产烧录时,我习惯在固件的末尾附加一个 4 字节的 CRC-32 值。芯片上电后,Bootloader 先计算整个固件的 CRC,然后与附加的 CRC 值比对。如果不一致,说明固件损坏,直接进入固件恢复模式。这个机制帮我挡住了至少 3 次因烧录中断导致的批量事故。

4.4.3 CRC 计算示例

uint32_t crc32_hardware(uint8_t *data, uint32_t len) {
    uint32_t crc = 0xFFFFFFFF;
    for (uint32_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 1) {
                crc = (crc >> 1) ^ 0xEDB88320;
            } else {
                crc >>= 1;
            }
        }
    }
    return ~crc;
}

性能注意:软件计算 CRC-32 在低速 MCU 上可能耗时较长。如果芯片有硬件 CRC 模块,一定要用硬件计算。我曾经在某个项目中,用软件 CRC 校验 256KB 的固件,耗时超过 500ms,导致上电启动时间超标。换成硬件 CRC 后,耗时降到 5ms 以内。

4.5 知识体系总览

下面这张图总结了本章的核心知识点和它们之间的关系:

烧录文件格式知识体系 ELF 文件 Hex 文件 Bin 文件 objcopy objcopy 带地址信息 行内校验和 ASCII 文本格式 纯二进制数据 无地址信息 体积最小 简单校验和 CRC 校验 量产烧录 → 推荐 Bin + 外部 CRC-32 校验

从这张图可以看得很清楚:ELF 是源头,通过 objcopy 转换成 Hex 或 Bin。Hex 自带地址和校验,适合开发调试;Bin 体积小、解析快,适合量产。而无论哪种格式,最终都需要通过校验和或 CRC 来保证数据完整性。

我个人在量产项目中,始终遵循一个原则:用 Bin 文件烧录,用 CRC-32 校验,用硬件加速计算。这个组合在速度和可靠性之间取得了最好的平衡。