Keil HEX 文件格式解析及数据提取 您所在的位置:网站首页 如何将bin文件中的数据读取出来 Keil HEX 文件格式解析及数据提取

Keil HEX 文件格式解析及数据提取

2023-09-14 02:21| 来源: 网络整理| 查看: 265

1. 背景

进行 MCU OTA 升级调试时,升级重启后,MCU 程序跑飞,怀疑程序下载缓冲区的内容被改写,为了排除这个疑点,需要将缓冲区内的数据与编译生成的 BIN 文件或 HEX 文件内的数据进行比对。思路是将数据以文本形式输出到文件中,使用文件比对工具进行比对。

Keil 的 SAVE 调试命令会将下载缓冲区中的数据输出到 Intel HEX 格式[1]的文件中,虽然编译生成的 *.hex 文件也是该格式,但是二者有时不能直接对比:前者的地址会从 0x0000 开始,但是编译生成的 *.hex 文件内记录的起始地址与实际指定的程序烧录地址有关,但是数据是一样的。因此,需要从这两个文件中分别提取数据进行比对。如果是 BIN 文件,仅包含二进制数据,可以将数据提取出来,以同样的文本形式输出到文件中进行比对。

2. 文件格式(1) Intel HEX 文件格式

Keil 生成的 Intel HEX 文件中,每一行都是一条 HEX 记录,形如:

1 :llaaaatt[dd...]cc

每条记录的格式如下[1]:

: 每条记录都以冒号开始.ll 是“记录长度”字段,表示该条记录中“数据”字段(dd)占多少字节。aaaa 是“地址”字段,表示该条记录中,“数据”字段所在的起始地址。tt 是“类型”字段,表示该条记录的类型,有如下值: 00 - 数据(data) 01 - 文件结束(end-of-file) 02 - 扩展段地址(extended segment address) 04 - 扩展线性地址(extended linear address) 05 - 开始线性地址(start linear address),仅 MDK-ARM 使用dd 是“数据”字段,表示一个字节的数据。一条记录中可能有多个字节的数据,长度由 ll 字段指定。cc 是记录的校验和。

Keil 输出的 HEX 文件,每条记录最多包含 16 字节的数据,例如:

1 :10100000D0BC01208D420008978300084D70000875 (2) BIN 文件格式

BIN 文件其实没有格式。

3. 数据提取示例代码(1) 提取 HEX 文件内的数据

接下来给出一段提取 HEX 文件内容的程序,该程序逐行读取 HEX 文件,如果遇到类型为“数据”的记录,则将该记录中的“数据”字段提取出来,以十六进制 ASCII 字符的形式输出到另一个文件中。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 /** * @file hex_tool.c * @author qianchenzhumeng ([email protected]) * @brief 从 Intel HEX 文件中提取数据,以 ASCII 文本的形式输出到指定文件中。 * @version 0.1 * @date 2022-02-12 * * @copyright Copyright (c) 2022 qianchenzhumeng * */ #include #include #include #include /* Inter HEX 文件格式描述: * : 每条记录都以冒号开始. * ll 是“记录长度”字段,表示该条记录中“数据”字段(dd)占多少字节。 * aaaa`是“地址”字段,表示该条记录中,“数据”字段所在的起始地址。 * tt 是“类型”字段,表示该条记录的类型,有如下值: * 00 - 数据(data) * 01 - 文件结束(end-of-file) * 02 - 扩展段地址(extended segment address) * 04 - 扩展线性地址(extended linear address) * 05 - 开始线性地址(start linear address),仅 MDK-ARM 使用 * dd 是“数据”字段,表示一个字节的数据。一条记录中可能有多个字节的数据,长度由 `ll` 字段指定。 * cc 是记录的校验和。 */ /** * @brief 记录类型 * */ typedef enum xRECORD_TYPE { eData, eEOF, eExtendedSegmentAddress, eExtendedLineAddress, eStartLinearAddress }xRecordType_t; /** * @brief 行信息 * */ typedef struct xHEX_80_LINE_INFO { /* LL AAAA TT DDDD CC*/ /* :02 0000 04 2000 DA */ /* LL AAAA TT DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD CC*/ /* :10 1000 00 D0BC01208D420008978300084D700008 75 */ uint8_t u8Length; uint16_t u16Address; uint8_t u8RecordType; uint8_t u8CheckSum; }xHexLineInfo_t; #define LINE_MAX_LENGTH (1024) char acLineData[LINE_MAX_LENGTH]; /** * @brief 解析 HEX 行数据,要保证传给该函数的行数据是有效的 HEX 行数据,否则可能发生错误 * * @param[in] pcLineData 行数据 * @param[out] pxHexLineInfo 行信息结构体地址 * * @return 数据开始的地址 */ void *pvHexLineParser(char *pcLineData, xHexLineInfo_t *pxHexLineInfo) { char acString[5]; if( (NULL == pcLineData) || (NULL == pxHexLineInfo) ) { return NULL; } /* 获取 LL 字段 */ memset((void *)acString, '\0', sizeof(acString)); memcpy((void *)acString, (void *)(pcLineData + 1), 2); pxHexLineInfo->u8Length = (uint8_t)strtol(acString, NULL, 16); /* 获取 AAAA 字段 */ memset((void *)acString, '\0', sizeof(acString)); memcpy((void *)acString, (void *)(pcLineData + 3), 4); pxHexLineInfo->u16Address = (uint16_t)strtol(acString, NULL, 16); /* 获取 TT 字段 */ memset((void *)acString, '\0', sizeof(acString)); memcpy((void *)acString, (void *)(pcLineData + 7), 2); pxHexLineInfo->u8RecordType = (uint8_t)strtol(acString, NULL, 16); /* 获取 CC 字段 */ memset((void *)acString, '\0', sizeof(acString)); memcpy((void *)acString, (void *)(pcLineData + 9 + pxHexLineInfo->u8Length), 2); pxHexLineInfo->u8CheckSum = (uint8_t)strtol(acString, NULL, 16); return pcLineData+9; } void vPrintHelp(char *pcAppName) { if( NULL != pcAppName) { printf("Usage: %s [input file] [output file]\n", pcAppName); } } int main(int argc, char *argv[]) { FILE *fInput; FILE *fOutput; size_t xLines = 0; size_t xLineLength = 0; size_t xFileSize; char cr; char *pcInputFileName = NULL; char *pcOutputFileName = NULL; char *pcData = NULL; xHexLineInfo_t xHexLineInfo; if( 3 != argc ) { vPrintHelp(argv[0]); exit(1); } else { pcInputFileName = argv[1]; pcOutputFileName = argv[2]; printf("output: %s\n", pcOutputFileName); } fInput = fopen(pcInputFileName, "rb"); if (fInput == NULL) { printf("Open %s error!\n", pcInputFileName); exit(1); } fOutput = fopen(pcOutputFileName, "wb+"); if( NULL == fOutput ) { fclose(fInput); printf("Open %s error!\n", pcOutputFileName); exit(1); } /* 获取文件大小 */ fseek(fInput, 0, SEEK_END); xFileSize = ftell(fInput); rewind(fInput); /* 统计输入文件行数 */ for(size_t i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有