近期有一个需求就是为Arduino开发板做一个基于蓝牙的无线烧录程序。眼下的Arduino程序都是通过USB线连接到电脑的主机上,实际的传输过程是基于USB协议的,这个过程还是比較麻烦的。由于每次的编译完以后都须要通过一个USB线来完毕传输烧录的工作,这个还是非常麻烦的。

原理解读

在Arduino中。利用USB来完毕传输烧录大概是这么一个过程。

每一个Arduino源程序。即sketch文件,经过一系列的编译处理以后。终于会形成一个Intel HEX格式的文件。这个HEX文件事实上就一个被封装好的数据包,包括头部、长度、偏移量、数据类型、数据和校验和等6个字段。

再利用USB协议传输这个数据包,再通过板子上已有的bootloader程序把这个HEX文件里的有效字节码写到板子上的Flash存储器中。就是由于这个bootloader程序的存在。才使得多数的单片机具有片内引导程序自编程功能。MCU通过执行这个常驻Flash的bootloader程序。就能够利用不论什么可用的数据接口读代替码(注:在Arduino中,这里的代码是指HEX文件里的有效数据字段,是终于字节码的形式)后写入到自身的flash存储器中,从而实现了自编程的功能。

理解了上面的原理以后,你比方说我要做一个支持无线传输的类似bootloader的东西。无线传输採用蓝牙协议。

蓝牙的接收端将接收到的数据所有存在串口中,所以核心的技术除了蓝牙收发部分以及对flash存储器的读写和存储空间的管理以外,还须要实现对这个HEX文件的解析,更准确、形象地说应该是对这些数据包的解析。怎么把这个数据包拆开,取出有效的字节码数据,然后把这些字节码按顺序分块组装,写到flash存储器。再把PC指针指到開始的地方就能够了。只是这东西确实非常底层,就是在直接跟内存打交道。怎么管理内存。怎么读写内存,怎么拆分组装数据,出错了以后怎么擦除已经写的内容,怎样优化等等一些列问题都须要解决。

事非经过不知难。经过方知难与易。慢慢做吧!先来第一步。对HEX格式的文件进行解析。解析之前自然要弄懂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得到余数,再对这个余数求补码,终于得出的结果就是校验和。所以检測方法也非常easy:在每一条记录内,将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文件的解析。

HEX文件解析的实现

解析过程还是比較简单的。这里直接附上源代码。

HexLexer.h头文件里定义了相关的类的属性和操作。

#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

HexLexer.cpp文件实现了头文件里函数,并给出了測试用例。

#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;
}

in.txt是在Arduino IDE编译blink程序生成的blik.cpp.hex的原始内容,直接复制到记事本中,其内容例如以下:

