【轉】hex和bin文件格式的區別


hex和bin文件格式的區別

  Intel HEX文件是記錄文本行的ASCII文本文件,在Intel HEX文件中,每一行是一個HEX記錄,由十六進制數組成的機器碼或者數據常量。Intel HEX文件經常被用於將程序或數據傳輸存儲到ROM、EPROM,大多數編程器和模擬器使用Intel HEX文件。

  很多編譯器的支持生成HEX格式的燒錄文件,尤其是Keil c。但是編程器能夠下載的往往是BIN格式,因此HEX轉BIN是每個編程器都必須支持的功能。HEX格式文件以行為單位,每行由“:”(0x3a)開始,以回車鍵結束(0x0d,0x0a)。行內的數據都是由兩個字符表示一個16進制字節,比如”01”就表示數0x01;”0a”,就表示0x0a。對於16位的地址,則高位在前低位在后,比如地址0x010a,在HEX格式文件中就表示為字符串”010a”。

  下面為HEX文件中的一行:

  :10000000FF0462FF051EFF0A93FF0572FF0A93FFBC

   “:”表示一行的開始。 “:”后的第1,2個字符“10”表示本行包含的數據的長度,這里就是0x10即16個。 第3,4,5,6個字符“0000”表示數據存儲的起始地址,這里表示從0x0000地址開始存儲16個數據,其中高位地址在前,低位地址在后。 第7,8個字符“00”表示數據的類型。該類型總共有以下幾種: 00 ----數據記錄 01 ----文件結束記錄 02 ----擴展段地址記錄 04 ----擴展線性地址記錄

  這里就是0x00即為普通數據記錄。自后的32個字符就是本行包含的數據,每兩個字符表示一個字節數據,總共有16個字節數據跟行首的記錄的長度相一致。最后兩個字符表示校驗碼。每個HEX格式的最后一行都是固定為:

:00000001FF

  以上的信息其實就足夠進行HEX轉BIN格式的程序的編寫。首先我們只處理數據類型為0x00及0x01的情況。0x02表示對應的存儲地址超過了64K,由於我的編程器只針對64K以下的單片機,因此在次不處理,0x04也是如此。

 

 

記錄格式

一個Intel HEX文件可以包含任意多的十六進制記錄,每條記錄有五個域,下面是一個記錄的格式:

:llaaaatt[dd...]cc

每一組字母是獨立的一域,每一個字母是一個十六進制數字,每一域至少由兩個十六進制數字組成,下面是字節的描述.

:冒號 是每一條Intel HEX記錄的開始

ll 是這條記錄的長度域,他表示數據(dd)的字節數目。

aaaa 是地址域,他表示數據的起始地址<如果是數據記錄,這表示將要燒錄的這條記錄中的數據在EPROM中的偏移地址,對於不支持擴展段地址和擴展線性地址的,如89C51,這就是此條記錄的起始地址>

tt 這個域表示這條HEX記錄的類型,他有可能是下面這幾種類型 00 ----數據記錄 01 ----文件結束記錄 02 ----擴展段地址記錄 04 ----擴展線性地址記錄

dd 是數據域,表示一個字節的數據,一個記錄可能有多個數據字節,字節數目可以查看ll域的說明

cc 是效驗和域,表示記錄的效驗和,計算方法是將本條記錄冒號開始的所有字母對<不包括本效驗字和冒號> 所表示的十六進制數字<一對字母表示一個十六進制數,這樣的一個十六進制數為一個字節>都加起來然后模除256得到的余數,最后求出余數的補碼,即是本效驗字節cc。

<例如: :0300000002005E9D cc=0x01+NOT((0x03+0x00+0x00+0x00+0x02+0x00+0x5E)%0x100)=0x01+0x9C=0x9D

C語言描述: UCHAR cc; cc=(UCHAR)~(0x03+0x00+0x00+0x00+0x02+0x00+0x5E); cc++; >

 

數據記錄

