hex文件分析+Qt5制作Hex文件转Bin文件的工具(含源码+工具下载)

目录

1.hex文件分析

2.hex文件和bin文件的区别

3.基于qt开发hex转bin工具


前言:很多MCU代码编译器都会生成hex文件,hex文件的用途很多。有些直接把它放进U盘,然后给MCU自举升级,在MCU读取hex文件的时候需要将hex里的数据转换之后才能写到自身的flash里,给自身升级。当然,也可以先把hex转换成bin文件,再放进U盘给MCU自举升级,这样MCU从bin里读到的数据可以直接写到flash里,从而提高大大提高升级速度。

1.hex文件分析

下面截取从hex文件截图了几行:


        第一行:02000040800F2  即: 0x02 0x00 0x00 0x04 0x08 0x00 0xF2,都是一个个的十六进制数。一行数据中不同的颜色代表不同的信息域:

以上面的数据行格式分析下面一行:

       各个域的信息分别为: 

                     数据长度:0x01
                     数据地址:0x0000
                     数据类型:0x00
                     数据:0807002001010008CB020008C3020008
                     校验值:0x15

其中,数据类型有以下几类:

00:数据记录
01:文件结束记录
02:扩展段地址记录
03:开始段地址记录
04:扩展线性地址记录
05:开始线性地址记录

扩展段地址记录和扩展线性地址记录是用来干什么的呢?

行格式里的数据地址用两个字节保存,最大值0xFFFF,只能表示64k;一般很多的MCU里的flash都超过64k,为了保存高地址的数据,就有了扩展段地址扩展线性地址

当读取这行的数据类型是0x04(扩展线性地址)时,那么,这行的数据就是随后数据的基地址。例如:

第一行的数据类型是0x04(扩展线性地址),它的数据0x0800,就是第二行数据的基地址。第二行的数据类型是0x00(数据记录),它的地址是0x0000,那么它的数据0807002001010008CB020008C3020008要写入flash的地址为(0x0800<<16)|0x0000 ,也就是写入flash的0x0800 0000这个地址。第三行的数据的写入地址为0x0800 0010,该基地址保留到下一个扩展记录为止。

当读取这行的数据类型是0x02(扩展段地址)时,例如:

第一行的数据类型是0x02(扩展段地址) ,它的数据是0x1200;那么,第二行的数据00000000472105014B21050100000000要写flash的地址为(0x1200<<2)+0x2460,也就是写入flash的0x0001 4460这个地址第三行也是以0x1200为基地址,同样的计算出真正写入的地址。

如果用Notepad+编辑器打开hex文件,它会把不同的数据域标记不同的颜色,而且,如果最后的校验值是的对话,标注为绿色,若校验值错误,标注为黄色。

到此,我们已经大概知道怎么解读hex文件了。下面可以开始尝试把它应用起来。

2.hex文件和bin文件的区别

第一点:

hex文件和bin文件都是进行MCU开发时生成的烧录文件,hex文件是携带了数据和其数据对应在MCU flash存放的地址;bin文件纯属数据,那么怎么知道它的数据该怎么保存在flash里呢?bin里的数据是没有地址,但是它的数据时连续的,只要确定数据的写入起始地址,那么所有数据就在这个起始地址开始递增写入就行了。当我们用上位机Flash Loader Demo烧录芯片,用的烧录文件就是bin文件,烧录的时候是要先配置flash起始烧录的地址。

第二点:

hex文件的数据需要经过转换才能写入flash,这个转换过程可以发生在烧录器里、烧录的上位机里或者是MCU自举升级时先转换在写flash。bin文件数据可以直接复制到相应的flash位置上,不需要再经过任何处理。

第三点:

同一个工程编译出来的hex文件肯定比bin文件大,所以bin文件具有节省内存、便于传输的优点。为什么hex文件会比bin文件大呢?看下面例子:

char hex[9] = "12487936";//占用9字节
char bin[4]={0x12,0x48,0x79,0x36};//只占用4字节

hex文件里的数据时可以打印显示出来的,而bin文件是二进制数据。 

接下来我们尝试怎么把hex文件转换成bin文件。

