来源:玩转嵌入式

含有单片机的电子产品在量产的时候会用到.hex文件或者.bin。hex是十六进制的,包含地址信息和数据信息,而bin文件是二进制的,只有数据而不包含地址。任何文件都有一定的格式规范,hex文件同样具有完整的格式规范。今天和大家分享一下,hex是如何解析的。
1.hex文件解析
hex文件可以通过UltraEdit、Notepad++、记事本等工具打开,用Notepad++打开之后会看到如下数据内容。

使用Notepad++打开后会不同含义的数据其颜色不同。每行数据都会有一个冒号开始,后面的数据由:数据长度、地址、标识符、有效数据、校验数据等构成。以上图的第一行为例,进行解析:
第1个字节10,表示该行具有0x10个数据,即16个字节的数据;
第2、3个字节C000,表示该行的起始地址为0xC000;
第4个字节00,表示该行记录的是数据;
第5-20个字节,表示的是有效数据;
第21个字节73,表示前面数据的校验数据,校验方法:0x100-前面字节累加和
其中,第4个字节具有5种类型:00-05,含义如下:
字段含义00表示后面记录的是数据01表示文件结束02表示扩展段地址03表示开始段地址04表示扩展线性地址05表示开始线性地址
单片机的hex文件以00居多,都用来表示数据。hex文件的结束部分如下图所示。

最后一行的01表示文件结束了,最后的FF表示校验数据,由0x100-0x01=0xFF得来。
2.扩展地址
细心的同学可能发现了,上面的地址都是两个字节,范围从0x000-0xFFFF,如果地址是0x17FFFF该怎么办呢?这就要用到扩展字段了,举例如下:

