含有单片机的电子产品在量产的时候会用到.hex文件或者.bin。hex是十六进制的,包含地址信息和数据信息,而bin文件是二进制的,只有数据而不包含地址。任何文件都有一定的格式规范,hex文件同样具有完整的格式规范。今天和大家分享一下,hex是如何解析的。 $ }; ~3 I P4 ~, s$ L. G+ G
. J! x E) ` k1 m2 @2 I
一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得来。7 K: [* _! N3 r7 q" q d6 n$ N9 t
' K% _/ j; f4 I( k
/ i3 V1 h: w0 F8 i* U6 {二扩展地址细心的同学可能发现了,上面的地址都是两个字节,范围从0x000-0xFFFF,如果地址是0x17FFFF该怎么办呢?这就要用到扩展字段了,举例如下:
- ]" V8 d' d* W: s( c 第一行中,第一个字节为0x02,表示只有两个字节的数据,而扩展段的标识符为0x04表示后面的数据0x0800为扩展线性地址,基地址的计算方法为: (0x0800<<16)=0x08000000,在0x04标识段出现之前,下面的数据都是这个基地址。 第二行的地址是0x0000,那么实际地址应是0x08000000+0x0000=0x08000000; 第二行的地址是0x0010,那么实际地址应是0x08000000+0x0010=0x08000010; 使用Notepad++工具,可以根据颜色的不同来确认校验数据是否正确,如果校验数据的颜色不是绿色,则表示校验结果是错的。 # g7 R$ ~6 E: V. b% E! _+ B) t
% o: S; G4 y0 B! N/ |三程序如何实现hex解析经常会用到上位机软件来实现单片机的烧录,那上位机就要解析hex文件,程序如何实现hex文件的解析呢? 1 }( c+ W' Z7 e( `- x9 m( f/ ]
头文件代码如下所示:
* k" W1 O0 `% [- #ifndef _HEXLEXER_H_
6 M( o2 g4 n) a7 Q; i2 ~( c - #define _HEXLEXER_H_" k; t& q2 N( l( i. ?
- #include <cstdio>' O/ j) V. `4 f/ ~6 W
- #include <cstring>3 c# ?% u4 p9 y5 l5 |1 o
- #include <cstdlib>
( S3 Y7 l) q4 t- k6 c% j - /*
3 X" x0 B! u! G( U - Intel Hex文件解析器V1.0, i+ G4 ]) S( ]4 a
- Hex文件的格式如下:
: V, j' j3 C" L/ n( I - RecordMark RecordLength LoadOffset RecordType Data Checksum6 M' T- A W: ?" Z
- 在Intel Hex文件中,RecordMark规定为“:”
- P' J( Y9 N0 ?+ _# d/ ^" u - */: z& n3 }0 l: Q' P4 o+ }
- #pragma warning(disable:4996)
6 C2 u* P+ w0 x. z" @ ]1 M3 O - #define MAX_BUFFER_SIZE 43% u6 ^( x% @$ C
- class Hex/ w6 {7 o7 @0 {
- {
% n* L; o: F2 R% F( C - public:
# m* `" o1 s6 L1 k7 Q - Hex(char mark);
9 ?1 b/ y- q7 h - ~Hex();. t% h6 M3 M5 d& p* x
- void ParseHex(char *data);//解析Hex文件) t" [7 c* {+ h6 U, I: O# }0 c4 A
- void ParseRecord(char ch);//解析每一条记录
1 `+ a# ?* N6 {4 c# M4 i; ]) F - size_t GetRecordLength();//获取记录长度
& v& `4 M/ T, w! X1 z( b' h - char GetRecordMark();//获取记录标识
( s' `8 e, {" M( z; s6 l1 F- g, s3 M - char *GetLoadOffset();//获取内存装载偏移) C2 O5 T- N' {8 U$ Q3 Q9 z6 T
- char *GetRecordType();//获取记录类型; z3 X0 G& C9 X8 h% _3 u* m
- char *GetData();//获取数据
[9 _2 o+ ?1 \3 } - char *GetChecksum();//获取校验和
9 i( g0 N: Y" B1 B& g - 9 Y7 O5 O! l3 R: Z. R* s# s
- private:
) W* {4 S# _! e' P9 ^5 N I3 U - char m_cBuffer[MAX_BUFFER_SIZE];//存储待解析的记录1 S" _# O8 H9 _7 {4 b0 ^( Q/ P
- char m_cRecordMark;//记录标识! _' t( h& `" G6 J' T4 }
- size_t m_nRecordLength;//记录长度9 |+ o" ?5 o6 n3 \& `9 v( K- b
- char *m_pLoadOffset;//装载偏移1 i2 O0 K% g, A g- Y. ~
- char *m_pRecordType;//记录类型, ^7 k6 K d. e2 \7 @
- char *m_pData;//数据字段$ O, R+ x$ H. W! O6 b
- char *m_pChecksum;//校验和
; X9 i4 I c1 w$ k9 o - bool m_bRecvStatus;//接收状态标识. z( F( s; t- O7 ?+ j7 f- x1 N3 \, Q7 Z
- //size_t m_nIndex;//缓存的字符索引值! q3 ^" U: P/ h b1 Z. Y7 o1 S
- };: Q& O* E9 C4 x, a& b! s$ M7 ^
- 7 y6 ^8 j, _: }8 j- I
- Hex::Hex(char mark)1 c8 R6 D1 f3 }2 O) u/ H B
- {; G1 ~: }6 ^" u
- this->m_cRecordMark = mark;
) R& L$ D& A! z# l5 C1 P' Z7 O - m_cBuffer[0] = '\0';6 c4 G: C/ Z3 f9 i( x* c
- //m_pBuffer = NULL;
3 O" t/ \8 [0 P+ \) x- ^ - m_nRecordLength = 0;+ H; b2 m8 ^. W. f: j5 Y' h
- m_pLoadOffset = NULL;1 {# h) u' h. y5 m, V) }: q
- m_pRecordType = NULL;% s/ H. |- l9 r" r9 j9 {- F
- m_pData = NULL;
, E; d7 U6 A4 ?5 i - m_pChecksum = NULL;" T, V/ a+ p5 w2 `
- m_bRecvStatus = false;) h+ \. q' R2 m. Y1 U2 f
- //m_nIndex = 0;
! L1 V! s. R+ k# m! ?5 g% n9 T" y - }: N' o5 e7 Q$ S; r* J( V
' m0 o8 K$ b5 b6 ? X" f/ S- Hex::~Hex()
' K: d' @5 l* o1 K. H" D8 t - {7 a" C. X. n- k T* h
- delete m_pLoadOffset, m_pRecordType, m_pData, m_pChecksum;
0 A* O3 x5 s! z' _ - }
7 y9 [: K" Y! f0 ^ - #endif
复制代码
! P+ n* D" K9 s W; Z# A# |* T 代码如下所示。
3 k- |0 s) Y8 O$ N, B, {$ a- M
; B$ G$ Z5 c% y( b: V8 C% a" |- #include "HexLexer.h"* ?# \7 k, X' p, X
- #include <iostream>
. ]7 S; u9 \% O) R3 z5 C0 ?, N - using namespace std;. }9 l5 K: o; X2 s& M* h% z
- //获取记录标识
P+ ?: n4 m; x - char Hex::GetRecordMark() l; p) [8 M. r) c1 t% @% ^
- {
4 o" h% P5 ]6 G, j+ r- ]3 y. f - return this->m_cRecordMark;6 w" t0 h3 U+ Z4 Y) w
- }# p2 P4 N; Z' J- A( K3 G
- //获取每条记录的长度
# @# {/ K2 ?- q9 A/ A0 q* t - size_t Hex::GetRecordLength()
; u3 D }1 L4 ?& V - {% z5 h% p- L+ n
- //char *len = (char*)malloc(sizeof(char)* 3);' G$ t6 Y9 e& i, K
- if (strlen(m_cBuffer)>=2), Q. ?- `+ d1 k5 W; m5 U
- {
& @9 z, B% [5 s4 U1 `; c - char len[3];
! o( {" K& A# ? O - len[0] = m_cBuffer[0];
: e; P' P- k' g+ D! N/ b1 N - len[1] = m_cBuffer[1];
* s7 w' ]: F! P) y& e9 | - len[2] = '\0';& }+ O6 c2 R) [" v* o
- char *p = NULL;
8 |4 H* p- y6 ~7 E! \" P - return strtol(len, &p, 16);
) {; O: ]5 I, C* [9 |! r - }
$ v* a. v, s; i( [! s - else( \: K( Y% ]! Q' j" m, v: @
- {' q* k( E! t' O7 J- ?
- return 0;& [! c" @' u1 l& r6 H
- }
a, t9 f. K: L - }
% D( C0 e) W' t K" I3 R Z) D+ \ - //获取装载偏移
8 i0 M" O, n2 j9 j. _# ~* w7 s - char* Hex::GetLoadOffset(). _, j, D9 c. [, n
- {
5 x$ t. ~( J9 J) { - if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
) i# U9 s# @( N7 Z/ c1 P0 o - {) H4 f3 Q: u- X- v" Z% P
- char *offset = (char*)malloc(sizeof(char)* 5);
8 ]& U( A. V6 B ^: y2 m) u% r& o - for (int i = 0; i < 4; ++i)
, Y& z. n G e1 H) ^) _5 p" Y - {4 I. f! y- ?) r" X+ u8 w
- offset[i] = m_cBuffer[i + 2];
* F- N7 c. Y' v) C - }+ U- u: f6 u+ a! G% l) t# y i" E
- offset[4] = '\0';; Y) R9 p W; |$ ~- H' U9 M
- m_pLoadOffset = offset;' E) o" L: v& F7 t0 M1 Y6 W# j
- offset = NULL;( d+ L3 n: l9 [& k2 L& _( y/ Z
- }
. T$ }' P+ \/ L" B5 V8 N - return m_pLoadOffset;
7 { G8 J# C& o W) c- ~" t - }
( A9 I$ A* y& n9 n/ G - //获取记录类型
, L! a5 i$ M+ I- m$ e4 [ - char* Hex::GetRecordType()
4 B5 E( W$ N' [( |4 h - {
" Z- @* }3 l0 q3 S, { - if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
! _. o+ p! K) R- h" _ - {
8 Q0 X! h, F3 F) M0 o: n1 B( a - char *type=(char*)malloc(sizeof(char)*3); t! ~1 p" G5 d6 P' M
- type[0] = m_cBuffer[6];: S: X9 t9 ^& L! q& z' \# b
- type[1] = m_cBuffer[7];$ I8 T% W" r/ O0 I6 ^0 ~/ G3 d
- type[2] = '\0';- G. ]2 r1 t- k* X3 o
- m_pRecordType = type;+ H0 T$ W5 i* R6 s( e" x
- type = NULL;
1 T+ Y) I3 V, @7 R - }
' G+ u4 v% o8 A. x, p+ w - return m_pRecordType;
+ d$ a" b3 D( X - }
* r' _8 G3 v( L& b% S* E0 w - //获取数据
( F+ a9 n( _6 r2 T: s" e) D - char* Hex::GetData()
# @9 N7 A! T5 v+ `2 G( N4 h6 i, [ - {; n4 i2 r* q# f' k2 |' C3 \: {% a
- if (strlen(m_cBuffer) == (GetRecordLength() + 5) * 2)
% l- ?9 @# z3 K5 ?5 H9 l6 R6 g( s - {
. O; k$ A/ p5 w, R - int len = GetRecordLength();
3 q2 ]7 l- \. ]1 g/ C" ?+ H8 ? - char *data = (char*)malloc(sizeof(char)*(len * 2 + 1));' @3 I+ P# m/ Q M3 N; C2 n
- for (int i = 0; i < len * 2;++i)
+ k- N8 _7 C' O; s2 D5 ?9 a8 Q1 [ - {
, r" O' E1 V2 e* f( g - data[i] = m_cBuffer[i + 8];: f0 d3 E( C2 y; Q& ?
- }
( i. H2 P( G; d - data[len * 2] = '\0';
5 f3 F2 d% d# n. I1 J - m_pData = data;
) q0 J5 H+ X# L. r- f6 M/ @7 I - data = NULL;
2 I _" F$ p- T - }9 F$ ]" B/ h! }
- return m_pData;& d# g& Y* [7 Y; z
- }
" o' I. Y7 o1 h5 Q1 V. I$ ]7 l, [6 Z - //获取校验和3 P2 a9 b3 f# l; {' R. v
- char* Hex::GetChecksum()! e: `9 P3 I& D0 e: |/ D7 v2 j4 o% k
- {* {$ ^/ v. g; a: m. ]
- int len = GetRecordLength();( w' E) ~( V, X* e6 k
- if (strlen(m_cBuffer) == (len + 5) * 2)9 f& h$ A) A; D) C7 ~/ o* ]* r
- {+ [$ {' e* j1 M+ y4 I
- char *checksum=(char*)malloc(sizeof(char)*3);
% ?7 E2 T( |9 f8 e9 b/ k - checksum[0] = m_cBuffer[(len + 5) * 2 - 2];9 p" @; Q. Q u& V, N- G U
- checksum[1] = m_cBuffer[(len + 5) * 2-1];* K; x# |. H' @% u
- checksum[2] = '\0';) m- e/ M4 e; W/ x( X
- m_pChecksum = checksum;1 E+ M7 c( r1 {" s) G' _5 A
- checksum=NULL;
3 @4 c& r" _# K - }
7 \6 W+ R$ [. I3 d. T - return m_pChecksum;. l4 ^ {+ t0 X' S- \2 d, M
- }
: i; u X$ j% N$ g L2 D - //解析Hex文件中的每一条记录" y( L9 ?5 k2 U; }9 |
- void Hex::ParseRecord(char ch)
2 ^9 c( j4 _* U0 u - {
/ q# p# K* \/ r - size_t buf_len = strlen(m_cBuffer);
4 H* V) l# v/ r& f4 H - if (GetRecordMark()==ch)/ F8 |8 D; ]% P
- {6 Q9 |; i8 I+ R2 K: W A/ R
- m_bRecvStatus = true;5 R2 `& H* ?) G- L2 U% m
- m_cBuffer[0] = '\0';9 B" W$ l; J" c' T
- //m_nIndex = 0;- O1 D8 A- F: Q7 J+ c3 d0 S) L7 o
- return;* l. @7 M5 W( Y ~
- }
8 j/ [* G5 O' V- q - if ((buf_len==(GetRecordLength()+5)*2-1))' Y: D$ p: ?# i/ X+ Y" A
- {
) H- T! M, Y. w1 { - //接收最后一个字符
6 t. m: f- B: z& P! ? - m_cBuffer[buf_len] = ch;: S4 q8 \0 c, _/ C N8 a8 b% s
- m_cBuffer[buf_len + 1] = '\0';7 F# g) F6 ~' v3 L. p: s
- //检验接收的数据0 r1 L1 W( s! S; E2 c; O
- char temp[3];1 h- P& M. w/ V5 z5 J. ?
- char *p = NULL;
4 W/ e+ ]# P5 h6 i* I) _* }) E% T - long int checksum = 0;
- @0 C* ?0 ?/ r$ k - for (int i = 0; i < strlen(m_cBuffer);i+=2)
$ X" `9 B$ C% n1 g - {
) ^7 [3 D. ~( }# N. R, ] - temp[0] = m_cBuffer[i];
1 k8 `+ a8 H R; A- \ - temp[1] = m_cBuffer[i + 1];
) c+ I9 a! l8 V0 j( U: P - temp[2] = '\0';
?8 r8 Z; l) G4 a9 v. S# f - checksum += strtol(temp, &p, 16);; q. g) x6 D7 m1 F; F
- temp[0] = '\0';
2 _" k1 J8 k, s. L! P; u* }# _: d2 k - }, `! e9 k4 v+ P% O; W
- checksum &= 0x00ff;//取计算结果的低8位
7 t6 T+ n/ O0 q( c* K: R' s - if (checksum==0)//checksum为0说明接收的数据无误) j$ c% r, @: z; s8 ^/ l: D h
- {3 R" T( R9 A2 M+ O% Z' v( ~6 k* k$ w
- cout << "RecordMark " << GetRecordMark() << endl;
8 k/ G) b, p3 }7 d - cout << "RecordLength " << GetRecordLength() << endl;
5 ~8 w) W6 I9 c; O9 U! k' a - cout << "LoadOffset " << GetLoadOffset() << endl;
j: o, v) d/ y i - cout << "RecordType " << GetRecordType() << endl;
! R# H' T- Q' L7 v - cout << "Data " << GetData() << endl;
( k: N5 q" D6 H0 ` - cout << "Checksum " << GetChecksum() << endl;
! r$ Z6 w9 h O' H' |/ Q. x) q" b - }
( v8 L1 f# n( F7 A% n - else//否则接收数据有误
: [; C8 A0 p$ s - {. y1 p$ u9 R2 f. ]! D
- cout << "Error!" << endl;. C# g, s' C$ b4 W! y* Y
- }
7 o( _8 r- l' g. h5 M, i - m_cBuffer[0] = '\0';9 b. [/ }1 ~6 n
- m_bRecvStatus = false;! v" {+ f' X+ c9 G3 t* y6 d6 P( N
- m_nRecordLength = 0;6 Y5 y/ {6 X) g0 Y
- m_pLoadOffset = NULL;
% T( k& m1 t& X3 g' L3 N - m_pRecordType = NULL;# v! Y* ^1 F A
- m_pChecksum = NULL;8 G; I! l0 O' n( q( S# m8 u% s
- m_bRecvStatus = false;
4 M0 @& Z0 x$ W - }$ b1 g | i) f" ^
- else if (m_bRecvStatus)
+ t0 c! _% h# h; s) m - {
8 |. M$ j6 |. S0 {7 u0 F - m_cBuffer[buf_len] = ch;
Q h+ q( l% H" t* h6 u - m_cBuffer[buf_len + 1] = '\0';2 y4 j: w# y# y* o# J( `2 _
- //m_nIndex++;5 T; ~# Z1 x7 [& d( g9 |& q' N
- }
3 A3 s6 V- R4 |! s7 F$ `. ~' _1 `3 E - }
) R7 m; h3 p) u. C - //解析Hex文件& V2 |' H5 \' y! l- E: F0 G
- void Hex::ParseHex(char *data)
: d) _( o# `) U& n" L - {
/ o* [) {* V) J% s( B) l+ m - for (int i = 0; i < strlen(data);++i)4 w5 H: Z+ o( J, T/ `8 A
- {. b- k3 {& J6 l4 K
- ParseRecord(data[i]);' o# S% B/ K# \# n6 C; w
- }
2 V7 U7 b8 ] g. Z - }5 U0 A* J4 `8 V0 K# C- w8 t
- int main(int argc, char *argv[])
5 t h0 j4 w! m3 J/ [3 @ - {
) M! i+ A, C- h' T - freopen("in.txt", "r", stdin);
0 O1 V$ \% i1 a( n& r - freopen("out.txt", "w", stdout);
5 e9 k; y" @7 ]8 T" Q - ! U$ p- }. l- A! e n& Q( t/ _
- Hex hex(':');
! ]& [7 r; d3 W5 i$ z! E' M - char ch;
0 T$ K8 g9 |" J- k/ d T - while (cin>>ch)
/ X4 m# ]; ~5 C - {
$ [( ~& k5 m# s ~1 m. p - hex.ParseRecord(ch);
5 A2 i8 v4 a2 k. I) Z! ~; C - }
+ w/ ^# x1 C4 \5 [ - fclose(stdout);
* j! s9 D; k/ [% d9 p- ~8 N2 m8 H - fclose(stdin);
; Y& ?7 ^- Z; Q6 L+ r: p! ] - return 0;
- F( `# q& H! O7 t" j- Y - }
复制代码 _1 C* T$ a* o
是不是这样呢?赶紧打开.hex文件来看一下吧。
7 w7 w# x6 b# ?/ X+ g: w |