4. HEX文件格式详解:Intel HEX记录类型、数据对齐与校验

HEX文件,做ECU刷写的没人不认识它。说白了,它就是一段段文本,描述了要把什么数据写到芯片的哪个地址去。我最早接触这玩意儿的时候,觉得它挺简单,不就是几行ASCII码嘛。后来踩了几个坑才发现,里面门道不少。

今天咱们就把Intel HEX格式掰开揉碎了讲。你想想看,刷写失败、校验不过,很多时候就是HEX文件本身有问题。搞懂了它,你才算真正入了ECU刷写的门。

4.1 HEX文件的骨架:记录类型

一个标准的HEX文件,每一行都是一条“记录”。每条记录长这样:

:10010000214601360121470136007EFE09D2190140

我习惯把它拆成五段来看:

  • 冒号(:):每行的起始标记,没它就不算数
  • 长度(1字节):表示后面数据域有多少个字节。比如上面的10,就是16个字节
  • 地址(2字节):这条数据要写入的起始地址。注意,这只是偏移地址
  • 记录类型(1字节):核心中的核心,决定了这行数据怎么解释
  • 数据域(N字节):真正的刷写数据
  • 校验和(1字节):整条记录的“身份证”,算不对就报错

嗯,这里要注意:地址字段只有16位,最大只能表示64KB的范围。那ECU的Flash动不动就几兆甚至几十兆,怎么办?别急,后面有专门解决这个问题的记录类型。

4.2 五种记录类型,一个都不能少

Intel HEX定义了6种记录类型,但咱们做ECU刷写,真正打交道的就5种。我列个表,你一看就明白:

类型码 名称 说明 ECU刷写中的用途
00 Data Record 数据记录,最常用的类型 存放实际的刷写数据
01 End of File Record 文件结束记录 告诉刷写工具:文件读完了
02 Extended Segment Address 扩展段地址记录 用于8086模式的地址扩展,现在用得少
03 Start Segment Address 起始段地址记录 指定代码的起始执行地址
04 Extended Linear Address 扩展线性地址记录 最常用的地址扩展方式,32位地址全靠它
05 Start Linear Address 起始线性地址记录 指定32位模式的起始执行地址

我个人最常用的是00、01和04。02和03在老的MCU上还能见到,现在的新项目基本都转04和05了。

4.2.1 Data Record(类型00)—— 真正的“干货”

这就是装数据的那条记录。每条最多装255个字节,但实际工具生成时,一般每行只放16个或32个字节。为什么?因为太长了不好读,也容易出错。

举个例子:

:10010000214601360121470136007EFE09D2190140

这条记录的意思是:从地址0x0100开始,写入16个字节的数据。数据内容就是21 46 01 36 ... 19 01。最后的40是校验和。

我在项目中遇到过一件事:有同事手动修改HEX文件,把数据改对了,但忘了重新算校验和。结果刷写工具直接报错,死活刷不进去。折腾了半天才发现是校验和不对。所以啊,改HEX文件一定要用专业工具,别手欠。

4.2.2 End of File Record(类型01)—— 刷写的“句号”

这个最简单,就一行:

:00000001FF

长度是0,地址是0,类型是01,校验和是FF。它告诉刷写工具:文件结束了,后面没内容了。没有这一行,有些刷写工具会一直等,或者直接报“文件不完整”。

4.2.3 Extended Linear Address(类型04)—— 搞定大地址

这是咱们最常用的地址扩展方式。它的原理很简单:用两个字节来指定高16位地址。

看个例子:

:020000040800F2

这条记录的意思是:从这条记录开始,后面的所有Data Record的地址,都要加上0x0800作为高16位。也就是说,如果后面有一条Data Record的地址是0x1000,那实际物理地址就是0x08001000。

为什么会这样?因为Data Record里的地址只有16位,最大只能到0xFFFF。而咱们ECU的Flash地址,比如STM32的从0x08000000开始,光靠16位地址根本不够用。所以需要用类型04来“抬高”基地址。

我建议你在解析HEX文件时,维护一个“当前基地址”变量。每遇到一条类型04记录,就更新这个变量。后面所有的Data Record,都用“基地址 + 偏移地址”来算实际地址。