第一行中,第一个字节为0x02,表示只有两个字节的数据,而扩展段的标识符为0x04表示后面的数据0x0800为扩展线性地址,基地址的计算方法为:
(0x0800<<16)=0x08000000,在0x04标识段出现之前,下面的数据都是这个基地址。
第二行的地址是0x0000,那么实际地址应是0x08000000+0x0000=0x08000000;
第二行的地址是0x0010,那么实际地址应是0x08000000+0x0010=0x08000010;
使用Notepad++工具,可以根据颜色的不同来确认校验数据是否正确,如果校验数据的颜色不是绿色,则表示校验结果是错的。
3.程序如何实现hex解析经常会用到上位机软件来实现单片机的烧录,那上位机就要解析hex文件,程序如何实现hex文件的解析呢?
头文件代码如下所示:
#ifndef _HEXLEXER_H_
  • #define _HEXLEXER_H_
  • #include <cstdio>
  • #include <cstring>
  • #include <cstdlib>
  • /*
  • Intel Hex文件解析器V1.0
  • Hex文件的格式如下:
  • RecordMark  RecordLength  LoadOffset  RecordType  Data  Checksum
  • 在Intel Hex文件中,RecordMark规定为“:”
  • */
  • #pragma warning(disable:4996)
  • #define MAX_BUFFER_SIZE 43
  • class Hex
  • {
  • public:
  •   Hex(char mark);
  •   ~Hex();
  •   void ParseHex(char *data);//解析Hex文件
  •   void ParseRecord(char ch);//解析每一条记录
  •   size_t GetRecordLength();//获取记录长度
  •   char GetRecordMark();//获取记录标识
  •   char *GetLoadOffset();//获取内存装载偏移
  •   char *GetRecordType();//获取记录类型
  •   char *GetData();//获取数据
  •   char *GetChecksum();//获取校验和
  •   
  • private:
  •   char m_cBuffer[MAX_BUFFER_SIZE];//存储待解析的记录
  •   char m_cRecordMark;//记录标识
  •   size_t m_nRecordLength;//记录长度
  •   char *m_pLoadOffset;//装载偏移
  •   char *m_pRecordType;//记录类型
  •   char *m_pData;//数据字段
  •   char *m_pChecksum;//校验和
  •   bool m_bRecvStatus;//接收状态标识
  •   //size_t m_nIndex;//缓存的字符索引值
  • };
  • Hex::Hex(char mark)
  • {
  •   this->m_cRecordMark = mark;
  •   m_cBuffer[0] = '\0';
  •   //m_pBuffer = NULL;
  •   m_nRecordLength = 0;
  •   m_pLoadOffset = NULL;
  •   m_pRecordType = NULL;
  •   m_pData = NULL;
  •   m_pChecksum = NULL;
  •   m_bRecvStatus = false;
  •   //m_nIndex = 0;
  • }
  • Hex::~Hex()
  • {
  •   delete m_pLoadOffset, m_pRecordType, m_pData, m_pChecksum;
  • }
  • #endif
  • 复制代码
    C代码如下:
    #include "HexLexer.h"
  • #include <iostream>
  • using namespace std;
  • //获取记录标识
  • char Hex::GetRecordMark()
  • {
  •   return this->m_cRecordMark;
  • }
  • //获取每条记录的长度
  • size_t Hex::GetRecordLength()
  • {
  •   //char *len = (char*)malloc(sizeof(char)* 3);
  •   if (strlen(m_cBuffer)>=2)
  •   {
  •     char len[3];
  •     len[0] = m_cBuffer[0];
  •     len[1] = m_cBuffer[1];
  •     len[2] = '\0';
  •     char *p = NULL;
  •     return strtol(len, &p, 16);
  •   }
  •   else
  •   {
  •     return 0;
  •   }
  • }
  • //获取装载偏移
  • char* Hex::GetLoadOffset()
  • {
  •   if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
  •   {
  •     char *offset = (char*)malloc(sizeof(char)* 5);
  •     for (int i = 0; i < 4; ++i)
  •     {
  •       offset[i] = m_cBuffer[i + 2];
  •     }
  •     offset[4] = '\0';
  •     m_pLoadOffset = offset;
  •     offset = NULL;
  •   }
  •   return m_pLoadOffset;
  • }
  • //获取记录类型
  • char* Hex::GetRecordType()
  • {
  •   if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
  •   {
  •     char *type=(char*)malloc(sizeof(char)*3);
  •     type[0] = m_cBuffer[6];
  •     type[1] = m_cBuffer[7];
  •     type[2] = '\0';
  •     m_pRecordType = type;
  •     type = NULL;
  •   }
  •   return m_pRecordType;
  • }
  • //获取数据
  • char* Hex::GetData()
  • {
  •   if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
  •   {
  •     int len = GetRecordLength();
  •     char *data = (char*)malloc(sizeof(char)*(len * 2 + 1));
  •     for (int i = 0; i < len * 2;++i)
  •     {
  •       data[i] = m_cBuffer[i + 8];
  •     }
  •     data[len * 2] = '\0';
  •     m_pData = data;
  •     data = NULL;
  •   }
  •   return m_pData;
  • }
  • //获取校验和
  • char* Hex::GetChecksum()
  • {
  •   int len = GetRecordLength();
  •   if (strlen(m_cBuffer) == (len + 5) * 2)
  •   {
  •     char *checksum=(char*)malloc(sizeof(char)*3);
  •     checksum[0] = m_cBuffer[(len + 5) * 2 - 2];
  •     checksum[1] = m_cBuffer[(len + 5) * 2-1];
  •     checksum[2] = '\0';
  •     m_pChecksum = checksum;
  •     checksum=NULL;
  •   }
  •   return m_pChecksum;
  • }
  • //解析Hex文件中的每一条记录
  • void Hex::ParseRecord(char ch)
  • {
  •   size_t buf_len = strlen(m_cBuffer);
  •   if (GetRecordMark()==ch)
  •   {
  •     m_bRecvStatus = true;
  •     m_cBuffer[0] = '\0';
  •     //m_nIndex = 0;
  •     return;
  •   }
  •   if ((buf_len==(GetRecordLength()+5)*2-1))
  •   {
  •     //接收最后一个字符
  •     m_cBuffer[buf_len] = ch;
  •     m_cBuffer[buf_len + 1] = '\0';
  •     //检验接收的数据
  •     char temp[3];
  •     char *p = NULL;
  •     long int checksum = 0;
  •     for (int i = 0; i < strlen(m_cBuffer);i+=2)
  •     {
  •       temp[0] = m_cBuffer[i];
  •       temp[1] = m_cBuffer[i + 1];
  •       temp[2] = '\0';
  •       checksum += strtol(temp, &p, 16);
  •       temp[0] = '\0';
  •     }
  •     checksum &= 0x00ff;//取计算结果的低8位
  •     if (checksum==0)//checksum为0说明接收的数据无误
  •     {
  •       cout << "RecordMark " << GetRecordMark() << endl;
  •       cout << "RecordLength " << GetRecordLength() << endl;
  •       cout << "LoadOffset " << GetLoadOffset() << endl;
  •       cout << "RecordType " << GetRecordType() << endl;
  •       cout << "Data " << GetData() << endl;
  •       cout << "Checksum " << GetChecksum() << endl;
  •     }
  •     else//否则接收数据有误
  •     {
  •       cout << "Error!" << endl;
  •     }
  •     m_cBuffer[0] = '\0';
  •     m_bRecvStatus = false;
  •     m_nRecordLength = 0;
  •     m_pLoadOffset = NULL;
  •     m_pRecordType = NULL;
  •     m_pChecksum = NULL;
  •     m_bRecvStatus = false;
  •   }
  •   else if (m_bRecvStatus)
  •   {
  •     m_cBuffer[buf_len] = ch;
  •     m_cBuffer[buf_len + 1] = '\0';
  •     //m_nIndex++;
  •   }
  • }
  • //解析Hex文件
  • void Hex::ParseHex(char *data)
  • {
  •   for (int i = 0; i < strlen(data);++i)
  •   {
  •     ParseRecord(data[i]);
  •   }
  • }
  • int main(int argc, char *argv[])
  • {
  •   freopen("in.txt", "r", stdin);
  •   freopen("out.txt", "w", stdout);
  •   Hex hex(':');
  •   char ch;
  •   while (cin>>ch)
  •   {
  •     hex.ParseRecord(ch);
  •   }
  •   fclose(stdout);
  •   fclose(stdin);
  •   return 0;
  • }
  • 复制代码