嵌入式--Hex文件总结:Hex文件格式,Hex文件转bin文件

序言

 这里所说的hex和bin文件是在嵌入式开发时,IDE生成的hex文件或bin文件。它们都是有一定的标准的制式文件。

  • hex文件:不能直接烧录到主控芯片的Flash里面去,烧录进入后也不能运行,它是为了方便烧录工具/软件烧录。换句话说就是hex文件里面的有用的数据是被“打包”到一行一行的数据包中去了。
  • bin文件:可以直接烧录到主控芯片的Flash里面去,就直接“拷贝”到主控芯片的0x8000000地址(一般来说ARM内核的芯片)地址累加,就可以运行了。

 如果你想自己写个烧录软件,那就有必要写个算法将hex文件转为bin文件,然后,使用软件直接将转换好的bin文件“拷贝”到芯片的相关地址(一般是0x8000000)就可以了。

Hex文件格式解析

 Hex文件如果用特殊的程序来查看(一般记事本就可以实现)。打开后可发现,整个文件以行为单位,每行以冒号开头,内容全部为16进制码(以ASCII码形式显示)。Hex文件可以按照如下的方式进行拆分来分析其中的内容:
例如:

:020000040000FA , 我把它看做 0x02 0x00 0x00 0x04 0x00 0x00 0xFA

第一个 0x02 为数据长度。
紧跟着后面的0x00 0x00 为地址。
再后面的0x04为数据类型,类型共分以下几类:

  • ‘00’ Data Record:数据记录
  • ‘01’ End of File Record:文件结束记录
  • ‘02’ Extended Segment Address Record:扩展段地址记录
  • ‘03’ Start Segment Address Record:开始段地址记录
  • ‘04’ Extended Linear Address Record:扩展线性地址记录
  • ‘05’ Start Linear Address Record:开始线性地址记录

然后,接着0x04后面的两个 0x00 0x00就是数据。最后一个0xFA是校验码。
HEX文件的每一行都是这样的格式:

HEX文件的格式

 Intel HEX格式的文件是由多条记录组成,而每条记录又是由6个字段组成。这些记录是由一些代表机器语言代码和常量的16进制数据组成的。Intel HEX 文件常用来传输要存储在 ROM 或者 EPROM 中的程序和数据。大部分的 EPROM 编程器能使用 Intel HEX文件记录的基本格式如下:

RecordMarkRecordLengthLoadOffsetRecordTypeDataChecksum
记录标志记录长度装载偏移记录类型数据校验和

 其中,RecordMark字段其实就是每条记录的首部,其值为0x3A,在ASCII码中就是冒号“:”。该字段在HEX文件中,这个头部只占有一个字节。
RecordLength表示每条记录包含的数据的长度,以字节为单位,最大描述255个字节,表现为2个16进制的字符,该字段在HEX文件中占2个字节。
LoadOffset表示该记录中的数据在整个存取器空间中的偏移,用4个十六进制字符描述一个16位数据,在HEX文件中该字段占有4个字节。
RecordType表示记录类型,表现为2个十六进制字符。取值有以下几种:

  • 00 表示数据记录;

  • 01 表示文件结束记录;

  • 02 描述拓展段地址记录;

  • 03 描述开始段地址记录;

  • 04 描述扩展线性地址记录;

  • 05 描述开始线性地址记录。

Data字段表示数据的具体内容,描述方法仍是两个16进制的字符表示1字节的数据。此字段的长度由该记录的RecordLength决定,数据的解释取决于记录类型(RecordType)。
Checksum字段为校验和。这个校验和是这么来的,将RecordMark(“:”)后的所有的数据按字节相加,即成对相加起来,然后模除256得到余数,再对这个余数求补码,最终得出的结果就是校验和。所以检测方法也很简单:在每一条记录内,将RecordMark(“:”)后的所有数据(包括Checksum)按字节相加后得到的8位数据为0,则说明数据无误,否则说明出错了。

 至于什么是拓展段地址记录、开始段地址记录、扩展线性地址记录、开始线性地址记录这里不做详细的介绍,在芯艺的《AVR单片机GCC程序设计》的附录部分有详细的说明。而在Arduino的HEX文件中,记录类型只有两种,数据记录和文件结束记录。所以RecordType这个字段的值不是0x00就是0x01。

数据记录适用于8位、16位和32位格式,其详细格式如下:

记录名RecordMarkRecordLengthLoadOffsetRecordTypeDataChecksum
记录名记录标志记录长度装载偏移记录类型数据校验和
内容“:”X-“00”--
字节数1121X1

文件结束记录适用于8位、16位和32位格式,其详细格式如下:

记录名RecordMarkRecordLengthLoadOffsetRecordTypeChecksum
记录名记录标志记录长度装载偏移记录类型校验和
内容“:”“00”“0000”“01”“FF”
字节数11211

 比如说,有如下一条数据记录:“:1001A000808184608083808182608083808181609F”,则,其RecordMark为“:”,RecordLength为”10”,这里是16进制的,对应10进制为16,也就是说Data字段有16个字节;LoadOffset的值为01A0,也就是说在该条记录中,数据字段在内存中的起始地址为01A0;RecordType为00,表示是记录类型;Data的值为80818460808380818260808380818160,一共有16个字节;Checksum的值为9F。用计算器按照上面的方式验证一下是可以得到一个低8位为0的数据,也就是说这条记录是合法的。下面开始编码实现对HEX文件的解析。

使用C语言将Hex文件转成Bin文件

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;
            }
            //比较当前记录的绝对地址absoluteAddrCurrent和本地的绝对地址absoluteAddrLocal是否有偏差
            Bytesskipped = 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;
}

void do_hex2bin(void)
{
    QFile hexfile(ui->edtFileName->text());//获取hex文件路径
    // 就在原目录下生成一个bin文件
    QFile outfile(ui->edtFileName->text().replace("hex", "bin")); // 生成的目标bin文件

    if (!hexfile.open(QIODevice::ReadOnly)) // hex文件
    {
        qDebug("file open fail!");
        return;
    }

    if (!outfile.open(QIODevice::WriteOnly)) // bin文件
    {
        qDebug("file open fail!");
        return;
    }

    QDataStream out(&outfile);

    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();//关闭输入文件
        }
        ret=HexToBin(&HexDataStr,out);//将解读后的数据写入bin文件
        if(ret!=0){
            break;
        }
    }
    qDebug("hex2bin ok");
    hexfile.close();
    outfile.close();
}
  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值