:100000000C945C000C946E000C946E000C946E00CA
:100010000C946E000C946E000C946E000C946E00A8
:100020000C946E000C946E000C946E000C946E0098
:100030000C946E000C946E000C946E000C946E0088
:100040000C9488000C946E000C946E000C946E005E
:100050000C946E000C946E000C946E000C946E0068
:100060000C946E000C946E00000000080002010069
:100070000003040700000000000000000102040863
:100080001020408001020408102001020408102002
:10009000040404040404040402020202020203032E
:1000A0000303030300000000250028002B000000CC
:1000B0000000240027002A0011241FBECFEFD8E043
:1000C000DEBFCDBF21E0A0E0B1E001C01D92A930AC
:1000D000B207E1F70E94F1010C9401020C940000B8
:1000E00061E08DE00C94810161E08DE00E94BA0135
:1000F00068EE73E080E090E00E94F50060E08DE043
:100100000E94BA0168EE73E080E090E00C94F50084
:100110001F920F920FB60F9211242F933F938F933C
:100120009F93AF93BF938091010190910201A091A1
:100130000301B09104013091000123E0230F2D371A
:1001400020F40196A11DB11D05C026E8230F0296DB
:10015000A11DB11D20930001809301019093020124
:10016000A0930301B09304018091050190910601D1
:10017000A0910701B09108010196A11DB11D8093C6
:10018000050190930601A0930701B0930801BF9168
:10019000AF919F918F913F912F910F900FBE0F9034
:1001A0001F9018953FB7F894809105019091060132
:1001B000A0910701B091080126B5A89B05C02F3F6B
:1001C00019F00196A11DB11D3FBF6627782F892F19
:1001D0009A2F620F711D811D911D42E0660F771FDE
:1001E000881F991F4A95D1F70895CF92DF92EF9219
:1001F000FF92CF93DF936B017C010E94D200EB0151
:10020000C114D104E104F10489F00E9400020E94AB
:10021000D2006C1B7D0B683E734090F381E0C81ADE
:10022000D108E108F108C851DC4FEACFDF91CF9146
:10023000FF90EF90DF90CF900895789484B582601E
:1002400084BD84B5816084BD85B5826085BD85B57A
:10025000816085BDEEE6F0E0808181608083E1E829
:10026000F0E0108280818260808380818160808361
:10027000E0E8F0E0808181608083E1EBF0E0808164
:1002800084608083E0EBF0E0808181608083EAE736
:10029000F0E08081846080838081826080838081BF
:1002A000816080838081806880831092C10008957E
:1002B000833081F028F4813099F08230A1F00895E4
:1002C0008730A9F08830B9F08430D1F48091800073
:1002D0008F7D03C0809180008F7780938000089588
:1002E00084B58F7702C084B58F7D84BD08958091D9
:1002F000B0008F7703C08091B0008F7D8093B000F5
:100300000895CF93DF9390E0FC01E458FF4F2491D0
:10031000FC01E057FF4F8491882349F190E0880F5A
:10032000991FFC01E255FF4FA591B4918C559F4F49
:10033000FC01C591D4919FB7611108C0F8948C91CC
:10034000209582238C93888182230AC0623051F4E5
:10035000F8948C91322F309583238C938881822B53
:10036000888304C0F8948C91822B8C939FBFDF917B
:10037000CF9108950F931F93CF93DF931F92CDB723
:10038000DEB7282F30E0F901E859FF4F8491F901D9
:10039000E458FF4F1491F901E057FF4F04910023F7
:1003A000C9F0882321F069830E9458016981E02FF8
:1003B000F0E0EE0FFF1FEC55FF4FA591B4919FB7F2
:1003C000F8948C91611103C01095812301C0812B99
:1003D0008C939FBF0F90DF91CF911F910F91089544
:1003E00008950E941D010E94F0010E947000C0E06B
:1003F000D0E00E9474002097E1F30E940000F9CF42
:060400000895F894FFCFFF
:00000001FF

解析的结果直接写入到记事本中。结果例如以下:

