含有单片机的电子产品在量产的时候会用到.hex文件或者.bin。hex是十六进制的,包含地址信息和数据信息,而bin文件是二进制的,只有数据而不包含地址。任何文件都有一定的格式规范,hex文件同样具有完整的格式规范。今天和大家分享一下,hex是如何解析的。
1 c& d3 i8 q- Q# C ( j2 o/ C# \; ^0 q, M7 ^ p" m
一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得来。
+ x& ^) T7 F& M0 o2 F2 o $ t3 Q( q: I4 V1 @/ W
2 B, ^4 T1 E4 ]二扩展地址细心的同学可能发现了,上面的地址都是两个字节,范围从0x000-0xFFFF,如果地址是0x17FFFF该怎么办呢?这就要用到扩展字段了,举例如下:
$ ?8 r6 v& D- \8 b 第一行中,第一个字节为0x02,表示只有两个字节的数据,而扩展段的标识符为0x04表示后面的数据0x0800为扩展线性地址,基地址的计算方法为: (0x0800<<16)=0x08000000,在0x04标识段出现之前,下面的数据都是这个基地址。 第二行的地址是0x0000,那么实际地址应是0x08000000+0x0000=0x08000000; 第二行的地址是0x0010,那么实际地址应是0x08000000+0x0010=0x08000010; 使用Notepad++工具,可以根据颜色的不同来确认校验数据是否正确,如果校验数据的颜色不是绿色,则表示校验结果是错的。 : }) o3 f% W# v
. U) T1 ]! u1 v$ D$ h5 P三程序如何实现hex解析经常会用到上位机软件来实现单片机的烧录,那上位机就要解析hex文件,程序如何实现hex文件的解析呢? % V2 C; \: N+ @9 b
头文件代码如下所示:
" E/ @) z! j' ^; K" \- k8 ]- #ifndef _HEXLEXER_H_5 z8 r! ]. t# t0 S! Q% s+ U; V
- #define _HEXLEXER_H_
$ k$ { |8 t5 J, p - #include <cstdio>3 ~& S$ j$ z n7 ~+ L! I6 Z* H$ M
- #include <cstring> r; }% c# _# R: B$ c5 y1 J, ~; e' k
- #include <cstdlib>
+ L4 {; N* t$ _. r- | - /*- F% y! i. C6 G4 i6 O1 @
- Intel Hex文件解析器V1.04 R8 L$ d; c9 O, I! N
- Hex文件的格式如下:& R0 e* y& B/ s( G% D/ F0 {9 X
- RecordMark RecordLength LoadOffset RecordType Data Checksum$ V/ o/ L+ E/ @+ g6 S: @
- 在Intel Hex文件中,RecordMark规定为“:”: P+ w: l8 L9 G9 C5 g& Q& _$ I
- */% x! z( s5 \; R" }, K0 n( u. T
- #pragma warning(disable:4996)
% `) x Y' z. R! |' D3 @5 o9 b - #define MAX_BUFFER_SIZE 43
: q7 K( Y% z$ b( U" x% G$ U) g3 _ - class Hex
, f4 i. l! B* Q - { S% R a6 G& D! I; K9 c# k
- public:6 c+ P H( H7 I0 t
- Hex(char mark);
1 S2 H% J! o( J6 c5 c, R0 z" }9 P - ~Hex();
' K# S$ }% w) G* R$ s) A! }' N( B# W - void ParseHex(char *data);//解析Hex文件" c B( \8 \/ D, C1 g2 K# `$ L' M
- void ParseRecord(char ch);//解析每一条记录 H2 W, q3 e: x2 ?: H
- size_t GetRecordLength();//获取记录长度
; u4 x. H; n2 k' L) f - char GetRecordMark();//获取记录标识 }7 p8 A* e7 B6 T' |
- char *GetLoadOffset();//获取内存装载偏移1 T1 | ], O) A2 O: ?
- char *GetRecordType();//获取记录类型
4 _. @% R' P( [3 m, D - char *GetData();//获取数据0 X0 U( ]6 C8 s8 ^4 s
- char *GetChecksum();//获取校验和1 L; x( N; j% [! f9 }
- - `6 D* W: h. m' `' q, n1 {' c& F/ u
- private:/ \8 [, y) x# o: q
- char m_cBuffer[MAX_BUFFER_SIZE];//存储待解析的记录+ q( l8 t' \, ?9 _& N2 u& o
- char m_cRecordMark;//记录标识5 E' j; z4 ]$ \% c7 i
- size_t m_nRecordLength;//记录长度
7 R' @3 m/ h. Z - char *m_pLoadOffset;//装载偏移
9 A2 @; h7 p" e& I- f6 E, W2 d - char *m_pRecordType;//记录类型" b2 }% |! _# ~: n2 ~
- char *m_pData;//数据字段
0 v& ]2 { V" ]8 }, w. E6 ~ - char *m_pChecksum;//校验和
$ k9 ^1 ~ ^$ u - bool m_bRecvStatus;//接收状态标识
+ y- G! K7 G( } - //size_t m_nIndex;//缓存的字符索引值+ O2 V ?, a) R1 V8 A+ @
- };0 F& h6 a1 O" L5 R6 U
( G7 M" n6 r$ G# ^1 }- I- Hex::Hex(char mark)
! h$ u8 o& O- c" V/ B1 ~( a - {
/ [2 H0 B7 ? M - this->m_cRecordMark = mark;: I7 `! _0 G9 O% l, p2 @- d
- m_cBuffer[0] = '\0';
2 s# ?% t4 J: e - //m_pBuffer = NULL;/ m2 W. }5 c0 x* k a
- m_nRecordLength = 0;
" A, B+ F3 ~8 Z9 ^1 t - m_pLoadOffset = NULL;/ I! n2 G7 C4 M4 n7 x1 i
- m_pRecordType = NULL;9 D* T" `9 A# l% {. I
- m_pData = NULL;0 O3 [+ w7 W5 K5 f# C
- m_pChecksum = NULL;+ p7 O4 s9 k0 y/ L" O
- m_bRecvStatus = false;4 w$ A# H: T. W2 \' ]+ d; G( r
- //m_nIndex = 0;/ B+ }0 z6 {+ B
- }
8 [4 K( d! X/ q- L( N6 u4 M y) | - ; e: H( d& U$ q& ^3 r9 K
- Hex::~Hex()
! X9 |/ d' N b - {
8 D5 L' C4 v+ K, l) P - delete m_pLoadOffset, m_pRecordType, m_pData, m_pChecksum;
' \' i& I) b8 t7 z4 L - }6 U- _& Q9 s) H6 Q$ ^
- #endif
复制代码 5 H7 ?! L/ r7 E4 E& L+ A
代码如下所示。
; ?/ a9 T* I0 X* `- @4 C" o9 J9 l( r
; {' L* S& F, G" }( L# Y% k0 K6 X- #include "HexLexer.h"3 a$ j6 Y) W: _: c. ~ Y3 i6 P
- #include <iostream>
% f" o9 ?4 W* y0 Q' a2 P - using namespace std;
( c7 Z, r. n7 _. ~& _" P4 s/ a- @ - //获取记录标识; U# w" O+ \/ O+ w1 ~' f0 X
- char Hex::GetRecordMark()
3 Y' f1 z9 M$ U+ `: A$ T - {
: C/ C- o9 H. W9 B: L; Q& {( u - return this->m_cRecordMark;; A) f2 _( V: f. {9 Z
- }
6 C- i$ l" k) i/ P$ I0 D; I - //获取每条记录的长度8 @ z( h/ ?. w/ v5 C# |$ z5 z& d4 I6 Z# y
- size_t Hex::GetRecordLength()
' \$ `8 R& Q, E6 d1 a. a7 q' G, q - {' s f+ _. F) p g& D# O E G
- //char *len = (char*)malloc(sizeof(char)* 3);
6 \1 E9 S0 U& W4 T. E - if (strlen(m_cBuffer)>=2)$ @( P# s- n% w5 d2 P$ j5 H) Y
- {6 z# L6 r' t$ [- w
- char len[3];0 Z3 ~0 P7 g( l9 z4 c* V! t) B6 d2 `$ \
- len[0] = m_cBuffer[0];& w; H5 x0 C4 K
- len[1] = m_cBuffer[1];* P) [" o5 B g* G' Z% i; Q/ C
- len[2] = '\0';9 u! O4 H. k7 k% f0 @2 K& V
- char *p = NULL;
) Y8 z- H0 ]& v+ L3 b7 K8 A - return strtol(len, &p, 16);
% _ X/ J0 {* r% z+ C1 S - }# {$ Q: y& \0 l+ Q1 A! G% L$ J4 l
- else4 V% R1 u u: j' f3 I
- {/ R% F: Z6 C/ p/ ^% x7 Q" v
- return 0;
, a' o6 o x& \ - }4 g. ]4 A8 Y4 M; |2 q- s" p
- }
, f3 j. P2 A! M* _7 E - //获取装载偏移8 w1 Y# ^ @$ S2 a' P# m" [8 ]
- char* Hex::GetLoadOffset()3 ?! R V2 i3 M P* L U3 v; e
- {
5 ]. }% B; _' Z4 n& U; R' Q" z - if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
{: z/ |+ C* q( d - {- h8 V2 I2 ~* U- Q+ m. s: o7 O
- char *offset = (char*)malloc(sizeof(char)* 5);6 v1 b* s. Q+ k1 l- R
- for (int i = 0; i < 4; ++i)
/ S; T2 e; ~8 P1 k6 } - {* r1 ]( E# e0 Y y2 m9 \& E; c2 l
- offset[i] = m_cBuffer[i + 2];
' M# s6 U8 N( u( U2 V - }
" C0 @' D) [2 q/ d. y2 C/ v - offset[4] = '\0';; l$ P1 Y1 Y6 w2 @. C
- m_pLoadOffset = offset;5 }; T! A" I4 b) @) j- G' F( M: s
- offset = NULL;
" U9 S) u, q) M1 o - }& N8 ^+ g6 {7 s5 M
- return m_pLoadOffset;
! n5 m* v/ O% ~ - }
+ v9 J/ {: b% P - //获取记录类型' o( [5 Q" K5 D3 T" t& \) r! p
- char* Hex::GetRecordType()8 T( Z% I, \! p# j
- {' u0 Z3 G2 }8 _9 ]7 N* e3 [
- if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)7 e9 }8 i. ~+ A/ E1 c
- {
3 q' X5 N% f* v- A - char *type=(char*)malloc(sizeof(char)*3);. u. _7 c# Y9 S5 j' L- }7 ^. F
- type[0] = m_cBuffer[6];
8 E/ q% Z( H: N6 H - type[1] = m_cBuffer[7];0 C! j% p$ E6 B; q# |/ l
- type[2] = '\0';" p+ O' L; \! m/ D9 ?
- m_pRecordType = type; l6 I: |0 s) [
- type = NULL;* L( z L8 [" F% a& R
- }
" `. Z+ S1 E$ E - return m_pRecordType;; ~+ D0 t' f8 s8 C2 y# y
- }
3 D# d5 V: a; X# ^ - //获取数据 p: E7 F, O0 q8 n; o
- char* Hex::GetData()6 b' b. l- Q, H
- {
" x2 P. ~5 o' p0 s" M& t0 u - if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)% h( `+ e# y/ x3 M' O* d& M
- {4 r: r2 l* ~2 A- L' w
- int len = GetRecordLength();; N5 g! M* k" v& q* O
- char *data = (char*)malloc(sizeof(char)*(len * 2 + 1));
! H: A, s/ Z3 ^1 g; @ - for (int i = 0; i < len * 2;++i)' L8 u1 c1 [1 ] n
- {5 i7 v3 P4 H- i/ ]/ R
- data[i] = m_cBuffer[i + 8];
7 X) O: L% @7 {/ k - }
/ q) a4 m$ m W/ G - data[len * 2] = '\0';. h" O. k. t7 h" l" ~% L
- m_pData = data;
) n+ `. a' m, Q+ T - data = NULL;
$ ~0 F. p6 W4 G9 ` - }
* z! `( `3 J" [$ i o) [# l4 t- s; M2 D - return m_pData;
7 y& C: z$ @ f% b7 i4 N5 _4 D - }- a4 {1 p2 p! P% {4 T
- //获取校验和
: o) d/ B, h. W! `. h8 L* V - char* Hex::GetChecksum()
# I/ c0 h9 ~' K+ F+ \' i- C% K - {
9 x3 k& t. }7 [. O6 j - int len = GetRecordLength();
/ `% C2 e c t2 b) }1 b7 F - if (strlen(m_cBuffer) == (len + 5) * 2)
0 V8 Y. a8 z F - {2 d4 o9 p8 T, Y5 K8 n3 D
- char *checksum=(char*)malloc(sizeof(char)*3);3 z- m5 c( E; i2 w
- checksum[0] = m_cBuffer[(len + 5) * 2 - 2];9 Q- i: r8 Z& H' p E
- checksum[1] = m_cBuffer[(len + 5) * 2-1];
( d6 z4 I! ^7 N% w. h - checksum[2] = '\0';
& U$ b4 ^4 i8 N - m_pChecksum = checksum;
! K0 x/ w/ T; i! O1 u/ @/ z( T - checksum=NULL;& t# ]6 t- y( x1 ?8 h6 @0 R9 @9 B
- }
( _5 g6 A- ]7 T( F - return m_pChecksum;8 n1 r) n/ L/ @" g0 D1 Y8 r+ r
- }: S% z) j: E. e; l' Q( B
- //解析Hex文件中的每一条记录: S( l6 R8 V" g9 ^5 q9 [6 g
- void Hex::ParseRecord(char ch): \9 ]& V$ ]. K* N
- {& [5 f5 C; {8 z4 s+ g- F5 S
- size_t buf_len = strlen(m_cBuffer);
1 K6 z, m# T( C) o6 B# W, J8 t, ^ - if (GetRecordMark()==ch)
( \8 e8 H/ j7 y2 p# | @3 p4 ~ - {$ A- f: g# S. |5 x
- m_bRecvStatus = true;' ]4 k) k) }* I. {6 ~ T! m9 G
- m_cBuffer[0] = '\0';
! f. }; ], F' l8 I - //m_nIndex = 0;/ z; j4 }, F; n1 L) Q- P
- return;: G" ]: D# v( \3 V& S: L1 p$ f
- }3 b0 d9 b" X! F" k$ n' g
- if ((buf_len==(GetRecordLength()+5)*2-1))0 `+ p, {, j" v" Z
- {/ z5 b: w$ Y* A6 E2 d
- //接收最后一个字符) I5 I1 N" ^, }% h
- m_cBuffer[buf_len] = ch;
0 F# k7 @4 ~* c% H4 h+ | - m_cBuffer[buf_len + 1] = '\0';
1 V. P2 h& C# E) f: e/ d2 u - //检验接收的数据+ {! S; K/ l! U1 m& a* e
- char temp[3];" C* q4 f5 F6 e) o+ E, b4 [7 s+ Z7 R
- char *p = NULL;0 e1 n* C; [2 Z% O& e- @+ n9 b
- long int checksum = 0;
2 k6 Q+ S6 |( z9 S( q r( N' e( ? - for (int i = 0; i < strlen(m_cBuffer);i+=2)
! N j/ |6 @4 h( }$ I' j - {
2 _, b2 r1 G$ t7 Z$ M; L - temp[0] = m_cBuffer[i];/ ?. ]$ D3 ?& o. b( r# L
- temp[1] = m_cBuffer[i + 1];
/ e0 O% V9 ~/ J) t - temp[2] = '\0';
3 j# h! _( G T- w - checksum += strtol(temp, &p, 16);( V1 c T! B& k, d
- temp[0] = '\0';
* h, {5 `5 c6 z* L% F7 u- ^ - }; X# e z2 b3 W; V$ ^
- checksum &= 0x00ff;//取计算结果的低8位
2 h, S- d% ^1 [$ T4 R, x0 Y' y) T! n - if (checksum==0)//checksum为0说明接收的数据无误
& y% _/ S# ^( b4 T$ N1 B - {4 F, H2 O- J" g& {/ H# W( D
- cout << "RecordMark " << GetRecordMark() << endl;
5 B5 v+ Q/ n5 o O - cout << "RecordLength " << GetRecordLength() << endl;6 M" B" U% U1 E& i
- cout << "LoadOffset " << GetLoadOffset() << endl;9 B& P! y$ F" d8 q! ?3 D1 ^
- cout << "RecordType " << GetRecordType() << endl;
+ `6 E7 k" O' Q7 V- f" u0 G - cout << "Data " << GetData() << endl;
" }( O+ \9 T8 k% F1 P2 J - cout << "Checksum " << GetChecksum() << endl;" v1 O9 k5 c. @7 H6 ?% |
- }3 d1 L4 e8 U9 t; ]
- else//否则接收数据有误2 B, R2 }3 z" {: m$ j4 j+ x# h
- {' S$ s7 e; v4 b0 s& V
- cout << "Error!" << endl;7 O) U L. Q6 X, e q7 M7 x4 }1 d7 c
- }
+ H3 N. t2 V/ ?7 H5 Y, W - m_cBuffer[0] = '\0';
; C+ ]& e& L9 \8 j" n4 y/ G - m_bRecvStatus = false;
. u8 S4 L* k x9 A2 B - m_nRecordLength = 0;
& e: m# H$ A# w5 q - m_pLoadOffset = NULL;' g' r! h2 w- K
- m_pRecordType = NULL;
* z) q; @; r N9 u - m_pChecksum = NULL;
9 m$ x0 s# p Q) W - m_bRecvStatus = false;
- |3 _9 t* w H; d7 J3 A& V4 E - }$ b F9 [6 i- m2 Y" C) r
- else if (m_bRecvStatus)1 x, r. y% S+ ?/ g1 M6 Z+ m
- {5 g4 ^* M+ F- s; T% z: a
- m_cBuffer[buf_len] = ch;
5 A) [7 T4 F+ D0 Q, \ - m_cBuffer[buf_len + 1] = '\0';
- c" I" \/ n7 P, J3 n' ~ - //m_nIndex++;5 D( W ^, L: N
- }4 S6 N1 J; J) Z1 R1 H
- }/ X& T' l& v3 b- \; R8 K* O" b9 w! H
- //解析Hex文件0 m. C! J9 T2 N' B1 Q' l6 b% k% x
- void Hex::ParseHex(char *data)
6 Y* R9 H5 ]5 y4 ~8 w u# P' L# R: C - {
7 O) S p) `; t; G) v. n1 @ - for (int i = 0; i < strlen(data);++i)4 P! C4 K4 O9 Q/ _+ o
- {
( F- {% X s( F, ?& \; j# ?% V - ParseRecord(data[i]);
& [, ^! B4 G. f- P8 F4 g* o& S - }! g$ i$ R1 Q% J
- }
1 O: c: ^7 P* `, E - int main(int argc, char *argv[])
& m6 W) A% _9 k: \ - {% f Z# t% J( g( _. [) ?( ?
- freopen("in.txt", "r", stdin);
! G# Z4 g2 ?( M - freopen("out.txt", "w", stdout);
& t, K) I2 G" T0 ^7 t
S$ `8 Q. V% w# R1 {" |2 t5 d- Hex hex(':');
1 A5 P8 N& w+ r$ o - char ch;
. t9 O2 S& o+ {6 J, \ - while (cin>>ch)
# n& f( K' X/ F, m- T& z, W - {4 L/ d* n+ x @) k
- hex.ParseRecord(ch);' Q, M- y4 E: s6 V
- }
6 h$ _$ A* d# n6 Y: l - fclose(stdout);, D, m0 m0 K$ [+ L1 @
- fclose(stdin);' ?% z! N3 _) P
- return 0;
! C* ?# z/ D* W+ n; \4 C - }
复制代码
* y% c& G; b+ X! S是不是这样呢?赶紧打开.hex文件来看一下吧。
2 w5 F% G. P) [& p |