3.基于qt开发hex转bin工具

如果用keil工具进行编译,keil提供了直接生成bin文件的插件,在配置里加入:

这样就能直接生成bin文件,文件名为UPDATA.bin。而接下来要做的是,基于qt自己编写一个exe程序,通过读入hex文件转换生成bin文件。

1.新建一个Qt控制台应用:

2.先看main函数:

#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <stdlib.h>
#include <QByteArray>
#include <QDataStream>


typedef struct{
    unsigned char datalen;//数据字节
    unsigned short addr;//地址域
    unsigned char datatype;//类型
    unsigned char databuf[16];//数据记录
    unsigned char checkout;//校验和
}HexFormatForLine ;

bool ReadHexLineData(HexFormatForLine* out,const QByteArray & ba);//false: 校验错误 true:校验成功
char HexToBin(HexFormatForLine* ba,QDataStream & out);//return 0: ok  1:hex文件结束 2:hex文件有误

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString filepath=QString(argv[1]);//获取hex文件路径
    QFile hexfile(filepath);
    QFile outfile;

    outfile.setFileName(QString(argv[2]));//获取bin文件的保存路径
    if(outfile.open(QIODevice::WriteOnly)==false){
        qDebug("创建bin文件失败!");
         a.exit(0);
    }
    QDataStream out(&outfile);

    if(hexfile.open(QIODevice::ReadOnly)==false){
        qDebug("打开hex文件失败!");
        outfile.close();
         a.exit(0);
    }
    
    QByteArray alinedata;
    HexFormatForLine HexDataStr;
    while(!hexfile.atEnd()){//循环处理,至hex文件读完
        /*若: alinedata =QByteArray::fromHex(":12345678");
          则: alinedara ={0x12,0x23,0x45,0x78};*/
        alinedata = QByteArray::fromHex(hexfile.readLine());//从hex文件中读取一行
        bool ret = ReadHexLineData(&HexDataStr,alinedata);//将一行数据解读到HexDataStr结构体
        if(!ret){
            qDebug("校验出错,hex文件有误.");
            outfile.remove();//删除输出的bin文件
            hexfile.close();//关闭输入文件
             a.exit(0);
        }
        ret=HexToBin(&HexDataStr,out);//将解读后的数据写入bin文件
        if(ret!=0){
            break;
        }
    }

    qDebug() <<"hex2bin ok.";
    hexfile.close();
    outfile.close();
    a.exit(0);
}

编程要点: 

  • 通过argv参数获取hex文件的路径和配置bin文件的保存路径
  • QFile文件读写操作
  • 利用fromHex函数将hex里的字符串转换成16进制编码的数组。
  • 在while循环里,每一次循环读取hex文件的一行数据进行处理,ReadHexLineData函数将一行数据里的每一个域解析并保存在HexDataStr结构体里面。 然后将该结构体传入HexToBin函数处理。

3.下面提供ReadHexLineData和HexToBin函数:

bool ReadHexLineData(HexFormatForLine* out,const QByteArray & ba)//false: 校验错误 true:校验成功
{
     unsigned char i,checkoutCal=0;

    //计算校验值
     for(i=0;i < ba.size()-1;i++){
        checkoutCal += (unsigned char)ba.at(i);
     }
     checkoutCal = 0x100-checkoutCal;
     //获取个部分域的值
     out->datalen =(unsigned char)ba.at(0);
     out->addr = ((unsigned char)ba.at(1)<<8)|(unsigned char)ba.at(2);
     out->datatype = (unsigned char)ba.at(3);
     memset(out->databuf,0,sizeof(out->databuf));
     for(i = 0;i<out->datalen;i++){
        out->databuf[i] = (unsigned char)ba.at(4+i);
     }
     out->checkout = (unsigned char)ba.at(4+i);
#if  0 //调试时打开
     qDebug("datalen=%X",out->datalen);
     qDebug("addr=%X",out->addr);
     qDebug("datatype=%X",out->datatype);
     qDebug("checkout=%X",out->checkout);
     qDebug("checkoutCal=%X",checkoutCal);
#endif
    //比较读取的校验值和计算的校验值是否一致
    if(checkoutCal == out->checkout){
        return true;
    }
    return false;
}