Intel HEX文件由若干個數據記錄組成,一個數據記錄以一個回車和一個換行結束<回車為0x0d換行為0x0a> 比如下面的一條數據記錄 :10246200464C5549442050524F46494C4500464C33 10  是此行記錄數據的字節數目 2462  是數據在內存<將要燒寫的eprom地址>中的起始地址 00    是記錄類型00(是一個數據記錄) 464C  到 464C 是數據 33      是此行記錄的效驗和

 

擴展線性地址記錄(HEX386) 擴展線性地址記錄也可稱為32位地址記錄/HEX386記錄,這個紀錄包含高16(16-31位)位數據地址,這種擴展的線性記錄總是有兩個字節數據,像下面這樣: :02000004FFFFFC 02 是記錄的數據字節數目 0000 是地址域,這在擴展地址記錄中總是0000 04 是記錄類型04(擴展地址記錄) FFFF 是高16位地址 FC 是記錄效驗和,計算方法如下: 01h + NOT(02h + 00h + 00h + 04h + FFh + FFh) 當一個擴展線性地址記錄被讀到后,擴展線性地址記錄的數據區域將被保存,並應用到后面從Intel HEX文件中讀出的記錄,這個擴展線性記錄一直有效,直到讀到下一個擴展線性記錄。 絕對內存地址 = 數據記錄中的地址 + 移位后的擴展線性地址 下面舉例說明這個過程:從數據記錄的地址域得到地址 2462,從擴展線性地址記錄的地址域得到地址 FFFF,絕對內存地址 FFFF2462

 

擴展段地址記錄 (HEX86)

擴展段地址記錄也被稱為HEX86記錄,包含 4-19位的數據地址段,這個擴展段地址記錄總是有兩字節數據,如下: :020000021200EA 02 是 記錄中的數據字節數目 0000 是地址域,在擴展段地址記錄中,這個域總是0000 02 是記錄類型,02(擴展段地址的標示) 1200 是該段的地址 EA 是效驗和 計算如下: 01h + NOT(02h + 00h + 00h + 02h + 12h + 00h). 當擴展段地址記錄被讀后,擴展段地址將被存儲並應用到以后從Intel HEX文件讀出的記錄,這個段地址一直有效直到讀到下一個擴展段地址記錄 絕對內存地址 = 數據記錄中的地址 + 移位后的擴展段地址 數據記錄中的地址域,移位后擴展段地址記錄中的地址域。 下面舉例說明這個過程:從數據記錄的地址域得到地址 2 4 6 2,從擴展段地址記錄的地址域得到地址 1 2 0 0,絕對內存地址 0 0 0 1 4 4 6 2

 

文件結束記錄(EOF) 一個Intel HEX文件必須有一個文件結束記錄,這個記錄的類型域必須是01, 一個EOF記錄總是這樣: :00000001FF 00是記錄中數據字節的數目 0000這個地址對於EOF記錄來說無任何意義 01記錄類型是01(文件結束記錄標示) FF是效驗和計算如下:01h + NOT(00h + 00h + 00h + 01h).

 

格式:BBAAAATTHHHH...HHHHCC

BB: Byte AAAA:數據記錄的開始地址,高位在前,地位在后。因為這個格式只支持8bits,地址被倍乘。所以,為了得到實際的PIC的地址,需要將地址除以2 TT: Type 00 數據記錄 01 記錄結束 04 擴展地址記錄(表示32位地址的前綴,當然這種只能在 INHX32) HHHH:一個字(Word)的數據記錄,高Byte在前,低Byte在后。TT之后,總共有 BB/2 個字 的數據 CC: 一個Byte的CheckSum

因為PIC16F873A只有4K的程序空間,所以,不會有 TT=04的 Linear Address Record 

  hex和bin文件格式   Hex文件,這里指的是Intel標准的十六進制文件,也就是機器代碼的十六進制形式,並且是用一定文件格式的ASCII碼來表示。具體格式介紹如下: Intel hex 文件常用來保存單片機或其他處理器的目標程序代碼。它保存物理程序存儲區中的目標代碼映象。一般的編程器都支持這種格式。 

Intel hex 文件全部由可打印的ASCII字符組成,如下例所示:

