资讯动态

固件BIN与HEX互转合并:地址信息内化与外置的工程实践

发布时间:2026/10/19 3:58:30 来源:尧图企业网站定制
简介这是一款面向嵌入式开发者的BIN与HEX文件互转合并工具基于MFC对话框程序实现参考了公开的BIN转HEX代码并加以扩展适合需要处理单片机固件格式转换的工程师与学习者使用。资源包共22个文件包含5个h头文件、4个cpp源文件、2个exe可执行程序以及rc、dsw、dsp等工程配置与资源文件压缩包约654KB可直接运行或自行编译修改。工具支持BIN转HEX、HEX转BIN以及HEX文件合并三类操作并针对实际使用中的问题持续修复转HEX时A~F字符统一为大写以避免Keil使用出错解决了bin文件超过64k数据错乱的问题还修复了C8051所用Keil编译的hex文件转换到bin时的错误。目前已有3664人学习下载适合需要快速完成固件格式转换、合并或排查转换异常的开发者参考使用。1. 固件格式的巴别塔为什么 BIN 和 HEX 明明描述同一块 Flash却必须互转你手上有一份从芯片里读出来的固件扩展名是.bin用十六进制编辑器打开是一堆看不出结构的二进制字节同事发来的另一份是.hex用文本编辑器打开却是一行行:10000000...的 ASCII 记录。两者描述的很可能是同一块 Flash 里的同一段程序但直接diff会告诉你它们毫无关系。这不是玄学而是两种格式对地址和空洞的建模方式根本不同BIN 是纯字节流地址靠烧录时的起始偏移隐含约定HEX 是带地址字段的记录集合每一行自带目标地址还能表达这段没数据的间隙。搞懂互转和合并的底层机制本质上是搞懂地址信息到底存在哪里——这也是为什么合并两个 BIN 时最容易翻车而合并两个 HEX 反而相对安全。下面从格式本身拆起。2. 格式解剖BIN 的裸和 HEX 的自描述差在哪2.1 BIN 为什么没有地址地址去哪了BIN 文件就是一段连续的字节序列文件偏移 0 对应某个基地址但这个基地址不存在文件里。烧录工具在写 Flash 时要么用命令行参数指定起始地址要么按芯片的默认映射比如从0x08000000开始来放。这意味着同一个 BIN 文件配不同的起始地址烧进去运行结果完全不同——地址信息被外置到了烧录配置里。常见的实现思路是BIN 只负责内容位置由外部上下文提供。这带来两个直接后果。第一BIN 无法表达不连续的地址空间如果程序在0x08000000有一段、在0x08010000又有一段中间隔了 64KB 空洞BIN 只能把空洞用0xFF或0x00填满文件体积白白膨胀。第二两个 BIN 合并时你必须自己知道各自的起始地址否则拼接出来的东西地址全错。# 查看 BIN 大小推断它可能覆盖的地址范围 ls -l firmware.bin # 假设大小 0x8000 字节起始 0x08000000则覆盖到 0x08007FFF这里ls -l给出的字节数就是地址跨度的下限前提是没有被填充的空洞。如果实际有效数据只有 4KB 却生成了 32KB 的 BIN多半是中间有大段填充。2.2 HEX 的记录结构每一行都是一个带地址的数据包HEXIntel HEX是纯 ASCII 文本每行以:开头后面是十六进制字符。一行的字段顺序是数据长度、地址、记录类型、数据、校验和。记录类型决定了这行是数据还是元信息记录类型含义关键作用00数据记录携带实际字节和 16 位地址01文件结束标记 HEX 结束02扩展段地址提供高位地址段基址 × 1604扩展线性地址提供高 16 位地址拼成 32 位03/05起始地址指定执行入口不参与数据搬运关键在04记录它把地址的高 16 位设进去后续00数据记录里的 16 位地址就作为低 16 位两者拼成完整 32 位地址。这就是 HEX 能表达任意地址、还能跳过空洞的原因——它不需要连续每段数据自带坐标。:020000040800F2 ← 类型04设置高16位地址为0x0800 :1000000012345678... ← 类型00低16位0x0000实际地址0x08000000 :00000001FF ← 类型01文件结束第一行的02是数据长度2 字节0000是这条记录自身的地址对类型 04 无意义04是记录类型0800是高 16 位地址F2是校验和。第二行10表示 16 字节数据0000是低 16 位地址。两行合起来数据落在0x08000000。校验和是前面所有字节取反加一用来发现传输或编辑错误。2.3 互转的本质地址信息的注入与剥离BIN 转 HEX是给裸字节流注入地址选定一个基地址按每行最多 16 或 32 字节切块逐块生成04记录当地址跨越 64KB 边界时和00记录最后补01结束行。HEX 转 BIN是剥离地址解析所有00记录按地址排序找到最小和最大地址中间没数据的地方填0xFF输出连续字节流。这里有个容易忽略的点HEX 转 BIN 时如果地址不连续填充字节的选择会影响后续校验和比对0xFF是 Flash 擦除后的默认值通常比0x00更安全。3. 合并的两种语义拼接和叠加选错了直接覆盖3.1 拼接合并地址相邻时的简单串联拼接适用于两段数据地址首尾相接比如 Bootloader 在0x08000000到0x08007FFFApplication 从0x08008000开始。合并逻辑是确认第一段的结束地址等于第二段的起始地址然后把字节流直接接上。BIN 拼接就是cat boot.bin app.bin full.bin前提是 boot.bin 的起始地址和大小正好让 app.bin 落在正确位置。# 拼接两个 BIN需已知各自起始地址和大小 BOOT_ADDR 0x08000000 APP_ADDR 0x08008000 with open(boot.bin, rb) as f: boot f.read() with open(app.bin, rb) as f: app f.read() # 校验boot 的结束地址必须等于 app 的起始地址 assert BOOT_ADDR len(boot) APP_ADDR, 地址不连续不能直接拼接 with open(full.bin, wb) as f: f.write(boot app)assert那行是血泪经验如果 boot.bin 实际大小是0x9000超过了0x8000直接拼接会让 app 的数据被顶到错误地址烧进去后程序跑飞。参数BOOT_ADDR和APP_ADDR必须来自链接脚本或芯片手册不能凭感觉填。3.2 叠加合并地址重叠时的覆盖规则叠加适用于两段数据地址有重叠比如一份是出厂固件一份是补丁补丁要覆盖出厂固件的某一段。这时不能简单拼接而要按地址逐字节写入后写的覆盖先写的。HEX 合并天然适合这种场景因为每条记录自带地址合并工具可以按地址排序后处理重叠。def merge_hex_records(records_a, records_b): 按地址叠加b 覆盖 a 的重叠部分 mem {} for addr, byte in records_a: mem[addr] byte for addr, byte in records_b: mem[addr] byte # 后写覆盖 return sorted(mem.items())这段伪代码展示了叠加的核心用地址做键的字典天然去重且后写覆盖。实际工具里还要处理04记录切换地址段、校验和重算。参数上覆盖顺序由调用方决定通常补丁在后。3.3 为什么 BIN 合并更容易翻车BIN 没有地址字段合并工具只能靠外部传入的地址参数。一旦参数错了工具不会报错只会默默生成一个地址错位的文件。而 HEX 合并时工具能读到每行的地址发现重叠或间隙时可以给出警告。这就是自描述格式的价值把地址信息内化到文件里减少对外部约定的依赖。常见的实现思路是合并 BIN 前先用objdump或链接脚本的 map 文件确认各段地址再写进合并脚本的常量里。4. 避坑与排查合并后烧录不启动先查这五处4.1 现象合并后文件变大很多烧录后程序不跑原因通常是 HEX 转 BIN 时把大段空洞填了0xFF或者合并时地址算错导致数据被顶到高地址。解决用十六进制编辑器打开合并结果跳到预期起始地址看开头几个字节是不是预期的中断向量表通常是栈顶地址和复位向量。如果不是说明地址偏移错了。4.2 现象两个 HEX 合并后校验和报错原因是合并工具没有重算每行的校验和或者04扩展地址记录没有随数据地址更新。解决合并后必须重新生成每一行的校验和不能直接拼接原始文本行。校验和算法是把该行除:和校验和本身外的所有十六进制字节相加取低 8 位再用0x100减去该值。4.3 现象BIN 合并后中间出现一段0x00但源文件里没有原因是拼接时两个文件地址不连续工具自动填充了间隙而填充值选了0x00。Flash 擦除后是0xFF写入0x00会改变未使用区域的状态某些芯片的选项字节或校验区可能因此异常。解决填充值统一用0xFF并在合并脚本里显式指定。4.4 现象HEX 转 BIN 后文件比预期小原因是 HEX 里存在地址回退或乱序转换工具按最小到最大地址输出但中间某些段被后续记录覆盖后丢失。解决转换前先按地址排序所有00记录检查是否有地址重叠重叠部分明确覆盖规则。4.5 现象合并后的文件烧录成功但运行到某功能就死机原因是两段数据的地址边界正好切断了某个函数或常量表。比如 Application 的起始地址设在了某个函数的中间。解决合并前用 map 文件确认各段的起止地址确保边界落在段与段之间而不是段内部。提示合并操作前永远先备份原始 BIN 和 HEX合并结果用sha256sum记录哈希烧录失败时能快速回退比对。5. 从格式转换到工程习惯地址这件事能内化就别外置BIN 和 HEX 的互转合并表面是文件格式问题底层是地址信息存在哪里的工程取舍。BIN 把地址外置换来的是紧凑和直接HEX 把地址内化换来的是自描述和可校验。合并操作放大了这个差异HEX 合并时工具能自己发现地址冲突BIN 合并时全靠人肉保证参数正确。我一般会这么理解如果你的工作流里频繁出现 BIN 合并说明地址约定散落在各个脚本和命令行参数里这是隐患。更稳的做法是维护一份链接脚本或 map 文件作为唯一地址来源合并脚本从这份来源读取地址而不是硬编码常量。另一个习惯是任何合并结果在烧录前先用xxd或十六进制编辑器抽查三个位置起始地址、两段交界处、结束地址前 16 字节。这三个点能覆盖绝大多数地址错位。验证方法上可以写一个简单的往返测试BIN 转 HEX 再转回 BIN比对哈希是否一致。如果不一致说明转换工具在填充值或地址处理上有偏差。这个测试能提前暴露大部分格式转换的坑。# 往返测试bin - hex - bin比对哈希 bin2hex firmware.bin firmware.hex --base 0x08000000 hex2bin firmware.hex firmware_rt.bin --fill 0xFF sha256sum firmware.bin firmware_rt.bin两个哈希一致才说明转换链路可信。参数--base指定基地址--fill指定空洞填充值这两个参数必须和实际烧录配置一致否则往返测试通过也不代表烧录正确。从那以后我每次做固件合并都强制走一遍map 文件确认地址 → 脚本合并 → 往返哈希比对 → 十六进制抽查这四步不再凭感觉cat两个文件了事。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