手把手教你用Python解析Hex文件:自己写个简易烧录器脚本
·
Python实战:从零构建Hex文件解析器与Bin转换工具
在嵌入式开发中,Hex文件与Bin文件是两种最常见的固件格式。许多开发者习惯依赖现成的IDE工具进行格式转换,但当我们需要自动化流程或定制化处理时,理解其底层原理并手动实现转换工具就显得尤为重要。本文将带你用Python标准库一步步构建一个健壮的Hex文件解析器,并生成可直接烧录的Bin文件。
1. Hex文件格式深度解析
Hex文件(Intel HEX格式)是一种文本格式的十六进制编码文件,每行代表一条记录。每条记录由六个关键字段组成:
:BBAAAATTHHHH...HHCC
其中各字段含义如下:
| 字段符号 | 名称 | 字节数 | 描述 |
|---|---|---|---|
| : | 记录起始符 | 1 | 固定为冒号 |
| BB | 数据长度 | 1 | 本记录中数据字节数 |
| AAAA | 地址 | 2 | 数据存储的起始地址 |
| TT | 记录类型 | 1 | 决定该记录的作用 |
| HH...HH | 数据 | 变长 | 实际数据内容 |
| CC | 校验和 | 1 | 用于验证记录完整性的校验值 |
记录类型(TT字段)主要有以下几种:
- 0x00 :数据记录(Data Record)
- 0x01 :文件结束记录(End Of File)
- 0x04 :扩展线性地址记录(Extended Linear Address)
- 0x05 :起始线性地址记录(Start Linear Address)
校验和的计算方法是:将记录起始符后的所有字节相加,取和的低8位补码。例如对于记录 :10010000214601360121470136007EFE09D2190140 :
def calc_checksum(hex_str):
bytes_data = bytes.fromhex(hex_str[1:])
return (0x100 - (sum(bytes_data) & 0xFF)) & 0xFF
2. 构建Python解析框架
我们首先创建一个HexParser类来处理文件解析:
import struct
from collections import defaultdict
class HexParser:
def __init__(self):
self.memory_map = defaultdict(lambda: 0xFF)
self.current_address = 0
self.extended_linear_address = 0
def parse_line(self, line):
line = line.strip()
if not line.startswith(':'):
raise ValueError("Invalid HEX line format")
byte_count = int(line[1:3], 16)
address = int(line[3:7], 16)
record_type = int(line[7:9], 16)
data = line[9:-2]
checksum = int(line[-2:], 16)
# 校验和验证
calculated_checksum = self._calculate_checksum(line[1:-2])
if calculated_checksum != checksum:
raise ValueError(f"Checksum mismatch at line: {line}")
return {
'byte_count': byte_count,
'address': address,
'record_type': record_type,
'data': data,
'checksum': checksum
}
def _calculate_checksum(self, hex_str):
bytes_data = bytes.fromhex(hex_str)
return (0x100 - (sum(bytes_data) & 0xFF)) & 0xFF
3. 处理扩展地址与数据拼接
Hex文件可能包含非连续地址数据,特别是当使用扩展线性地址记录(0x04类型)时。我们需要正确处理地址偏移:
def process_record(self, parsed_line):
record_type = parsed_line['record_type']
data_bytes = bytes.fromhex(parsed_line['data'])
if record_type == 0x00: # 数据记录
base_address = self.extended_linear_address + parsed_line['address']
for i, byte in enumerate(data_bytes):
self.memory_map[base_address + i] = byte
elif record_type == 0x04: # 扩展线性地址记录
self.extended_linear_address = (data_bytes[0] << 24 |
data_bytes[1] << 16)
elif record_type == 0x01: # 文件结束记录
return False
return True
4. 生成Bin文件与地址间隙处理
Bin文件是连续的二进制数据,而Hex文件可能有地址间隙。我们需要填充这些间隙(通常用0xFF):
def generate_bin(self, output_path, fill_value=0xFF):
if not self.memory_map:
raise ValueError("No data parsed")
min_addr = min(self.memory_map.keys())
max_addr = max(self.memory_map.keys())
bin_size = max_addr - min_addr + 1
# 创建并填充二进制缓冲区
bin_data = bytearray([fill_value] * bin_size)
for addr, value in self.memory_map.items():
bin_data[addr - min_addr] = value
# 写入文件
with open(output_path, 'wb') as f:
f.write(bin_data)
return bin_size
5. 完整实现与错误处理
将各部分组合成完整的解决方案:
def hex_to_bin(hex_path, bin_path):
parser = HexParser()
try:
with open(hex_path, 'r') as f:
for line in f:
parsed = parser.parse_line(line)
if not parser.process_record(parsed):
break
size = parser.generate_bin(bin_path)
print(f"Successfully converted {hex_path} to {bin_path}")
print(f"Output size: {size} bytes")
return True
except Exception as e:
print(f"Error during conversion: {str(e)}")
return False
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print("Usage: python hex2bin.py <input.hex> <output.bin>")
sys.exit(1)
hex_to_bin(sys.argv[1], sys.argv[2])
6. 高级功能扩展
6.1 支持分段Bin文件生成
某些情况下,我们需要根据地址范围生成多个Bin文件:
def generate_segmented_bin(self, output_dir, segment_size=0x10000):
if not self.memory_map:
raise ValueError("No data parsed")
min_addr = min(self.memory_map.keys())
max_addr = max(self.memory_map.keys())
segment_start = min_addr & ~(segment_size - 1)
segment_end = (max_addr + segment_size - 1) & ~(segment_size - 1)
for segment_base in range(segment_start, segment_end, segment_size):
segment_data = bytearray([0xFF] * segment_size)
segment_min = segment_base
segment_max = segment_base + segment_size - 1
for addr, value in self.memory_map.items():
if segment_min <= addr <= segment_max:
segment_data[addr - segment_base] = value
output_path = f"{output_dir}/segment_{segment_base:08x}.bin"
with open(output_path, 'wb') as f:
f.write(segment_data)
6.2 添加CRC校验
为生成的Bin文件添加CRC校验:
import zlib
def add_crc_to_bin(bin_path):
with open(bin_path, 'rb') as f:
data = f.read()
crc32 = zlib.crc32(data) & 0xFFFFFFFF
with open(bin_path + '.crc', 'w') as f:
f.write(f"CRC32: {crc32:08X}\n")
return crc32
7. 实际应用场景
这个脚本可以集成到各种自动化流程中:
- CI/CD流水线 :在固件构建后自动转换格式
- 批量处理 :一次性处理多个Hex文件
- 自定义烧录工具 :作为底层转换模块
- 固件分析 :解析Hex文件内容进行安全检查
# 示例:批量处理目录下所有Hex文件
import glob
import os
def batch_convert(input_dir, output_dir):
os.makedirs(output_dir, exist_ok=True)
for hex_file in glob.glob(os.path.join(input_dir, '*.hex')):
bin_file = os.path.join(
output_dir,
os.path.basename(hex_file).replace('.hex', '.bin')
)
hex_to_bin(hex_file, bin_file)
这个Python实现的Hex解析器不仅完整实现了基本功能,还包含了许多实用扩展。相比现成工具,它具有以下优势:
- 完全透明可控 :每个处理步骤都可自定义
- 轻量无依赖 :仅使用Python标准库
- 易于集成 :可作为模块嵌入其他系统
- 跨平台 :在Linux、Windows、Mac上均可运行
在实际项目中,我曾用类似方案解决了IDE生成Hex文件与生产烧录工具不兼容的问题,通过自定义地址映射规则,完美实现了格式转换。这种深入理解文件格式并自主实现处理工具的能力,往往是区分普通开发者与技术专家的关键。
更多推荐
所有评论(0)