1 前言
開發完 MCU 軟體後,通常都會生成 hex 檔案或者 bin 檔案,用來做韌體燒錄或者升級,如果用來做產品開發,就涉及到韌體版本的問題,初學者通常採用韌體檔案重新命名來區分版本。
如果需要將版本寫入韌體中,就需要通過一定的方式去實現,實現的方式有很多。
2 介紹
下面介紹一個自動打包微控制器韌體的指令碼軟體,主要實現以下功能:
- 基於 Windows 平臺的微控制器 MCU 韌體指令碼打包工具
- 支援 hex 檔案的裁剪和 hex 檔案的合併
- 可以為 hex 韌體新增版本資訊、Git Commit 分支和提交記錄等
- 按照版本資訊命名hex韌體,可生成 bin 檔案等
- 以上均可通過 ini 配置檔案設定引數對 hex 檔案進行操作
3 實現步驟
下面以 MDK + STM32 開發為例介紹。
3.1 __attribute__ 機制
首先了解一下__attribute__
機制,它是個編譯器指令,告訴編譯器宣告的特性,或者讓編譯器進行更多的錯誤檢查和高階優化。
GUN C中可以使用__attribute__()
給變數、函式和型別設定各種屬性,而__attribute__
的section選項可以改變段的特性;
其中__attribute__((section("section_name")))
的作用是將該定義的函式或資料變數放入指定名為”section_name”段中。
無論是 GNU 還是 ARM 的編譯器, 都支援
__attribute__
所指定的編譯屬性。
開啟keil的options…,取消勾選下圖所示,然後點選“Edit…”。
自動彈出“*.sct”檔案(先編譯通過再操作),下面就是 Keil 中 STM32 的連結檔案,編譯器會根據連結檔案和__attribute__
的section選項(可以自己新增一個段,分配地址和大小)等分配函式和資料變數在程式韌體中的地址。
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00010000 { ; load region size_region
ER_IROM1 0x08000000 0x00010000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00005000 { ; RW data
.ANY (+RW +ZI)
}
}
這裡不做過多介紹了,下面介紹的方式不需要自己修改“*.sct”檔案,還是採用__attribute__
的section選項,只不過在section選項中指定位置即可。
__attribute__ ((section(".ARM.__at_0x08000020")))
3.2 程式碼實現
1. 定義一個結構體,裡面定義一些軟體版本相關的資訊
typedef struct
{
char szVersion[32]; // 軟體版本
char szBuildDate[32]; // 程式編譯日期
char szBuildTime[32]; // 程式編譯時間
char szCommitId[32]; // git commit id
}AppInfo_t;
2. 通過__attribute__
定義一個只讀結構體變數(只讀的目的:防止程式改變、節約RAM),賦初值(其中__DATE_
和__TIME__
是C語言中的內建巨集,分別是當前的編譯日期和編譯時間)。
const AppInfo_t __attribute__ ((section(".ARM.__at_0x08002000"))) sg_tAppInfo =
{
"STM32_TEST",
__DATE__,
__TIME__,
""
};
注:STM32的程式碼起始地址是從0x08000000開始的,且儲存中斷向量表資訊,因此在選擇程式地址的時候一定要繞開,也不能太靠後,不然生成的bin檔案超出了實際的程式碼韌體大小,在實現bin檔案升級的時候就會耗時太長。
3. 通過串列埠列印出來
int main(void)
{
FML_USART_Init();
USART_Printf(0, "Version : %s\r\n", sg_tAppInfo.szVersion);
USART_Printf(0, "buildTime: %s\r\n", sg_tAppInfo.szBuildDate);
USART_Printf(0, "buildTime: %s\r\n", sg_tAppInfo.szBuildTime);
USART_Printf(0, "commitId: %s\r\n\r\n", sg_tAppInfo.szCommitId);
while(1);
}
4. 提交git編譯後,可以看的 git commit id 值(通過 git commit 可以迅速定位是什麼時候的原始碼進行編譯的)
3.3 韌體打包
下載韌體打包指令碼,根據配置設定後,雙擊 bat 即可完成韌體打包,然後點選下載驗證即可。
需要通過 J-LINK 工具包或者 ST-Link 工具開啟生成的韌體進行燒錄(通過Keil編譯直接下載的沒有用,我這裡用的是 ST-Link 工具)。
4 配置檔案內容
下面列舉配置檔案中的選項
; 文中的路徑可採用絕對路徑或者相對路徑(相對於韌體打包bat檔案而言)
; 版本資訊 Flash 起始地址 預留大小 字首字串
[version]
addr=0x08002000
size=32
strPrefix=
; Git 資訊 Flash 起始地址 預留大小
[git_commit]
addr=0x08002060
size=32
[boot_file]
; Boot Hex 檔案路徑 檔名稱
hexFilePath=.\
hexFileName=test_boot
[file]
; Hex 檔案路徑 檔名稱
hexFilePath=.\
hexFileName=test
; 裁剪起始地址 保留大小
hexFileAddr=0x08000000
hexFileSize=0xFFFF
; 打包檔案的輸出路徑
outputPath=.\output
[option]
; 是否合併boot韌體
isMergeBootHexFile=0
; 是否生成 Bin 檔案
isGenerateBin=1
; 是否裁剪 Hex 檔案,根據(hexFileAddr hexFileSize)
isCropHexFile=1
; 是否新增 Git Commit 資訊
isAddGitCommit=0
; 打包成功後是否清除臨時檔案
isClearTmpFile=1
5 下載地址