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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章