char HexToBin(HexFormatForLine* ba,QDataStream & out)//return 0: ok  1:hex文件结束 2:hex文件有误
{
    static unsigned int ExStageAddr = 0x00;//扩展段地址
    static unsigned int ExLineAddr = 0x00;//扩展线性地址
    static unsigned int absoluteAddrLocal = 0x00;//本地记录绝对地址
    unsigned int absoluteAddrCurrent = 0x00;//计算当前记录的绝对地址
    unsigned int Bytesskipped = 0;//被跳过的字节数

    switch(ba->datatype){
        case 0x00://数据记录
            //计算出当前记录的绝对地址
            if(ExStageAddr != 0){
                absoluteAddrCurrent = (ba->addr+ExStageAddr);
            }else if(ExLineAddr != 0){
                absoluteAddrCurrent = (ba->addr|ExLineAddr);
            }else{
                absoluteAddrCurrent = ba->addr;
            }
            //hex文件第一条数据记录时,将本地绝对地址absoluteAddrLocal同步等于当前记录的绝对地址absoluteAddrCurrent
            if(absoluteAddrLocal == 0){
                    absoluteAddrLocal = absoluteAddrCurrent;
            }
            Bytesskipped = absoluteAddrCurrent-absoluteAddrLocal;//比较当前记录的绝对地址absoluteAddrCurrent和本地的绝对地址absoluteAddrLocal是否有偏差
        break;
        case 0x01://文件结束记录
            return 1;
        break;
        case 0x02://扩展段地址记录
            ExStageAddr = (ba->databuf[0]<<8|ba->databuf[1])<<2;
            ExLineAddr = 0x00;
            return 0;//return ok
        break;
        case 0x04://扩展线性地址记录
            ExLineAddr = (ba->databuf[0]<<8|ba->databuf[1])<<16;
            ExStageAddr = 0x00;
            return 0;//return ok
        break;
        default:
            return 2;
        break;
    }

    for(unsigned int i = 0;i < Bytesskipped;i++){//被跳过的地址,填充0
        out <<(unsigned char)0x00;
    }
    if(Bytesskipped!=0){
        qDebug() <<Bytesskipped;
    }
    absoluteAddrLocal += Bytesskipped;//本地绝对地址absoluteAddrLocal累加

    for(unsigned int i = 0;i < ba->datalen;i++){
        out <<ba->databuf[i];
    }
    absoluteAddrLocal += ba->datalen;//本地绝对地址absoluteAddrLocal累加

    return 0;//return ok
}

将上面两块代码复制在工程,选择Release,点击build,编译出exe程序。这里不能点击run,因为点击run默认不会写argv参数。我们需要argv参数提供hex文件路径和bin文件的保存路径(这里可以在代码里加入一个默认路径,这样,若argv为空则使用默认路径)。

4.将生成的exe文件复制到单独的文件夹:

在电脑左下角搜索qt,找到Qt 5.4 for Desktop (MinGW 4.9 32 bit) 工具,点击打开,切换到刚才保存exe文件的目录,输入windeployqt hex2bin.exe,这个就可以把exe需要的库文件复制到exe保存路径下:

红框里的文件可以删除来节约空间:

 

5.接下来把需要转换的hex文件也放进这个文件夹,同时增加了run_hex2bin.bat文件,这个是为了调用hex2bin.exe,bat里面的内容是:

hex2bin.exe  ./UPDATA.hex ./UPDATA.bin

 

 点击run_hex2bin.bat,就会运行hex2bin.exe文件,然后生成的bin文件就会保存在该目录下。

6.前面提到过用keil自带的插件可以生成bin文件,那么现在可以在keil工程配置那里,after build之后凋用我们自己做的hex2bin.exe来将hex文件转换成bin文件。当然前提要把hex2bin.exe文件及其需要的dll文件全部复制到keil工程文件uvproj所在的目录下。再进行如下配置:

将keil工程编译一次,在对比用fromelf和自己做的hex2bin.exe生成的bin文件是否完成一致,我试过是一样的。

发布了32 篇原创文章 · 获赞 187 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章