RecordMark :
RecordLength 16
LoadOffset 0000
RecordType 00
Data 0C945C000C946E000C946E000C946E00
Checksum CA
RecordMark :
RecordLength 16
LoadOffset 0010
RecordType 00
Data 0C946E000C946E000C946E000C946E00
Checksum A8
RecordMark :
RecordLength 16
LoadOffset 0020
RecordType 00
Data 0C946E000C946E000C946E000C946E00
Checksum 98
RecordMark :
RecordLength 16
LoadOffset 0030
RecordType 00
Data 0C946E000C946E000C946E000C946E00
Checksum 88
RecordMark :
RecordLength 16
LoadOffset 0040
RecordType 00
Data 0C9488000C946E000C946E000C946E00
Checksum 5E
RecordMark :
RecordLength 16
LoadOffset 0050
RecordType 00
Data 0C946E000C946E000C946E000C946E00
Checksum 68
RecordMark :
RecordLength 16
LoadOffset 0060
RecordType 00
Data 0C946E000C946E000000000800020100
Checksum 69
RecordMark :
RecordLength 16
LoadOffset 0070
RecordType 00
Data 00030407000000000000000001020408
Checksum 63
RecordMark :
RecordLength 16
LoadOffset 0080
RecordType 00
Data 10204080010204081020010204081020
Checksum 02
RecordMark :
RecordLength 16
LoadOffset 0090
RecordType 00
Data 04040404040404040202020202020303
Checksum 2E
RecordMark :
RecordLength 16
LoadOffset 00A0
RecordType 00
Data 0303030300000000250028002B000000
Checksum CC
RecordMark :
RecordLength 16
LoadOffset 00B0
RecordType 00
Data 0000240027002A0011241FBECFEFD8E0
Checksum 43
RecordMark :
RecordLength 16
LoadOffset 00C0
RecordType 00
Data DEBFCDBF21E0A0E0B1E001C01D92A930
Checksum AC
RecordMark :
RecordLength 16
LoadOffset 00D0
RecordType 00
Data B207E1F70E94F1010C9401020C940000
Checksum B8
RecordMark :
RecordLength 16
LoadOffset 00E0
RecordType 00
Data 61E08DE00C94810161E08DE00E94BA01
Checksum 35
RecordMark :
RecordLength 16
LoadOffset 00F0
RecordType 00
Data 68EE73E080E090E00E94F50060E08DE0
Checksum 43
RecordMark :
RecordLength 16
LoadOffset 0100
RecordType 00
Data 0E94BA0168EE73E080E090E00C94F500
Checksum 84
RecordMark :
RecordLength 16
LoadOffset 0110
RecordType 00
Data 1F920F920FB60F9211242F933F938F93
Checksum 3C
RecordMark :
RecordLength 16
LoadOffset 0120
RecordType 00
Data 9F93AF93BF938091010190910201A091
Checksum A1
RecordMark :
RecordLength 16
LoadOffset 0130
RecordType 00
Data 0301B09104013091000123E0230F2D37
Checksum 1A
RecordMark :
RecordLength 16
LoadOffset 0140
RecordType 00
Data 20F40196A11DB11D05C026E8230F0296
Checksum DB
RecordMark :
RecordLength 16
LoadOffset 0150
RecordType 00
Data A11DB11D209300018093010190930201
Checksum 24
RecordMark :
RecordLength 16
LoadOffset 0160
RecordType 00
Data A0930301B09304018091050190910601
Checksum D1
RecordMark :
RecordLength 16
LoadOffset 0170
RecordType 00
Data A0910701B09108010196A11DB11D8093
Checksum C6
RecordMark :
RecordLength 16
LoadOffset 0180
RecordType 00
Data 050190930601A0930701B0930801BF91
Checksum 68
RecordMark :
RecordLength 16
LoadOffset 0190
RecordType 00
Data AF919F918F913F912F910F900FBE0F90
Checksum 34
RecordMark :
RecordLength 16
LoadOffset 01A0
RecordType 00
Data 1F9018953FB7F8948091050190910601
Checksum 32
RecordMark :
RecordLength 16
LoadOffset 01B0
RecordType 00
Data A0910701B091080126B5A89B05C02F3F
Checksum 6B
RecordMark :
RecordLength 16
LoadOffset 01C0
RecordType 00
Data 19F00196A11DB11D3FBF6627782F892F
Checksum 19
RecordMark :
RecordLength 16
LoadOffset 01D0
RecordType 00
Data 9A2F620F711D811D911D42E0660F771F
Checksum DE
RecordMark :
RecordLength 16
LoadOffset 01E0
RecordType 00
Data 881F991F4A95D1F70895CF92DF92EF92
Checksum 19
RecordMark :
RecordLength 16
LoadOffset 01F0
RecordType 00
Data FF92CF93DF936B017C010E94D200EB01
Checksum 51
RecordMark :
RecordLength 16
LoadOffset 0200
RecordType 00
Data C114D104E104F10489F00E9400020E94
Checksum AB
RecordMark :
RecordLength 16
LoadOffset 0210
RecordType 00
Data D2006C1B7D0B683E734090F381E0C81A
Checksum DE
RecordMark :
RecordLength 16
LoadOffset 0220
RecordType 00
Data D108E108F108C851DC4FEACFDF91CF91
Checksum 46
RecordMark :
RecordLength 16
LoadOffset 0230
RecordType 00
Data FF90EF90DF90CF900895789484B58260
Checksum 1E
RecordMark :
RecordLength 16
LoadOffset 0240
RecordType 00
Data 84BD84B5816084BD85B5826085BD85B5
Checksum 7A
RecordMark :
RecordLength 16
LoadOffset 0250
RecordType 00
Data 816085BDEEE6F0E0808181608083E1E8
Checksum 29
RecordMark :
RecordLength 16
LoadOffset 0260
RecordType 00
Data F0E01082808182608083808181608083
Checksum 61
RecordMark :
RecordLength 16
LoadOffset 0270
RecordType 00
Data E0E8F0E0808181608083E1EBF0E08081
Checksum 64
RecordMark :
RecordLength 16
LoadOffset 0280
RecordType 00
Data 84608083E0EBF0E0808181608083EAE7
Checksum 36
RecordMark :
RecordLength 16
LoadOffset 0290
RecordType 00
Data F0E08081846080838081826080838081
Checksum BF
RecordMark :
RecordLength 16
LoadOffset 02A0
RecordType 00
Data 816080838081806880831092C1000895
Checksum 7E
RecordMark :
RecordLength 16
LoadOffset 02B0
RecordType 00
Data 833081F028F4813099F08230A1F00895
Checksum E4
RecordMark :
RecordLength 16
LoadOffset 02C0
RecordType 00
Data 8730A9F08830B9F08430D1F480918000
Checksum 73
RecordMark :
RecordLength 16
LoadOffset 02D0
RecordType 00
Data 8F7D03C0809180008F77809380000895
Checksum 88
RecordMark :
RecordLength 16
LoadOffset 02E0
RecordType 00
Data 84B58F7702C084B58F7D84BD08958091
Checksum D9
RecordMark :
RecordLength 16
LoadOffset 02F0
RecordType 00
Data B0008F7703C08091B0008F7D8093B000
Checksum F5
RecordMark :
RecordLength 16
LoadOffset 0300
RecordType 00
Data 0895CF93DF9390E0FC01E458FF4F2491
Checksum D0
RecordMark :
RecordLength 16
LoadOffset 0310
RecordType 00
Data FC01E057FF4F8491882349F190E0880F
Checksum 5A
RecordMark :
RecordLength 16
LoadOffset 0320
RecordType 00
Data 991FFC01E255FF4FA591B4918C559F4F
Checksum 49
RecordMark :
RecordLength 16
LoadOffset 0330
RecordType 00
Data FC01C591D4919FB7611108C0F8948C91
Checksum CC
RecordMark :
RecordLength 16
LoadOffset 0340
RecordType 00
Data 209582238C93888182230AC0623051F4
Checksum E5
RecordMark :
RecordLength 16
LoadOffset 0350
RecordType 00
Data F8948C91322F309583238C938881822B
Checksum 53
RecordMark :
RecordLength 16
LoadOffset 0360
RecordType 00
Data 888304C0F8948C91822B8C939FBFDF91
Checksum 7B
RecordMark :
RecordLength 16
LoadOffset 0370
RecordType 00
Data CF9108950F931F93CF93DF931F92CDB7
Checksum 23
RecordMark :
RecordLength 16
LoadOffset 0380
RecordType 00
Data DEB7282F30E0F901E859FF4F8491F901
Checksum D9
RecordMark :
RecordLength 16
LoadOffset 0390
RecordType 00
Data E458FF4F1491F901E057FF4F04910023
Checksum F7
RecordMark :
RecordLength 16
LoadOffset 03A0
RecordType 00
Data C9F0882321F069830E9458016981E02F
Checksum F8
RecordMark :
RecordLength 16
LoadOffset 03B0
RecordType 00
Data F0E0EE0FFF1FEC55FF4FA591B4919FB7
Checksum F2
RecordMark :
RecordLength 16
LoadOffset 03C0
RecordType 00
Data F8948C91611103C01095812301C0812B
Checksum 99
RecordMark :
RecordLength 16
LoadOffset 03D0
RecordType 00
Data 8C939FBF0F90DF91CF911F910F910895
Checksum 44
RecordMark :
RecordLength 16
LoadOffset 03E0
RecordType 00
Data 08950E941D010E94F0010E947000C0E0
Checksum 6B
RecordMark :
RecordLength 16
LoadOffset 03F0
RecordType 00
Data D0E00E9474002097E1F30E940000F9CF
Checksum 42
RecordMark :
RecordLength 6
LoadOffset 0400
RecordType 00
Data 0895F894FFCF
Checksum FF
RecordMark :
RecordLength 0
LoadOffset 0000
RecordType 01
Data 
Checksum FF

參考文献
[1]Wikipedia.Intel HEX[DB/OL].http://en.wikipedia.org/wiki/Intel_HEX,2016,4