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

╰半夏微凉° 2022-10-22 14:53 858阅读 0赞

文章目录

  • 序言
  • Hex文件格式解析
  • HEX文件的格式
  • 使用C语言将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文件记录的基本格式如下:






















RecordMark RecordLength LoadOffset RecordType Data Checksum
记录标志 记录长度 装载偏移 记录类型 数据 校验和

 其中,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位格式,其详细格式如下:










































记录名 RecordMark RecordLength LoadOffset RecordType Data Checksum
记录名 记录标志 记录长度 装载偏移 记录类型 数据 校验和
内容 “:” X - “00” - -
字节数 1 1 2 1 X 1

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






































记录名 RecordMark RecordLength LoadOffset RecordType Checksum
记录名 记录标志 记录长度 装载偏移 记录类型 校验和
内容 “:” “00” “0000” “01” “FF”
字节数 1 1 2 1 1

 比如说,有如下一条数据记录:“: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文件

  1. bool ReadHexLineData(HexFormatForLine* out,const QByteArray &ba)//false: 校验错误 true:校验成功
  2. {
  3. unsigned char i,checkoutCal=0;
  4. //计算校验值
  5. for(i=0;i < ba.size()-1;i++){
  6. checkoutCal += (unsigned char)ba.at(i);
  7. }
  8. checkoutCal = 0x100-checkoutCal;
  9. //获取个部分域的值
  10. out->datalen =(unsigned char)ba.at(0);
  11. out->addr = ((unsigned char)ba.at(1)<<8)|(unsigned char)ba.at(2);
  12. out->datatype = (unsigned char)ba.at(3);
  13. memset(out->databuf,0,sizeof(out->databuf));
  14. for(i = 0;i<out->datalen;i++){
  15. out->databuf[i] = (unsigned char)ba.at(4+i);
  16. }
  17. out->checkout = (unsigned char)ba.at(4+i);
  18. #if 0 //调试时打开
  19. qDebug("datalen=%X",out->datalen);
  20. qDebug("addr=%X",out->addr);
  21. qDebug("datatype=%X",out->datatype);
  22. qDebug("checkout=%X",out->checkout);
  23. qDebug("checkoutCal=%X",checkoutCal);
  24. #endif
  25. //比较读取的校验值和计算的校验值是否一致
  26. if(checkoutCal == out->checkout){
  27. return true;
  28. }
  29. return false;
  30. }
  31. char HexToBin(HexFormatForLine* ba,QDataStream & out)//return 0: ok 1:hex文件结束 2:hex文件有误
  32. {
  33. static unsigned int ExStageAddr = 0x00;//扩展段地址
  34. static unsigned int ExLineAddr = 0x00;//扩展线性地址
  35. static unsigned int absoluteAddrLocal = 0x00;//本地记录绝对地址
  36. unsigned int absoluteAddrCurrent = 0x00;//计算当前记录的绝对地址
  37. unsigned int Bytesskipped = 0;//被跳过的字节数
  38. switch(ba->datatype)
  39. {
  40. case 0x00://数据记录
  41. //计算出当前记录的绝对地址
  42. if(ExStageAddr != 0){
  43. absoluteAddrCurrent = (ba->addr+ExStageAddr);
  44. }else if(ExLineAddr != 0){
  45. absoluteAddrCurrent = (ba->addr|ExLineAddr);
  46. }else{
  47. absoluteAddrCurrent = ba->addr;
  48. }
  49. //hex文件第一条数据记录时,将本地绝对地址absoluteAddrLocal同步等于当前记录的绝对地址absoluteAddrCurrent
  50. if(absoluteAddrLocal == 0){
  51. absoluteAddrLocal = absoluteAddrCurrent;
  52. }
  53. //比较当前记录的绝对地址absoluteAddrCurrent和本地的绝对地址absoluteAddrLocal是否有偏差
  54. Bytesskipped = absoluteAddrCurrent-absoluteAddrLocal;
  55. break;
  56. case 0x01://文件结束记录
  57. return 1;
  58. break;
  59. case 0x02://扩展段地址记录
  60. ExStageAddr = (ba->databuf[0]<<8|ba->databuf[1])<<2;
  61. ExLineAddr = 0x00;
  62. return 0;//return ok
  63. break;
  64. case 0x04://扩展线性地址记录
  65. ExLineAddr = (ba->databuf[0]<<8|ba->databuf[1])<<16;
  66. ExStageAddr = 0x00;
  67. return 0;//return ok
  68. break;
  69. default:
  70. return 2;
  71. break;
  72. }
  73. for(unsigned int i = 0;i < Bytesskipped;i++){ //被跳过的地址,填充0
  74. out <<(unsigned char)0x00;
  75. }
  76. if(Bytesskipped!=0){
  77. qDebug() <<Bytesskipped;
  78. }
  79. absoluteAddrLocal += Bytesskipped;//本地绝对地址absoluteAddrLocal累加
  80. for(unsigned int i = 0;i < ba->datalen;i++){
  81. out <<ba->databuf[i];
  82. }
  83. absoluteAddrLocal += ba->datalen;//本地绝对地址absoluteAddrLocal累加
  84. return 0;
  85. }
  86. void do_hex2bin(void)
  87. {
  88. QFile hexfile(ui->edtFileName->text());//获取hex文件路径
  89. // 就在原目录下生成一个bin文件
  90. QFile outfile(ui->edtFileName->text().replace("hex", "bin")); // 生成的目标bin文件
  91. if (!hexfile.open(QIODevice::ReadOnly)) // hex文件
  92. {
  93. qDebug("file open fail!");
  94. return;
  95. }
  96. if (!outfile.open(QIODevice::WriteOnly)) // bin文件
  97. {
  98. qDebug("file open fail!");
  99. return;
  100. }
  101. QDataStream out(&outfile);
  102. QByteArray alinedata;
  103. HexFormatForLine HexDataStr;
  104. while(!hexfile.atEnd()){ //循环处理,至hex文件读完
  105. /*若: alinedata =QByteArray::fromHex(":12345678"); 则: alinedara ={0x12,0x23,0x45,0x78};*/
  106. alinedata = QByteArray::fromHex(hexfile.readLine());//从hex文件中读取一行
  107. bool ret = ReadHexLineData(&HexDataStr,alinedata);//将一行数据解读到HexDataStr结构体
  108. if(!ret){
  109. qDebug("校验出错,hex文件有误.");
  110. outfile.remove();//删除输出的bin文件
  111. hexfile.close();//关闭输入文件
  112. }
  113. ret=HexToBin(&HexDataStr,out);//将解读后的数据写入bin文件
  114. if(ret!=0){
  115. break;
  116. }
  117. }
  118. qDebug("hex2bin ok");
  119. hexfile.close();
  120. outfile.close();
  121. }

发表评论

表情:
评论列表 (有 0 条评论,858人围观)

还没有评论,来说两句吧...

相关阅读

    相关 HEX文件格式详解

    Hex文件是可以烧录到MCU中,被MCU执行的一种文件格式。如果用记事本打开可发现,整个文件以行为单位,每行以冒号开头,内容全部为16进制码(以ASCII码形式显示)。Hex文

    相关 HEX文件格式

    HEX文件格式学习笔记 为了编写一个可以按照自己的要求进行ISP的程序,大概学习了一下HEX文件格式。把学习笔记写出来,以为重新巩固所学习内容。 HEX文件以行为单位。每行

    相关 hex文件分析

    用文本随便打开一个文件都可以看到hex文件的内容。例如: :020000040800F2 :1000000080040020690100087101000873010008