微控制器 MCU 韌體打包指令碼軟體

2022-06-30 12:03:47

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 下載地址

MCU韌體打包指令碼