一、Hex文件简介
由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。一般用于MCU程序烧录,可以把hex文件理解为带有地址信息的bin数据的记录集合。(注:烧录时如果使用bin文件则需要指明对应首地址的位置,而使用hex文件则不需要,因为hex文件中包含了对应地址包含的数据内容)
二、Hex文件记录格式与解析
Hex文件记录格式:
RECORD MASK ":" | RECLEN | LOAD OFFSET | RECTYPE | INFO OR DATA | CHKSUM |
1 Byte | 1 Byte | 2 Byte | 1 Byte | n Byte | 1 Byte |
- RECORD MASK:HEX文件的起始格式,文件一开始应该是一个冒号作为起点
- RECLEN:表示[INFO OR DATA]区域有多少字节的数据长度
- LOAD OFFSET:表示数据偏移的地址(数据开始记录的偏移地址)
- RECTYPE:表示该行的Hex文本的类型
- 00 表示该行记录的是数据(可以理解为bin文件内容)
- 01 表示该Hex文件结束,一般放在最末尾
- 02 表示记录的是扩展段地址(数据地址段)
- 03 表示记录的是开始段地址
- 04 表示记录的是扩展线性地址
- 05 表示记录的是起始线性地址(一般认为main函数入口地址)
- INFO OR DATA:该行的数据或者地址信息
- CHKSUM:该行的检验和 [该行所有16进制(除了checksum)的累加和S,然后(0x100 - S)& 0xFF]
注意:最常见的Recv type有00、01、04、05
(1)RECTYPE 00 数据标识
: 04 2000 00 FECACEFA 4C
04 表示本行有4个字节数据
2000 表示偏移地址为0x2000
FECACEFA 表示本行数据
4C 为该行校验和计算如下:
- 累加和S =(04 + 20 + 00 + FE + CA + CE + FA)= 0x3B4;
- 校验和CHKSUM = (0x100 - S)& 0xFF = 0x4C
(2)RECTYPE 01 文件结束标识
: 00 0000 01 FF
00 表示本行有0个字节数据
0000 表示偏移地址为 0x0000
01 表示Hex文件结束
FF 为该行校验和计算如下:
- 累加和S =(00 + 00 + 00 + 01)= 0x01;
- 校验和CHKSUM = (0x100 - S)& 0xFF = 0xFF
(3)RECTYPE 02 扩展段标识
USBA:用于记录16位扩展段地址的段基址为位4—19(SBA),其中SBA的位0---3为0。位4—19称为上段基地址USBA,再加上LOAD OFFSET就可计算数据偏移量地址。
:02 0000 02 1200 EA
02 表示本行有2个字节数据
0000 表示偏移地址为 0x0000
02 表示扩展段标识
1200 表示扩展段标识,对应的段地址为0x0001 2000
EA 为该行校验和计算如下:
- 累加和S =(02 + 00 + 00 + 02 + 12 + 00)= 0x16;
- 校验和CHKSUM = (0x100 - S)& 0xFF = 0xEA
(4)RECTYPE 03 开始段地址
(5)RECTYPE 04 扩展线性地址标识
: 02 0000 04 0005 F5
02 表示本行有2个字节数据
0000 表示偏移地址为 0x0000
04 表示为拓展线性地址
0005 表示扩展线性地址的高16位为0x0005,低16位在LOAD OFFSET中为 0x0000,即在下一个04出现前,后续数据记录的基址为 0x0005 0000
F5 为该行校验和计算如下:
- 累加和S =(02 + 00 + 00 + 04 + 00 + 05)= 0x0B;
- 校验和CHKSUM = (0x100 - S)& 0xFF = 0xF5
(6)RECTYPE 05 起始线性地址标识
: 04 0000 05 00000411 E2
04 表示本行有4个字节数据
0000 表示偏移地址为 0x0000
05 表示起始线性地址标识
00000411 表示目标执行起始地址为0x0000 0411可以在对应map文件中找到
E2 为该行校验和计算如下:
- 累加和S =(04 + 00 + 00 + 05 + 00 + 00 + 04 + 11)= 0x1E;
- 校验和CHKSUM = (0x100 - S)& 0xFF = 0xE2
示例:
:040000050000841162
(7)Recv type 04/00/05/01 简单示例
:020000040005F5
:04200000FECACEFA4C
:0400000500000411E2
:00000001FF
: 02 0000 04 0005 F5 :该行表示后续数据记录的扩展线性地址的基址为 0005 0000
: 04 2000 00 FECACEFA 4C:表示 0x0005 2000地址起始的数据为FECACEFA
0x0005 2000 | 0xFE |
0x0005 2001 | 0xCA |
0x0005 2002 | 0xCE |
0x0005 2003 | 0xFA |
: 04 0000 05 00000411 E2:表示0x0000 0411为main函数的入口地址
: 00 0000 01 FF:表示Hex文件末尾
(8)Recv type 04/02/00 简单示例
:020000040108F1
:020000021200EA
:0401000090FFAA556D
:02 0000 04 0108 F1:该行表示后续数据记录的扩展线性地址的基址为 0108 0000
:02 0000 02 1200 EA:该行表示后续数据记录的扩展段地址的基址为 0001 2000
:04 0100 00 90FFAA55 6D:该行表示0x0108 0000 + 0x0001 2000 + 0x0100 的起始地址数据为90FFAA55
实际物理地址=扩展线性地址 + 扩展段地址 + 数据偏移地址 = 0x0108 0000 + 0x0001 2000 + 0x0100 = 0x0109 2100
三、Hex文件解析Python代码示例
Hex文件的内容行解析python代码示例:
def _line_analyse(self, line:str):# string fliterline = line.strip() # 去除开头与结尾的空格或换行符# Hex file formatrecord_mask = line[0]data_len = int(line[1:3], 16)load_offset = int(line[3:7], 16)recv_type = int(line[7:9], 16)checksum = int(line[-2:], 16)# Hex file info string disposeDATA_START_INDEX = 9DATA_STR_LENGTH = data_len * 2if (record_mask == ":") and (self.is_finished_analyse == False):# 数据记录类型if (recv_type == 0x00): base_addr = self.address_info_list[-1]address = base_addr + load_offsetdata_hex_list = []for i in range(0, DATA_STR_LENGTH, 2):data_hex = int(line[DATA_START_INDEX + i : DATA_START_INDEX + i + 2], 16)data_hex_list.append(data_hex)# checksum 校验和检测# 该行所有16进制累加和S, 然后0x100 - Schecksum_temp = 0for i in range(1, len(line) - 2, 2):checksum_temp += int(line[i : i+2], 16)checksum_temp = (0x100 - checksum_temp) & 0xFF# 数据保存if checksum_temp == checksum:self.data_dict[address] = data_hex_listelse:print("checksum_temp:0x{:2x}, checksum:0x{:2x}, line:{:s}".format(checksum_temp, checksum, line))# 文件结束类型elif(recv_type == 0x01): self.is_finished_analyse = True# 标识拓展段地址 (Hex86)elif(recv_type == 0x02): pass# 标识开始段地址 (Hex86)elif(recv_type == 0x03):pass# 标识扩展线性地址elif(recv_type == 0x04):address_high_half_word = int(line[DATA_START_INDEX : DATA_START_INDEX + DATA_STR_LENGTH], 16)address_low_half_word = load_offsetaddress = ((address_high_half_word << 16) | (address_low_half_word)) & 0xFFFFFFFF# 补充地址信息self.address_info_list.append(address)# 标识开始线性地址 (main函数入口地址)elif(recv_type == 0x05):eip_address = int(line[DATA_START_INDEX : DATA_START_INDEX + DATA_STR_LENGTH], 16) + load_offsetself.main_address = eip_addresselse:pass