[Arduino_AVR] 23-Intel HEX Format
在嵌入式開發的世界裡,我們寫好的 C 或 C++ 代碼經過編譯、連結後,最終會生成一個機器碼檔案。而 Intel HEX 格式(通常副檔名為 .hex)就是最常用來將這些二進位數據「包裝」起來的文字格式,方便燒錄器(Programmer)將程式寫入微控制器的 Flash 記憶體中。
一、為什麼需要 HEX 格式
你可能會問:「既然是二進位數據,為什麼不直接用二進位檔(.bin)?」
• 自我檢查(Checksum): HEX 檔案包含校驗碼,能確保傳輸過程中數據沒有損壞。
• 包含地址資訊: BIN 檔案只是單純的數據流,而 HEX 檔案明確標註了每一段數據應該存放在記憶體的哪個位置。
• 可讀性: HEX 是 ASCII 文字格式,可以用記事本直接打開查看,方便除錯。
回到目錄二、HEX 檔案的組成結構
一個 HEX 檔案是由許多行組成的,每一行我們稱為一條 Record(紀錄)。
每一行都以冒號 : 開頭,並遵循嚴格的格式:
:LLAAAATT[DD…DD]CC
1. 起始碼 :
- 固定為冒號
: - 表示一行資料開始
2. 資料長度 LL
- 1 byte(2 hex字元)
- 表示後面 資料區(DD)有幾個 byte
範例: 10 → 表示 16 bytes 資料
3. 位址 AAAA
- 2 bytes(4 hex字元)
- 表示資料要寫入的 記憶體位址(offset)
注意:
- 這通常是 相對位址
- 真正位址可能要搭配 Extended Address 計算
4. 記錄類型 TT
這是 Intel HEX 的核心之一,共有幾種常見類型:
- Extended Segment Address(Type 02)(較舊,無法應付大容量)
實際位址 = ( Segment << 4 ) + AAAA
- Extended Linear Address(Type 04)位址延伸機制(重要,支援大容量)
由於 AAAA 只有 16-bit,無法表示大記憶體,因此需要擴展。
假設有一紀錄 :02000004XXXXCC
XXXX = 高 16 bits 位址
實際位址 = ( XXXX << 16 ) + AAAA
範例: :020000040001F9
Base Address = ( 0x0001 << 16 ) + 0x00000 = 0x00010000
5. 資料欄位 DD
- 長度 = LL bytes
- 每個 byte 用 2 個 hex 表示
範例:48656C6C6F → “Hello”
Get Morgan Ting’s stories in your inbox
Join Medium for free to get updates from this writer.
6. 校驗碼 CC
用於檢查資料是否正確。
計算方式:
1. 將以下欄位全部轉成 byte 相加:
LL + AAAA ( 高+低 ) + TT + 所有DD
2. 取總和的低 8 位元
3. 再做 2’s 補數
完整計算如下 ( 確保長度為 8 bit )
Checksum = ( 0x100 — ( sum & 0xFF )) & 0xFF
實例演練:
假設 PC 傳送了三個 Byte 的資料:0x01, 0x02, 0x03。
1. 計算加總 (sum):0x01 + 0x02 + 0x03 = 0x06
2. 帶入公式:Checksum = ( 0x100 — ( 0x06 & 0xFF )) & 0xFF
Checksum = 0xFA & 0xFF = 0xFA
驗證:接收端如何驗證?
下位機收到資料與 Checksum 後,直接將所有資料加在一起:Total = 0x01 + 0x02 + 0x03 + 0xFA = 0x0100 此時,下位機只要檢查 (Total & 0xFF) == 0x00。因為 0x0100 & 0xFF 確實是 0x00,代表傳輸完全無誤。
7. 結束記錄
範例: :00000001FF
LL=00 無資料
TT=01 EOF
CC=FF checksum
每個 HEX 檔 必須以這行結尾
回到目錄三、實戰範例拆解
接下來我們實際編輯一個簡單的程式碼 Blink ,將 Atmega328P 的 PortB_5 作為輸出 ( 對應 Arduino UNO 的 Pin13 ),並反覆切換輸出狀態,每隔一秒鐘切換一次。利用 Arduino IDE 以 AVR 指令撰寫並輸出 Hex 檔案。
- 程式碼如下:
#define F_CPU 16000000UL // 定義 CPU 頻率為 16MHz,供 delay 函式計算使用
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
// 1. 設置 PB5 為輸出模式
// DDRB 是方向暫存器,(1 << 5) 代表將第 5 位元設為 1
DDRB |= (1 << DDB5);
while (1) {
// 2. 翻轉 PB5 的電位
PORTB ^= (1 << PORTB5); // 讀取 PB5 當前狀態,做XOR運算後再回寫到 PB5
// 3. 等待 1000 毫秒 (1秒)
_delay_ms(1000);
}
return 0;
}存檔後按下編譯按鈕 ( Sketch >> verify / compile )。
2. 如何從 Arduino IDE 得到 Hex 檔案:
Sketch >> Export compiled Binary
完成後到存檔目錄會找到 3 個檔案分別是後綴 .ino 的原始檔、編譯後的Hex 檔、附加 Bootloader 的 Hex 檔( 免費附贈 Bootloader ,真划算 XD )。
3. 以 Notepad++ 之類的編輯軟體打開 Hex 檔,不要用附加 Bootloader 那個檔案。
結果如下
:100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C944F000C940000E6
:10008000259A90E285B1892785B92FEF33ED80E37A
:10009000215030408040E1F700C00000F3CFF894D9
:0200A000FFCF90
:00000001FF可以看到 【位址段】很整齊地從 0x0000 開始按順序 0x0010 往下排列。沒有看見特別的 Record Type ,只有 00 型一般資料。後面重複出現 0C 94 … 那是 AVR 跳轉指令,一直到位址 0x0060 才開始我們的主要程式,在此之前都在做一些初始化動作。
接著往下看到 :0200A000FFCF90 這是無窮迴圈重複執行相關程式內容,最後 :00000001FF 表示檔案結束。
回到目錄四、總結
Intel Hex 檔案格式為大部分嵌入式系統如8051、AVR、ARM等遵循。其中以 Record 為記錄單位,一筆 Record 包含開頭、資料長度、位址、類型、資料內容、校驗碼,內容為 ASCII 碼容易使用編輯軟體觀察,Record 不必按順序排列如此可以在修改局部程式碼的情況下無須進行全部改動,每一筆 Record 含有校驗碼可以進行檢查,付出極少代價換得資料安全傳輸。
感謝有您
感謝讀者閱讀文章若有任何意見與想法歡迎留言指導讓知識擴大加深。
謝謝您。