4.2.4 Start Linear Address(类型05)—— 程序从哪开始跑

这个记录指定了程序的入口地址。对于ECU来说,就是复位向量。比如:

:0400000508000001F2

表示程序的起始地址是0x08000001。注意,有些编译器生成的HEX文件会带这个,有些不会。刷写工具一般会忽略它,因为ECU的启动地址是由芯片的硬件机制决定的,不是HEX文件说了算。

4.3 数据对齐:别让地址“错位”

做ECU刷写,数据对齐是个容易忽略但很重要的问题。我见过有人刷写失败,就是因为数据没有按4字节对齐。

什么叫对齐?简单说,就是数据的起始地址,最好是4的倍数。比如0x08000000、0x08000004、0x08000008这些。为什么?因为MCU的Flash控制器,一次读取就是4字节或8字节。地址不对齐,读写效率会下降,甚至直接报错。

我个人的习惯是:

  • 每条Data Record的数据长度,尽量是4的倍数
  • 每条Data Record的起始地址,尽量是4的倍数
  • 如果遇到地址不对齐的情况,用0xFF填充补齐

举个例子,如果你的数据从0x08000001开始,只有3个字节。那我会建议你:

  • 从0x08000000开始,写4个字节,第一个字节填0xFF,后面三个是真实数据
  • 或者从0x08000001开始,写3个字节,但后面再补一个0xFF凑成4字节

具体怎么处理,取决于你的刷写工具和MCU的要求。但记住一条原则:不要留“尾巴”。地址不对齐的尾巴,最容易出问题。

4.4 校验和:HEX文件的“自检”机制

校验和的计算方法很简单:

校验和 = 0x100 - (长度 + 地址高字节 + 地址低字节 + 类型 + 所有数据字节) 的总和 & 0xFF

说白了,就是把整条记录(不包括冒号)的所有字节加起来,取低8位,然后用0x100减去这个值。如果结果是0x100,那就取0x00。

我写个伪代码你感受一下:

uint8_t calc_checksum(uint8_t* record, int len) {
    uint8_t sum = 0;
    for (int i = 0; i < len; i++) {
        sum += record[i];
    }
    return (uint8_t)(0x100 - sum);
}

注意,这里的record是从长度字节开始算的,不包括冒号。校验和算出来之后,如果和记录里的校验和字节不一致,那这条记录就是坏的。

我曾经踩过的坑:有一次刷写ECU,总是刷到一半就失败。排查了很久,发现是HEX文件里有一条记录的校验和算错了。工具读到那条记录时直接跳过,导致数据不完整。从那以后,我每次生成HEX文件都会用工具做一次全文件校验。

4.5 知识体系:一张图看懂HEX文件

下面这张图,是我自己总结的HEX文件解析流程。你照着这个思路走,基本不会出错:

HEX文件解析流程 读取一行HEX记录 解析:长度、地址、类型、数据 判断记录类型 类型00:数据记录 类型01:文件结束 类型04:扩展地址 计算并验证校验和 处理下一条记录

这张图的核心逻辑就是:读一行,解析,判断类型,处理数据,校验,然后继续下一行。直到遇到类型01,结束。

4.6 实操中的几个小技巧

我的个人习惯:在解析HEX文件时,我会先扫描一遍整个文件,把所有类型04记录提取出来,建立一个“地址段映射表”。这样在后续处理Data Record时,可以快速知道当前数据属于哪个地址段,效率高很多。

重要提醒:HEX文件里的地址是逻辑地址,不是物理地址。比如STM32的Flash物理地址从0x08000000开始,但HEX文件里可能写的是0x08000000,也可能写的是0x00000000加上类型04的0x0800。具体怎么映射,要看芯片的存储器映射和编译器的设置。

嗯,关于HEX文件格式,今天就讲这么多。你只要记住:记录类型决定了怎么解释数据,数据对齐决定了刷写是否顺利,校验和决定了文件是否完整。把这三点搞透了,HEX文件在你手里就是透明的。


可可精品资料库