:2000000012014c75a800e4f508f509780a7a78e4f608dafcd283fcfded240af9a7050dbd81 :2000200000010ced2488ec34ff50edc283e4fcfded240af9e76d7013ed33e43c700d0dbd2a :2000400000010ced2488ec34ff50e50509e50970020508e50924a8e50834fd50aee4f50874

Intel hex 由一條或多條記錄組成,每條記錄都由一個冒號“:”打頭,其格式如下:

:CCAAAARR...ZZ

其中: CC    本條記錄中的數據字節數

AAAA  本條記錄中的數據在存儲區中的起始地址

RR    記錄類型:     00 數據記錄 (data record)     01 結束記錄 (end record)     02 段記錄 (paragraph record)     03 轉移地址記錄 (transfer address record)

...     數據域

ZZ    數據域校驗和

校驗值:每一行的最后一個值為此行數據的校驗和。例如:

:1000000018F09FE518F09FE518F09FE518F09FE5C0 這行中的 0xC0

:1000100018F09FE5805F20B9F0FF1FE518F09FE51D 這行中的 0x1D

校驗和的算法為:計算從0x3A 以后(不包括0x3A)的所有各字節的和模256的余。即各字節二進制算術和,不計超過256的溢出值,然后用0x100減去這個算數累加和,得出得值就是此行得校驗和。

  Intel hex文件記錄中的數字都是16進制格式,兩個16進制數字代表一個字節。CC域是數據域中的實際字節數,地址、記錄類型和校驗和域沒有計算在內。校驗和是取記錄中從數據字節計數域(CC)到數據域(...)最后一個字節的所有字節總和的2的補碼。

  而Bin文件是最純粹的二進制機器代碼,沒有格式,或者說是"順序格式"。按assembly code順序翻譯成binary machine code。由於分析出來Hex文件中的數據域ASCII碼表示的十六進制與二進制一一對應,而且我公司DSP又是16位的,以一個word為最小單位,所以四個十六進制ASCII碼代表一條機器指令單位或者地址,借於上面分析,編寫了工具代碼。大體原理是用fscanf函數在每行的數據域讀入四個ASCII碼,以短整形(short int 16bit)形式儲存,在把這個短整形變量順序fwrite到文件流中去即可。

舉一例說明: 表1

復制代碼
ORG 0000H 
LJMP START 
ORG 040H 
START: 
MOV SP, #5FH  ;設堆棧 LOOP: NOP LJMP LOOP    ;循環 END        ;結束  
復制代碼

表2 

:03000000020040BB :0700400075815F000200431F

表3         

02 00 40 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 75 81 5F 00 02 00 43  

  表1為源程序,表2是匯編后得到的HEX文件,表3是由HEX文件轉換成的目標文件,也就是最終寫入EPROM的文件,它由編程器轉換得到,也可以由 HEXBIN一類的程序轉換得到。學過手工匯編者應當不難找出表3與表1的一一對應關系,值得注意的是從02 00 40后開始的一長串‘FF’,直到75 81,這是由於偽指令:ORG 040H造成的結果。

