HEX文件合并

来源:互联网 发布:win10网络共享速度慢 编辑:程序博客网 时间:2024/04/28 16:01

HEX文件合并

“源码见文章尾”

芯片一开始是空的,多个HEX文件需要多次下载,比较麻烦,效率低。若可以把多个HEX文件合并为一个。那么下载一次即可。**Intel HEX是把二进制信息转换成ASCLL文本的格式。**一、  HEX文件的格式1、  以行为单位。2、  每行开头是“:”(0X3A),结尾是“回车换行”(0X0D,0X0A)。3、  中间所有内容都是以字符形式表现的。若数据是“0X1A”,在HEX中就是“0X31 0X41”。4、  HEX文件格式:”:LLAAAATT[DD…]CC”。
字段 长度 : 1字节 数据长度 1字节 数据地址 2字节 数据类型 1字节 数据 N字节 校验 1字节 0x0D 0x0A 2字节(回车换行)

5、 数据类型分类。

数据类型 英文 说明 00 DATA 数据记录 01 END OF FILE 用来标识文件记录的结束 02 EXTENDED SEGMENT ADDRESS 用来标识扩展段地址 03 START SEGMENT ADDDRESS 开始段地址 04 EXTERNED LINEAR ADDRESS 用来标识扩展线性地址(虚地址) 05 START LINEAR ADDRESS 开始线性地址(虚地址)
32位扩展线性地址记录用于指定线性基地址(LBA)的位16-31,其中LBA的位0到15为零。LBA的位16-31被称为上线性基地址(ULBA)。通过将LBA添加到通过将包含数据记录的LOAD OFFSET字段添加到数据记录中的字节的索引(0,1,2…n)来计算的偏移量来获得后续数据记录中的内容字节的绝对存储器地址。该偏移量加法模数为4G(即32位),忽略任何进位,因此偏移环绕加载(从OFFFFFFFFH到OOOOOOOOOH)导致从端到端的包围。加载特定字节的线性地址计算为:(LBA + DRLO + DRI)MOD 4GDRLO是数据记录的LOAD OFFSET字段。 DRI是数据记录中的数据字节索引。6、  HEX文件由记录(RECORD)组成。每一行代表一个记录。7、  由于只能用2字节表示数据地址,所以最大只能到64K。为了在更高地址中保存数据,有了EXTENDED LINEAR ADDRESS RECORD。若一行的数据类型是0X04,那么这行的数据就是随后数据的基地址。:020000040004F6:1000000018F09FE518F09FE518F09FE518F09FE5C0:1000100018F09FE5805F20B9F0FF1FE518F09FE51D第一行,是EXTERNED LINEAR ADDRESS RECORD,里面的基地址是0x0004,第二行是DATA RECORD,里面的地址值是0x0000。那么数据18F09FE518F09FE518F09FE518F09FE5要写入FLASH中的地址为(0x0004 << 16) | 0x0000,也就是写入FLASH的0x00040000这个地址。同样,第三行的数据的写入地址为0x00040010。:02 0000 04 0801 F1    后面的记录地址=0x08010000+自己的偏移地址。 :02 0000 04 0802 F0    后面的记录地址=0x0802000+自己的偏移地址。8、  END OF FILE RECORD行是每隔HEX文件的最后一行。本行内容固定为:“:00000001FF”9、  校验:LLAAAATT[DD….]CC计算:0X100-((0XLL+0XAA+0XAA+0XTT+0XDD+…+0XDD)%256)二、  HEX文件合并1、网络上提供的集中思路①STM32的bootloader和APP的HEX文件合并。将bootloader的最后一行(文件结束记录)删除,把APP的HEX文件(有些人说要去头)直接COPY在bootloader的后面。PS:APP的起始地址一定要设置正确。②Srecord工具直接可以用来合并多个文件。③Python提供了方法。![python提供了方法](http://img.blog.csdn.net/20170621233540638?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMTY1NTU3NDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)2、使用srecord工具合并的文件存在以下情况:①L1909-L1921的几个04(:02 0000 04 0800 F2)记录,我个人认为没有必要。②L3965-L3977的几个04(:02 0000 04 0801 F1)记录,我个人认为没有必要。③合并前的文件中含有05记录“:040000050800293195”,合并后没有了。④合并文件每行记录的数据量扩展到了20条,然后自己又做了校验。我认为这个功能不是必须的。可以直接维持合并前文件中的数据,这样可以省却自己重新校验的麻烦。3、“05”记录    根据文档说明,05记录是用来将地址加载到Intel处理器的EIP寄存器中的。ARM中没有EIP寄存器。    EIP寄存器:存放CPU要执行的下一个指令的地址。三、  程序设计和实现第一阶段:简单实现两个文件的合并(不按照地址顺序分,自己指定文件的先后顺序)。第二阶段:合并中要注意文件去尾。第三阶段:按照地址顺序来合并文件(指定任意文件的顺序)。//(未完成)第四阶段:每行32个数据,重新校验。 //(未完成)第五阶段:每行自定义个数据,重新校验。第一阶段:1、  获取路径。2、  创建新文件并打开。3、  打开src文件。读取-写入-关闭。4、  关闭新文件。第二阶段:1、      获取路径。2、  创建新文件并打开。3、  打开所有src文件。读取-分析-写入-关闭。4、  关闭新文件。这种合并特点:1、  将SRC文件作为参数传入时,要按照地址顺序来传入2、  合并和文件的大小=源文件src大小求和。分析:参考的已经合并的文件大小“382k”,自己合并的文件大小“446k”。两个源文件大小分别为“429K”+“17K”=“446k”。两个合并文件的[DD…]都是相同的。不同之处在于邹工提供的文件每行有20个数据,共计5083行。自己合并的每行只有10个数据,共计10137行。每行都多出了“:”+“LL”+“AAAA”+“TT”+“CC”+0x0a+0x0d=13字节的数据。共多出了 13*(10137-5083)/1024=64K。正好446K-382K=64K。第三阶段:按照地址顺序来对文件进行合并。思路:1、  对每隔文件中的地址进行分析,计算出每个文件的地址范围。根据对HEX文件的分析,单个HEX文件的地址应该是顺序进行向下的。所以要先搞清楚02 03 04 05这个地址的计算方式。2、  按照地址范围给每个文件进行编号。做个映射。问题:int arry1[argc]与char **arry2[argc][256]中的行一一对应。按照arry1中的数值大小,对arry2的行进行重新排序。Index[2] =      {  1, 2};addr[2] =       {100,10};argv[argc][256] = {“APP”, “BOOT”};new_index[2]= { 2, 1};3、  按照编号,合并文件。校验  四、  地址计算起初HEX文件设计成16bit地址范围(64KB),随后增加到20bit地址范围(1MB),甚至32bit地址范围(4GB)。通常每条记录存放32个数据。不推荐一条记录中存放太多数据:会造成传输时间增加,错误出现的可能性变大。也不推荐每行中存放太少的数据:地址头太多,比较负载大。就是冗余嘛。文件体积过大。1、  如果没有04和02字记录那么地址就是Target address = AAAA2、  如果有02记录。就是为了获取20bit的地址数据。使地址可达1MB。Target address = DDDD*16+AAAA;(DDDD<<4 | AAAA)3、  如果有04记录,就是为了获取32位的地址数据。使地址可达到4GB。Target address = DDDD*65535+AAAA;(DDDD<<16 | AAAA)五、  校验计算:LLAAAATT[DD…]CC0x100-(0x02+0x00+0x00+0x04+0x08+0x00)%0x256=F210+00+00+00+08+2C+00+20+7D+05+00+08+F9+01+00+08+FB+01+00+08=94校验的程序是网上download来的还没验证过(仅供参考)。
bit AnalyseHEX(char hex[],int len){      unsigned char i=1;      unsigned char data;      int cc=0;      char temp[2];      do      {          temp[0] = hex[i++];          temp[1] = hex[i++];          sscanf(temp,"%x",&data);          cc += data;      } while (i<(len-2));      cc%=256;      cc=0x100-cc;      temp[0] = hex[i++];      temp[1] = hex[i++];      sscanf(temp,"%x",&data);      return (cc==data)?1:0;    }

hex_rec_als.h

#ifndef _HEX_REC_ALS_H#define _HEX_REC_ALS_H#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define HEX_NOT     (-1)#define HEX_DAT     0#define HEX_EOF     1#define HEX_ESA     2#define HEX_SSA     3#define HEX_ELA     4#define HEX_SLA     5#define LINE_LEN 269#define PATH_LEN 256//#define ADDR_MIN    0x00000000//#define ADDR_MAX    0xFFFFFFFFtypedef struct hex_addr_range{    unsigned int hex_addr_min;    unsigned int hex_addr_max;}hex_addr_range_t;extern int is_hex_rec_XXX(const char *line);extern int get_hex_addr_range(const char *filepath, hex_addr_range_t *range);#endif // _HEX_REC_ALS_H

hex_rec_als.c

#include "hex_rec_als.h"int is_hex_rec_XXX(const char *line){    //:LLAAAATT[DD...]CC    //012345678    switch(line[8])    {    case '0': return HEX_DAT;    case '1': return HEX_EOF;    case '2': return HEX_ESA;    case '3': return HEX_SSA;    case '4': return HEX_ELA;    case '5': return HEX_SLA;    default:  return HEX_NOT;    }}//得到单个HEX文件的地址范围int get_hex_addr_range(const char *filepath, hex_addr_range_t *range){    //遍历每行查找04    //第一个04+第一条数据记录的地址就是hex_addr_min.    //最后一个04+最后一条数据记录的地址就是hex_addr_max.    FILE *fp = fopen(filepath, "r");    if(NULL == fp)    {        perror("get hex addr range fopen");        exit(EXIT_FAILURE);    }    char line_buff[LINE_LEN] = {0};    unsigned int line_num = 0;    unsigned int range_base_temp = 0;    int hex_ela_cnt = 0;                                //04记录出现的次数    while(NULL != fgets(line_buff, LINE_LEN, fp))    {        line_num++;        int line_flag = is_hex_rec_XXX(line_buff);        if(line_flag == HEX_ELA)        {            hex_ela_cnt++;            char *c_addr_base = NULL;            unsigned int addr_base = 0;            c_addr_base = (char*)malloc(sizeof(char)*5);            c_addr_base = line_buff+9;            c_addr_base[4] = '\0';            sscanf(c_addr_base, "%x", &addr_base);            range_base_temp = addr_base<<16;            if(line_num == 1)                range->hex_addr_min = range->hex_addr_max;        }        if(line_flag == HEX_DAT)        {            //:LLAAAATTDDDDCC            //012345678            char *c_addr_offset = NULL;            unsigned int addr_offset = 0;            c_addr_offset = (char*)malloc(sizeof(char)*5);            c_addr_offset = line_buff+3;            c_addr_offset[4] = '\0';            //printf("c_addr_offset=%s\n", c_addr_offset);            sscanf(c_addr_offset, "%x", &addr_offset);            char *c_LL = NULL;            int  i_LL = 0;            c_LL = (char*)(malloc(sizeof(char)*3));            c_LL = line_buff+1;            c_LL[2] = '\0';            sscanf(c_LL, "%x", &i_LL);            //printf("len=%x\n", i_LL);            if(line_num == 1)                range->hex_addr_min = 0<<16 | addr_offset;            else if(line_num == 2 && hex_ela_cnt > 0)            {                range->hex_addr_min = range_base_temp | addr_offset;                //printf("range min=%08x\n", range->hex_addr_min);            }            else            {                range->hex_addr_max = range_base_temp | addr_offset | i_LL;                //printf("range max=%08x\n", range->hex_addr_max);            }        }        memset(line_buff, 0, LINE_LEN);    }    fclose(fp);    //printf("ela show up %d times\n", hex_ela_cnt);    //printf("start=%08x\n  end=%08X\n", range->hex_addr_min, range->hex_addr_max);    return 0;}

main.c

#include "hex_rec_als.h"int hex_reindex(unsigned int arry[], unsigned int index[], int len);int main(int argc, char **argv){    if(argc < 3)    {        printf("Usage: %s src1.hex target.hex\n", argv[0]);        exit(EXIT_FAILURE);    }    //00-get current work directory.    char cwd[PATH_LEN] = {0};    char new_file_path[PATH_LEN] = {0};    if(NULL == getcwd(cwd, PATH_LEN))    {        perror("getcwd");        exit(EXIT_FAILURE);    }    printf("cwd=%s\n", cwd);    sprintf(new_file_path, "%s\\%s.hex", cwd, argv[argc-1]);    printf("creat path=%s\n", new_file_path);    //01-create new hex file.    FILE *fp_tar = fopen(new_file_path, "w");    if(fp_tar == NULL)    {        perror("fopen ERR");        exit(EXIT_FAILURE);    }    //02-打开所有文件,确定地址范围。    int i = 0;    hex_addr_range_t arr_range[argc-2];    for(i = 0; i <= argc-3; i++)    {        get_hex_addr_range(argv[i+1], &arr_range[i]);        printf("%s address range from 0x%08X to 0x%08X\n", argv[i+1], arr_range[i].hex_addr_min, arr_range[i].hex_addr_max);    }    //03-生成新的index,合并时按照新的index顺序来。    unsigned int hex_addr_min[argc-2];    int new_index[argc-2];    for(i = 0; i <= argc-3; i++)        hex_addr_min[i] = arr_range[i].hex_addr_min;    hex_reindex(hex_addr_min, new_index, argc-2);    //04-按照新的index打开所有文件,必须成功打开所有文件。    FILE *fp_src[argc-2];    for(i = 0; i <= argc-3; i++)    {        fp_src[i] = fopen(argv[new_index[i]], "r");        if(NULL == fp_src[i])        {            printf("Open %s ERR.", argv[i+1]);            perror("fopen");            exit(EXIT_FAILURE);        }    }    //XX-除了最后一个不用去尾,其余都要去尾。    for(i = 0; i <= argc-3; i++)    {        char line_buff[LINE_LEN] = {0};        unsigned int line_num = 0;        if(i != argc-3)        {            while(NULL != fgets(line_buff, LINE_LEN, fp_src[i]))            {                line_num++;                int line_flag = is_hex_rec_XXX(line_buff);                if(line_flag != HEX_NOT && line_flag != HEX_EOF)                    fwrite(line_buff, sizeof(char), strlen(line_buff), fp_tar);                memset(line_buff, 0, LINE_LEN);            }        }        //最后一个不用去尾        else        {            while(NULL != fgets(line_buff, LINE_LEN, fp_src[i]))            {                line_num++;                int line_flag = is_hex_rec_XXX(line_buff);                if(line_flag != HEX_NOT)                    fwrite(line_buff, sizeof(char), strlen(line_buff), fp_tar);                memset(line_buff, 0, LINE_LEN);            }        }        fclose(fp_src[i]);        printf("%05u lines in %s\n", line_num, argv[i+1]);    }    printf("Already merge:\n");    for(i = 0; i <= argc-3; i++)        printf("%02d-->%s\n", i+1, argv[new_index[i]]);    printf("into file %s\n", new_file_path);    fclose(fp_tar);    //XX-验证合并后的地址范围是否正确。    hex_addr_range_t tar_range;    get_hex_addr_range(new_file_path, &tar_range);    printf("%s address range from 0x%08X to 0x%08X\n", new_file_path, tar_range.hex_addr_min, tar_range.hex_addr_max);    return 0;}int hex_reindex(unsigned int arry[], unsigned int index[], int len){    int i, j;    for(i = 0; i < len; i++)    {        index[i] = 0;        for(j = 0; j < len; j++)            index[i] = arry[i]<arry[j]?index[i]:index[i]+1;    }    /*    for(i = 0; i < len; i++)        printf("%d\t", index[i]);    printf("\n");    */    return 0;}