HEX 文件是一种 ASCII 文本格式,常用于存储 微控制器或EPROM 的机器码。
它以十六进制形式表示二进制数据,常见扩展名为:.hex / .ihx
基本格式
每一行(称为一条“记录 record”)的通用格式为:
:llaaaatt[dd…]cc
| 字段 | 长度(字节) | 说明 |
|---|---|---|
: | 1 | 每行的起始标志 |
ll | 1 | 数据字节数(该行数据的长度) |
aaaa | 2 | 起始地址(偏移地址) |
tt | 1 | 记录类型 |
dd... | ll | 实际数据内容 |
cc | 1 | 校验和 |
记录类型(tt)
| 类型值 | 含义 |
|---|---|
00 | 数据记录(最常见) |
01 | 文件结束记录(EOF) |
02 | 扩展段地址记录(16位段地址) |
04 | 扩展线性地址记录(高16位地址,用于32位地址) |
05 | 启动地址记录(执行起始地址) |
校验和计算规则
校验和(checksum)用于验证每行数据的正确性。
计算方法:
Sum = (ll + aaaa高字节 + aaaa低字节 + tt + 所有数据字节) 取低8位 Checksum = (0x100 - Sum) & 0xFF
验证时: 每行所有字节(包括校验和)之和的低8位应为 0。
示例解析
示例行:
:10010000214601360121470136007EFE09D2190140
逐项解析:
| 部分 | 含义 |
|---|---|
: | 起始符 |
10 | 数据长度 = 16字节 |
0100 | 起始地址 = 0x0100 |
00 | 记录类型 = 数据记录 |
214601360121470136007EFE09D21901 | 数据内容(16字节) |
40 | 校验和 |
验证校验和:
Sum = 0x10 + 0x01 + 0x00 + 0x00 + (16个数据字节) Sum = 0xC0 Checksum = (0x100 - 0xC0) & 0xFF = 0x40 ✅
文件结束行示例
:00000001FF
长度 = 0
地址 = 0000
类型 = 01 (文件结束)
校验和 = FF
扩展地址记录示例
扩展线性地址记录(用于 32 位地址)
:020000040001F9
长度 = 2
地址 = 0000
类型 = 04
数据 = 0001(高 16 位)
校验和 = F9
→ 之后的数据记录地址应加上 (0x0001 « 16) = 0x00010000
python 解析脚本
1
2# :llaaaatt[dd...]cc
3
4# : 1 每行的起始标志
5# ll 1 数据字节数(该行数据的长度)
6# aaaa 2 起始地址(偏移地址)
7# tt 1 记录类型
8# dd... ll 实际数据内容
9# cc 1 校验和
10
11# 00 数据记录(最常见)
12# 01 文件结束记录(EOF)
13# 02 扩展段地址记录(16位段地址)
14# 04 扩展线性地址记录(高16位地址,用于32位地址)
15# 05 启动地址记录(执行起始地址)
16
17import sys
18import io
19
20# 强制设置 stdout 编码为 UTF-8
21sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
22
23def parse_hex_fragments(hex_file_path):
24 """
25 解析HEX文件,返回所有独立数据块及起始地址,严格保留地址不连续性。
26
27 返回值:
28 - data_blocks: 列表,每个元素为字典,包含 'address' 和 'data'
29 - start_address: 首个数据块的地址(若文件无数据,返回None)
30 """
31 with open(hex_file_path, 'r') as f:
32 lines = f.readlines()
33
34 data_blocks = [] # 存储所有数据块 [{address: int, data: bytes}, ...]
35 upper_address = 0x0000 # 扩展线性地址(类型04)
36 current_segment = 0x0000 # 扩展段地址(类型02)
37 start_address = None
38
39 for line in lines:
40 line = line.strip()
41 if not line.startswith(':'):
42 continue
43
44 # 解析字段
45 byte_count = int(line[1:3], 16) # 1-2
46 address = int(line[3:7], 16)
47 record_type = int(line[7:9], 16)
48 data_bytes = bytes.fromhex(line[9:-2])
49 checksum = int(line[-2:], 16)
50
51 # 校验和验证
52 computed_sum = sum(bytes.fromhex(line[1:-2])) & 0xFF # Sum = (ll + aaaa高字节 + aaaa低字节 + tt + 所有数据字节)
53 computed_checksum = (0x100 - computed_sum) & 0xFF # Checksum = (0x100 - Sum) & 0xFF
54 if checksum != computed_checksum:
55 raise ValueError(f"校验和错误: {line}")
56
57 # 处理记录类型
58 if record_type == 0x00: # 数据记录
59 # 计算完整地址(支持段地址和线性地址)
60 if upper_address != 0x0000:
61 full_address = (upper_address << 16) + address
62 else:
63 full_address = (current_segment << 4) + address
64
65 # 记录数据块(即使地址不连续也独立存储)
66 data_blocks.append({
67 'address': full_address,
68 'data': data_bytes
69 })
70
71 # 更新起始地址(仅首个数据块)
72 if start_address is None:
73 start_address = full_address
74
75 elif record_type == 0x02: # 扩展段地址
76 current_segment = int.from_bytes(data_bytes, byteorder='big')
77
78 elif record_type == 0x04: # 扩展线性地址
79 upper_address = int.from_bytes(data_bytes, byteorder='big')
80
81 # 05 启动地址记录(执行起始地址)
82 elif record_type == 0x01: # 文件结束
83 break
84
85 return data_blocks, start_address
86
87if __name__ == '__main__':
88 # 示例用法
89 filename=r'AppDemo.hex'
90 data_blocks, start_addr = parse_hex_fragments(filename)
91
92 # 打印输出
93 if start_addr is not None:
94 print(f"文件起始地址: 0x{start_addr:08X}")
95 for idx, block in enumerate(data_blocks):
96 print(f"数据块 {idx + 1}:")
97 print(f" 起始地址: 0x{block['address']:08X}")
98 print(f" 数据长度: {len(block['data'])} 字节")
99 print(f" 数据内容: {block['data'].hex().upper()}")
100 else:
101 print("HEX文件中未找到有效数据记录!")
评论