在上一篇文章 深入解析Intel HEX文件格式 中,我们详细介绍了Intel HEX文件的格式和记录类型。在嵌入式系统开发中,Intel HEX文件是一种常见的二进制数据表示格式,通常用于存储和传输固件。在某些情况下,我们可能需要将多个HEX文件合并为一个文件,例如将多个模块的代码合并为一个完整的固件。本文将详细介绍如何合并Intel HEX文件,并提供一个基于Rust的简单实现。
合并HEX文件的场景
在某些情况下,我们需要将多个HEX文件合并为一个文件。常见的场景包括:
- 模块化开发:将多个模块的代码合并为一个完整的固件,比如包含boot loader的工程。
- 固件升级:将多个更新包合并为一个完整的固件。
- 硬件仿真:将多个HEX文件加载到硬件仿真器中。
合并HEX文件的挑战
合并HEX文件时,可能会遇到以下挑战:
- 地址冲突:多个HEX文件可能包含相同地址的数据,导致冲突。
- 记录类型处理:需要正确处理扩展地址记录(如扩展线性地址记录和扩展段地址记录)。
- 校验和计算:合并后的文件需要重新计算校验和。
合并HEX文件的步骤
合并HEX文件的基本步骤如下:
- 读取所有HEX文件:将每个HEX文件的记录解析为内存中的数据结构。
- 处理扩展地址记录:根据扩展地址记录调整数据的加载地址。
- 合并数据记录:将多个文件的数据记录合并为一个文件,确保地址不冲突。
- 生成新的HEX文件:将合并后的记录写入新的HEX文件,并重新计算校验和。
基于Rust的HEX文件合并工具
以下是一个基于Rust的简单HEX文件合并工具实现:
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
fn main() -> io::Result<()> {
// 定义要合并的HEX文件路径
let hex_files = vec![
"file1.hex",
"file2.hex",
// 添加更多的文件路径
];
// 定义输出文件路径
let output_file = "merged.hex";
// 打开输出文件
let mut output = File::create(output_file)?;
// 遍历每个HEX文件
for hex_file in hex_files {
let path = Path::new(hex_file);
let file = File::open(path)?;
let reader = BufReader::new(file);
// 逐行读取HEX文件并写入输出文件
for line in reader.lines() {
let line = line?;
writeln!(output, "{}", line)?;
}
}
println!("HEX files merged into {}", output_file);
Ok(())
}
代码说明
读取HEX文件:使用BufReader逐行读取每个HEX文件。
写入输出文件:将每行记录写入新的HEX文件。
合并逻辑:该工具假设所有HEX文件的地址范围不重叠,适用于简单的合并场景。
处理复杂场景
如果HEX文件的地址范围可能重叠,或者需要处理扩展地址记录,可以按照以下步骤增强工具:
- 解析扩展地址记录:在读取HEX文件时,解析扩展线性地址记录和扩展段地址记录。
- 调整地址范围:根据扩展地址记录调整数据的加载地址。
- 检查地址冲突:在合并数据记录时,检查地址是否冲突,并处理冲突。
python实现
以下是一个使用Python实现的简单HEX文件合并工具:
def parse_hex_record(line):
"""
解析HEX文件的一行记录。
返回一个字典,包含记录的长度、地址、类型、数据和校验和。
"""
if not line.startswith(":"):
raise ValueError("Invalid HEX record: record must start with ':'")
# 解析记录字段
record_length = int(line[1:3], 16)
address = int(line[3:7], 16)
record_type = int(line[7:9], 16)
data = line[9:9 + 2 * record_length]
checksum = int(line[9 + 2 * record_length:9 + 2 * record_length + 2], 16)
return {
"length": record_length,
"address": address,
"type": record_type,
"data": data,
"checksum": checksum,
}
def merge_hex_files(file_paths, output_path):
"""
合并多个HEX文件。
:param file_paths: 要合并的HEX文件路径列表
:param output_path: 合并后的输出文件路径
"""
merged_records = []
current_base_address = 0 # 当前基地址(用于处理扩展地址记录)
for file_path in file_paths:
with open(file_path, "r") as file:
for line in file:
line = line.strip()
if not line:
continue
record = parse_hex_record(line)
# 处理扩展地址记录
if record["type"] == 0x04: # 扩展线性地址记录
current_base_address = int(record["data"], 16) << 16
continue
elif record["type"] == 0x02: # 扩展段地址记录
current_base_address = int(record["data"], 16) << 4
continue
# 跳过文件结束记录(除了最后一个文件)
if record["type"] == 0x01: # 文件结束记录
if file_path != file_paths[-1]:
continue
# 计算绝对地址
absolute_address = current_base_address + record["address"]
# 检查地址冲突
for existing_record in merged_records:
if (
existing_record["address"] <= absolute_address < existing_record["address"] + existing_record["length"]
or absolute_address <= existing_record["address"] < absolute_address + record["length"]
):
raise ValueError(f"Address conflict detected at address {hex(absolute_address)}")
# 添加记录到合并列表
merged_records.append({
"address": absolute_address,
"length": record["length"],
"data": record["data"],
"type": record["type"],
})
# 按地址排序记录
merged_records.sort(key=lambda x: x["address"])
# 写入合并后的HEX文件
with open(output_path, "w") as output_file:
for record in merged_records:
# 计算校验和
data_bytes = bytes.fromhex(record["data"])
checksum = sum(data_bytes) & 0xFF
checksum = (~checksum + 1) & 0xFF # 取补码
# 写入记录
output_file.write(
f":{record['length']:02X}{record['address']:04X}{record['type']:02X}{record['data']}{checksum:02X}\n"
)
# 写入文件结束记录
output_file.write(":00000001FF\n")
print(f"HEX files merged into {output_path}")
# 示例用法
if __name__ == "__main__":
hex_files = ["file1.hex", "file2.hex"] # 要合并的HEX文件路径
output_file = "merged.hex" # 合并后的输出文件路径
merge_hex_files(hex_files, output_file)
代码说明
-
parse_hex_record
函数:- 解析HEX文件的一行记录,返回记录的长度、地址、类型、数据和校验和。
-
merge_hex_files
函数:- 合并多个HEX文件,处理扩展地址记录和文件结束记录。
- 检查地址冲突,确保合并后的文件地址范围不重叠。
- 将合并后的记录按地址排序,并写入输出文件。
-
地址冲突检查:
- 在合并过程中,检查每条记录的地址范围是否与已合并的记录冲突。
- 如果发现冲突,抛出错误。
-
校验和计算:
- 每条记录的校验和通过计算数据字节的和的补码得到。
-
文件结束记录:
- 跳过除最后一个文件之外的所有文件结束记录。
- 在合并后的文件末尾写入一个文件结束记录。
示例HEX文件
file1.hex
:10010000214601360121470136007EFE09D2190140
:00000001FF
file2.hex
:10020000314602360231470236008EFE09D3190240
:00000001FF
合并后的 merged.hex
:10010000214601360121470136007EFE09D2190140
:10020000314602360231470236008EFE09D3190240
:00000001FF
运行结果
运行上述代码后,merged.hex
文件将包含 file1.hex
和 file2.hex
的内容,且地址范围不冲突。
总结
合并Intel HEX文件是嵌入式系统开发中的常见任务。通过理解HEX文件格式和合并逻辑,可以轻松实现HEX文件的合并。本文提供了一个基于Rust的简单实现,并讨论了处理复杂场景的方法。希望本文能帮助你更好地理解和处理HEX文件!
rust库中有一个crate,用于开发中的合并情形基本够用,ihex-merge。这是个命令行工具,编译后只有 ~700KB,可以直接使用。注意,这个工具不支持段冲突的合并。
使用方法也非常简单:
ihex-merge output.hex in1.hex in2.hex in3.hex
后台回复 ihex
可以获取Windows二进制文件。
公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top