復制代碼
/* 使用方法 : bin2hex -b adress filename -b : 指示hex文件起始地址 address : hex文件的起始地址(FIXME:當前版本只支持k字節邊界) filename: 待轉換的文件名 示例 : bin2hex -b 32k rom.bin */ #include <stdio.h> #include <stdlib.h> #include <string.h> FILE *fp_read; /* 待讀取文件句柄 */ FILE *fp_write; /* 待寫入文件句柄 */ unsigned long start_adr; /* 轉換成Hex格式的起始地址 */ unsigned short cur_base; /* 轉換成Hex格式的當前地址高16位 */ unsigned short cur_offset; /* 轉換成Hex格式的當前地址低16位 */ unsigned char read_buf[16]; unsigned char write_buf[48]; void calc_start_adr (char *buf) { unsigned int len; len = strlen(buf); if ((buf[len-1] != 'k') && (buf[len-1] != 'K')) { printf ("Invalid argument.\n"); exit (-1); } buf[len-1] = 0; start_adr = atoi (buf); start_adr = start_adr * 1024; cur_base = start_adr >> 16; cur_offset = (unsigned short)start_adr; } void start_convert (void) { unsigned char cnt; unsigned char read_num; unsigned char cksum, highc, lowc; /* 設置當前地址高16位 */ highc = cur_base >> 8; lowc = (unsigned char)cur_base; cksum = 2 + 4 + highc + lowc; cksum = 0xFF - cksum; cksum = cksum + 1; sprintf (write_buf, ":02000004%04x%02x", cur_base, cksum); write_buf[15] = 0x0D; write_buf[16] = 0x0A; fwrite (write_buf, 1, 17, fp_write); read_num = fread (read_buf, 1, 16, fp_read); while (read_num == 16) { /* 寫入讀取的16字節 */ highc = cur_offset >> 8; lowc = (unsigned char)cur_offset; cksum = 0x10 + highc + lowc;      for (cnt=0; cnt<16; cnt++) { cksum += read_buf[cnt]; } cksum = 0xFF - cksum; cksum = cksum + 1; sprintf ( write_buf, ":10%02x%02x00%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",       highc, lowc,       read_buf[0], read_buf[1], read_buf[2], read_buf[3],       read_buf[4], read_buf[5], read_buf[6], read_buf[7],       read_buf[8], read_buf[9], read_buf[10], read_buf[11],       read_buf[12], read_buf[13], read_buf[14], read_buf[15],       cksum); write_buf[43] = 0x0D; write_buf[44] = 0x0A; fwrite (write_buf, 1, 45, fp_write); /* 計算當前地址低16位,當越限時寫入當前地址高16位 */ if (cur_offset == 65520) { cur_offset = 0; cur_base ++; highc = cur_base >> 8; lowc = (unsigned char)cur_base; cksum = 2 + 4 + highc + lowc; cksum = 0xFF - cksum; cksum = cksum + 1; sprintf (write_buf, ":02000004%04x%02x", cur_base, cksum); write_buf[15] = 0x0D; write_buf[16] = 0x0A; fwrite (write_buf, 1, 17, fp_write); } else { cur_offset += 16; } read_num = fread (read_buf,1,16,fp_read); } /* 寫入剩余的字節 */ if (read_num) { highc = cur_offset >> 8; lowc = (unsigned char)cur_offset; cksum = read_num + highc + lowc; for (cnt=0; cnt<read_num; cnt++) { cksum += read_buf[cnt]; } cksum = 0xFF - cksum; cksum = cksum + 1; sprintf (write_buf, ":%02x%02x%02x00", read_num, highc, lowc); for (cnt=0; cnt<read_num; cnt++) { sprintf (&write_buf[9 + cnt * 2], "%02x", read_buf[cnt]); } sprintf (&write_buf[9 + cnt * 2], "%02x", cksum); write_buf[11 + read_num * 2] = 0x0D; write_buf[12 + read_num * 2] = 0x0A; fwrite (write_buf, 1, 13 + read_num * 2, fp_write); } /* 寫入終止序列 */ sprintf (write_buf, ":00000001FF"); write_buf[11] = 0x0D; write_buf[12] = 0x0A; fwrite (write_buf, 1, 13, fp_write); } int main (int argc, char *argv[]) { if (argc != 4) { printf ("Usage : %s -b address filename.xxx\n", argv[0]); printf ("-b : indicate the starting address convert to.\n"); printf ("address : starting address.\n"); printf ("filename.xxx : file to be converted.\n"); printf ("output : filename.hex\n"); printf ("example : %s -b 64k rom.bin\n", argv[0]); return -1; } if (strcmp (argv[1], "-b")) { printf ("Invalid argument.\n"); return -1; }; fp_read = fopen (argv[3], "rb"); if (fp_read == NULL) { printf ("Can't open file %s", argv[3]); return -1; } fp_write = fopen ("rom.hex", "w"); if (fp_write == NULL) { printf ("Can't create file rom.hex"); return -1; } calc_start_adr (argv[2]); start_convert (); fclose (fp_read); fclose (fp_write); printf("Convert Seccessfully!\n"); return 0; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM