日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MDK 的编译过程及文件类型全解

發(fā)布時間:2025/4/5 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MDK 的编译过程及文件类型全解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

出處:MDK 的編譯過程及文件類型全解

?

MDK 的編譯過程及文件類型全解

------(在arm9的開發(fā)中,這些東西都是我們自己搞定的,但是在windows上,IDE幫我們做好了,了解這些對深入開發(fā)是很有幫助的,在有arm9開發(fā)的基礎(chǔ)上,下面的東西很容易理解,如果看不懂,證明你還沒有入門。下面的是從world復(fù)制過來的,格式和博客不太兼容,所有開始以字母q的,是world中的 □ 字符)

本章參考資料: MDK 的幫助手冊《ARM Development Tools》,點擊 MDK 界面的
“help->uVision Help”菜單可打開該文件。關(guān)于 ELF 文件格式,參考配套資料里的《ELF
文件格式》文件。
在本章中講解了非常多的文件類型,學(xué)習(xí)時請跟著教程的節(jié)奏,打開實際工程中的文
件來了解。
相信您已經(jīng)非常熟練地使用 MDK 創(chuàng)建應(yīng)用程序了,平時使用 MDK 編寫源代碼,然
后編譯生成機(jī)器碼,再把機(jī)器碼下載到 STM32 芯片上運(yùn)行,但是這個編譯、下載的過程
MDK 究竟做了什么工作?它編譯后生成的各種文件又有什么作用?本章節(jié)將對這些過程進(jìn)
行講解,了解編譯及下載過程有助于理解芯片的工作原理,這些知識對制作 IAP(bootloader)
以及讀寫控制器內(nèi)部 FLASH 的應(yīng)用時非常重要。

?

?

編譯過程生成的不同文件將在后面的小節(jié)詳細(xì)說明,此處先抓住主要流程來理解。
(1) 編譯, MDK 軟件使用的編譯器是 armcc 和 armasm,它們根據(jù)每個 c/c++和匯編源文件
編譯成對應(yīng)的以“.o”為后綴名的對象文件(Object Code,也稱目標(biāo)文件),其內(nèi)容主要
是從源文件編譯得到的機(jī)器碼,包含了代碼、數(shù)據(jù)以及調(diào)試使用的信息;
(2) 鏈接,鏈接器 armlink 把各個.o 文件及庫文件鏈接成一個映像文件“.axf”或“.elf”;

(3) 格式轉(zhuǎn)換,一般來說 Windows 或 Linux 系統(tǒng)使用鏈接器直接生成可執(zhí)行映像文件 elf
后,內(nèi)核根據(jù)該文件的信息加載后,就可以運(yùn)行程序了,但在單片機(jī)平臺上,需要把
該文件的內(nèi)容加載到芯片上,所以還需要對鏈接器生成的 elf 映像文件利用格式轉(zhuǎn)換器
fromelf 轉(zhuǎn)換成“.bin”或“.hex”文件,交給下載器下載到芯片的 FLASH 或 ROM 中。

具體工程中的編譯過程
下面我們打開 “多彩流水燈”的工程,以它為例進(jìn)行講解,其它工程的編譯過程也是
一樣的,只是文件有差異。打開工程后,點擊 MDK 的“rebuild”按鈕,它會重新構(gòu)建整
個工程,構(gòu)建的過程會在 MDK 下方的“Build Output”窗口輸出提示信息,見圖 48-2。

?

?

(3) 使用 armcc 編譯 c/c++文件。圖中列出了工程中所有的 c/c++文件的提示,同樣地,編
譯后每個 c/c++源文件都對應(yīng)有一個獨(dú)立的.o 文件。
(4) 使用 armlink 鏈接對象文件,根據(jù)程序的調(diào)用把各個.o 文件的內(nèi)容鏈接起來,最后生成
程序的 axf 映像文件,并附帶程序各個域大小的說明,包括 Code、 RO-data、 RW-data
及 ZI-data 的大小。
(5) 使用 fromelf 生成下載格式文件,它根據(jù) axf 映像文件轉(zhuǎn)化成 hex 文件,并列出編譯過程出現(xiàn)的錯誤(Error)和警告(Warning)數(shù)量。
(6) 最后一段提示給出了整個構(gòu)建過程消耗的時間。
構(gòu)建完成后,可在工程的“Output”及“Listing”目錄下找到由以上過程生成的各種
文件,見圖 48-4。

?

?

可以看到,每個 C 源文件都對應(yīng)生成了.o、 .d 及.crf 后綴的文件,還有一些額外
的.dep、 .hex、 .axf、 .htm、 .lnp、 .sct、 .lst 及.map 文件。

程序的組成、存儲與運(yùn)行
CODE、 RO、 RW、 ZI Data 域及堆棧空間
在工程的編譯提示輸出信息中有一個語句“Program Size: Code=xx RO-data=xx RWdata=xx ZI-data=xx”,它說明了程序各個域的大小,編譯后,應(yīng)用程序中所有具有同一性質(zhì)的數(shù)據(jù)(包括代碼)被歸到一個域,程序在存儲或運(yùn)行的時候,不同的域會呈現(xiàn)不同的狀
態(tài),這些域的意義如下:
q Code:即代碼域,它指的是編譯器生成的機(jī)器指令,這些內(nèi)容被存儲到 ROM 區(qū)。
q RO-data: Read Only data,即只讀數(shù)據(jù)域,它指程序中用到的只讀數(shù)據(jù),這些數(shù)
據(jù)被存儲在 ROM 區(qū),因而程序不能修改其內(nèi)容。例如 C 語言中 const 關(guān)鍵字定義
的變量就是典型的 RO-data(注:C語言中,const修飾的變量還是可以通過指針更改,c++中是不允許的)。
q RW-data: Read Write data,即可讀寫數(shù)據(jù)域,它指初始化為“非 0 值”的可讀寫
數(shù)據(jù),程序剛運(yùn)行時,這些數(shù)據(jù)具有非 0 的初始值,且運(yùn)行的時候它們會常駐在
RAM 區(qū),因而應(yīng)用程序可以修改其內(nèi)容。例如 C 語言中使用定義的全局變量,
且定義時賦予“非 0 值”給該變量進(jìn)行初始化。
q ZI-data: Zero Initialie data,即 0 初始化數(shù)據(jù),它指初始化為“0 值”的可讀寫數(shù)
據(jù)域,它與 RW-data 的區(qū)別是程序剛運(yùn)行時這些數(shù)據(jù)初始值全都為 0,而后續(xù)運(yùn)
行過程與 RW-data 的性質(zhì)一樣,它們也常駐在 RAM 區(qū),因而應(yīng)用程序可以更改
其內(nèi)容。例如 C 語言中使用定義的全局變量,且定義時賦予“0 值”給該變量進(jìn)
行初始化(若定義該變量時沒有賦予初始值,編譯器會把它當(dāng) ZI-data 來對待,初
始化為 0);
q ZI-data 的棧空間(Stack)及堆空間(Heap):在 C 語言中,函數(shù)內(nèi)部定義的局部變量
屬于棧空間,進(jìn)入函數(shù)的時候從向棧空間申請內(nèi)存給局部變量,退出時釋放局部
變量,歸還內(nèi)存空間。而使用 malloc 動態(tài)分配的變量屬于堆空間。在程序中的棧
空間和堆空間都是屬于 ZI-data 區(qū)域的,這些空間都會被初始值化為 0 值。編譯器
給出的 ZI-data 占用的空間值中包含了堆棧的大小(經(jīng)實際測試,若程序中完全沒
有使用 malloc 動態(tài)申請堆空間,編譯器會優(yōu)化,不把堆空間計算在內(nèi))。

程序的存儲與運(yùn)行
RW-data 和 ZI-data 它們僅僅是初始值不一樣而已,為什么編譯器非要把它們區(qū)分開?
這就涉及到程序的存儲狀態(tài)了,應(yīng)用程序具有靜止?fàn)顟B(tài)和運(yùn)行狀態(tài)。靜止態(tài)的程序被存儲
在非易失存儲器中,如 STM32 的內(nèi)部 FLASH,因而系統(tǒng)掉電后也能正常保存。但是當(dāng)程
序在運(yùn)行狀態(tài)的時候,程序常常需要修改一些暫存數(shù)據(jù),由于運(yùn)行速度的要求,這些數(shù)據(jù)
往往存放在內(nèi)存中(RAM),掉電后這些數(shù)據(jù)會丟失。因此,程序在靜止與運(yùn)行的時候它在
存儲器中的表現(xiàn)是不一樣的,見圖 48-5。

?

?

圖中的左側(cè)是應(yīng)用程序的存儲狀態(tài),右側(cè)是運(yùn)行狀態(tài),而上方是 RAM 存儲器區(qū)域,
下方是 ROM 存儲器區(qū)域。
程序在存儲狀態(tài)時, RO 節(jié)(RO section)及 RW 節(jié)都被保存在 ROM 區(qū)。當(dāng)程序開始運(yùn)行
時,內(nèi)核直接從 ROM 中讀取代碼,并且在執(zhí)行主體代碼前,會先執(zhí)行一段加載代碼,它
把 RW 節(jié)數(shù)據(jù)從 ROM 復(fù)制到 RAM, 并且在 RAM 加入 ZI 節(jié), ZI 節(jié)的數(shù)據(jù)都被初始化為
0。加載完后 RAM 區(qū)準(zhǔn)備完畢,正式開始執(zhí)行主體程序。
編譯生成的 RW-data 的數(shù)據(jù)屬于圖中的 RW 節(jié), ZI-data 的數(shù)據(jù)屬于圖中的 ZI 節(jié)。是
否需要掉電保存,這就是把 RW-data 與 ZI-data 區(qū)別開來的原因,因為在 RAM 創(chuàng)建數(shù)據(jù)的
時候,默認(rèn)值為 0,但如果有的數(shù)據(jù)要求初值非 0,那就需要使用 ROM 記錄該初始值,運(yùn)
行時再復(fù)制到 RAM。
STM32 的 RO 區(qū)域不需要加載到 SRAM,內(nèi)核直接從 FLASH 讀取指令運(yùn)行。計算機(jī)
系統(tǒng)的應(yīng)用程序運(yùn)行過程很類似,不過計算機(jī)系統(tǒng)的程序在存儲狀態(tài)時位于硬盤,執(zhí)行的
時候甚至?xí)焉鲜龅?RO 區(qū)域(代碼、只讀數(shù)據(jù))加載到內(nèi)存,加快運(yùn)行速度,還有虛擬內(nèi)存
管理單元(MMU)輔助加載數(shù)據(jù),使得可以運(yùn)行比物理內(nèi)存還大的應(yīng)用程序。而 STM32 沒
有 MMU,所以無法支持 Linux 和 Windows 系統(tǒng)。

?

?

編譯工具鏈
在前面編譯過程中, MDK 調(diào)用了各種編譯工具,平時我們直接配置 MDK,不需要學(xué)
習(xí)如何使用它們,但了解它們是非常有好處的。例如,若希望使用 MDK 編譯生成 bin 文件
的,需要在 MDK 中輸入指令控制 fromelf 工具;在本章后面講解 AXF 及 O 文件的時候,
需要利用 fromelf 工具查看其文件信息,這都是無法直接通過 MDK 做到的。關(guān)于這些工具
鏈的說明,在 MDK 的幫助手冊《ARM Development Tools》都有詳細(xì)講解,點擊 MDK 界
面的“help->uVision Help”菜單可打開該文件。

設(shè)置環(huán)境變量
調(diào)用這些編譯工具,需要用到 Windows 的“命令行提示符工具”,為了讓命令行方便
地找到這些工具,我們先把工具鏈的目錄添加到系統(tǒng)的環(huán)境變量中。查看本機(jī)工具鏈所在
的具體目錄可根據(jù)上一小節(jié)講解的工程編譯提示輸出信息中找到,如本機(jī)的路徑為
“D:\work\keil5\ARM\ARMCC\bin”。
1. 添加路徑到 PATH 環(huán)境變量
本文以 Win7 系統(tǒng)為例添加工具鏈的路徑到 PATH 環(huán)境變量,其它系統(tǒng)是類似的。
(1) 右鍵電腦系統(tǒng)的“計算機(jī)圖標(biāo)”,在彈出的菜單中選擇“屬性”,見圖 48-6;

?

?

(2) 在彈出的屬性頁面依次點擊“高級系統(tǒng)設(shè)置” ->“環(huán)境變量”,在用戶變量一欄
中找到名為“PATH”的變量,若沒有該變量,則新建一個。編輯“PATH”變量,
在它的變量值中輸入工具鏈的路徑,如本機(jī)的是
“;D:\work\keil5\ARM\ARMCC\bin”,注意要使用“分號;”讓它與其它路徑分隔
開,輸入完畢后依次點確定,見圖 48-7;

?

?

(3) 打開 Windows 的命令行,點擊系統(tǒng)的“開始菜單”,在搜索框輸入
“cmd”,在搜索結(jié)果中點擊“cmd.exe”即可打開命令行,見圖 48-8;

?

(4) 在彈出的命令行窗口中輸入“fromelf”回車,若窗口打印出 formelf 的幫助說明,
那么路徑正常,就可以開始后面的工作了;若提示“不是內(nèi)部名外部命令,也不
是可運(yùn)行的程序…”信息, 說明路徑不對,請重新配置環(huán)境變量,并確認(rèn)該工作
目錄下有編譯工具鏈。
這個過程本質(zhì)就是讓命令行通過“PATH”路徑找到“fromelf.exe”程序運(yùn)行,默認(rèn)運(yùn)
行“fromelf.exe”時它會輸出自己的幫助信息,這就是工具鏈的調(diào)用過程, MDK 本質(zhì)上也
是如此調(diào)用工具鏈的,只是它集成為 GUI,相對于命令行對用戶更友好,畢竟上述配置環(huán)
境變量的過程已經(jīng)讓新手煩躁了。

armcc、 armasm 及 armlink
略,在linux arm開發(fā)中已經(jīng)熟悉過了。

補(bǔ)充MDK生成bin文件:

只能通過命令行:

?

命令行:#K\ARM\ARMCC\bin\fromelf.exe --bin -o #L.bin #L? (#L.bin可以改成@L.bin,主要是生成的路徑不同,@生成在工程文件文件夾,#生成在輸出文件文件夾里)

這樣就可以生成bin文件了。

MDK 工程的文件類型
除了上述編譯過程生成的文件, MDK 工程中還包含了各種各樣的文件,下面我們統(tǒng)一
介紹, MDK 工程的常見文件類型見表 48-3。

?

?

uvprojx、 uvoptx、 uvguix 及 ini 工程文件
在工程的“Project”目錄下主要是 MDK 工程相關(guān)的文件,見圖 48-17。

?

?

  • 1,uvprojx 文件
    uvprojx 文件就是我們平時雙擊打開的工程文件,它記錄了整個工程的結(jié)構(gòu),如芯片類
    型、工程包含了哪些源文件等內(nèi)容,見圖 48-18。

?

?

  • 2,uvoptx 文件
    uvoptx 文件記錄了工程的配置選項,如下載器的類型、變量跟蹤配置、斷點位置以及
    當(dāng)前已打開的文件等等,見圖 48-19。

?

?

  • 3,uvguix 文件
    uvguix 文件記錄了 MDK 軟件的 GUI 布局,如代碼編輯區(qū)窗口的大小、編譯輸出提示
    窗口的位置等等。

?

?

uvprojx、 uvoptx 及 uvguix 都是使用 XML 格式記錄的文件,若使用記事本打開可以看
到 XML 代碼,見圖 48-17。而當(dāng)使用 MDK 軟件打開時,它根據(jù)這些文件的 XML 記錄加
載工程的各種參數(shù),使得我們每次重新打開工程時,都能恢復(fù)上一次的工作環(huán)境。

?

?

這些工程參數(shù)都是當(dāng) MDK 正常退出時才會被寫入保存,所以若 MDK 錯誤退出時(如
使用 Windows 的任務(wù)管理器強(qiáng)制關(guān)閉),工程配置參數(shù)的最新更改是不會被記錄的,重新
打開工程時要再次配置。根據(jù)這幾個文件的記錄類型,可以知道 uvprojx 文件是最重要的,
刪掉它我們就無法再正常打開工程了,而 uvoptx 及 uvguix 文件并不是必須的,可以刪除,重新使用 MDK 打開 uvprojx 工程文件后,會以默認(rèn)參數(shù)重新創(chuàng)建 uvoptx 及 uvguix 文件。
(所以當(dāng)使用 Git/SVN 等代碼管理的時候,往往只保留 uvprojx 文件)

Output 目錄下生成的文件
點擊 MDK 中的編譯按鈕,它會根據(jù)工程的配置及工程中的源文件輸出各種對象和列
表文件,在工程的“Options for Targe->Output->Select Folder for Objects”和“Options for
Targe->Listing->Select Folder for Listings”選項配置它們的輸出路徑,見圖 48-22 和圖 48-23。

?

?

  • lib 庫文件
    在某些場合下我們需要提供給第三方一個可用的代碼庫,但不希望對方看到源碼,這個時候我們就可以把工程生成 lib 文件(Library file)提供給對方,在 MDK 中可配置“Options for Target->Create Library”選項把工程編譯成庫文件,見圖 48-25。
  • ?

    ?

    工程中生成可執(zhí)行文件或庫文件只能二選一,默認(rèn)編譯是生成可執(zhí)行文件的,可執(zhí)行
    文件即我們下載到芯片上直接運(yùn)行的機(jī)器碼。
    得到生成的*.lib 文件后,可把它像 C 文件一樣添加到其它工程中,并在該工程調(diào)用 lib提供的函數(shù)接口,除了不能看到*.lib 文件的源碼,在應(yīng)用方面它跟 C 源文件沒有區(qū)別。

  • dep、 d 依賴文件
    *.dep 和*.d 文件(Dependency file)記錄的是工程或其它文件的依賴, 主要記錄了引用的頭文件路徑, 其中*.dep 是整個工程的依賴, 它以工程名命名, 而*.d 是單個源文件的依賴,它們以對應(yīng)的源文件名命名。這些記錄使用文本格式存儲,我們可直接使用記事本打開,見圖 48-26 和圖 48-27。
  • ?

    ?

    ?

  • crf 交叉引用文件
    *.crf 是交叉引用文件(Cross-Reference file),它主要包含了瀏覽信息(browse information),即源代碼中的宏定義、變量及函數(shù)的定義和聲明的位置。我們在代碼編輯器中點擊“Go To Definition Of ‘xxxx’”可實現(xiàn)瀏覽跳轉(zhuǎn),見圖 48-28,跳轉(zhuǎn)的時候, MDK 就是通過*.crf 文件查找出跳轉(zhuǎn)位置的。
  • ?

    ?

    通過配置 MDK 中的“Option for Target->Output->Browse Information”選項可以設(shè)置編譯時是否生成瀏覽信息,見圖 48-29。只有勾選該選項并編譯后,才能實現(xiàn)上面的瀏覽跳轉(zhuǎn)功能。

    ?

  • o、 axf 及 elf 文件
    *.o、 *.elf、 *.axf、 *.bin 及*.hex 文件都存儲了編譯器根據(jù)源代碼生成的機(jī)器碼,根據(jù)應(yīng)用場合的不同,它們又有所區(qū)別。
  • ELF 文件說明
    *.o、 *.elf、 *.axf 以及前面提到的 lib 文件都是屬于目標(biāo)文件,它們都是使用 ELF 格式來存儲的,關(guān)于 ELF 格式的詳細(xì)內(nèi)容請參考配套資料里的《ELF 文件格式》文檔了解,它講解的是 Linux 下的 ELF 格式,與 MDK 使用的格式有小區(qū)別,但大致相同。在本教程中,僅講解 ELF 文件的核心概念。
    ELF 是 Executable and Linking Format 的縮寫,譯為可執(zhí)行鏈接格式,該格式用于記錄目標(biāo)文件的內(nèi)容。在 Linux 及 Windows 系統(tǒng)下都有使用該格式的文件(或類似格式)用于記錄應(yīng)用程序的內(nèi)容,告訴操作系統(tǒng)如何鏈接、加載及執(zhí)行該應(yīng)用程序。

    目標(biāo)文件主要有如下三種類型:
    (1) 可重定位的文件(Relocatable File), 包含基礎(chǔ)代碼和數(shù)據(jù),但它的代碼及數(shù)據(jù)都沒有指定絕對地址,因此它適合于與其他目標(biāo)文件鏈接來創(chuàng)建可執(zhí)行文件或者共享
    目標(biāo)文件。 這種文件一般由編譯器根據(jù)源代碼生成。
    例如 MDK 的 armcc 和 armasm 生成的*.o 文件就是這一類,另外還有 Linux
    的*.o 文件, Windows 的 *.obj 文件。
    (2) 可執(zhí)行文件(Executable File) ,它包含適合于執(zhí)行的程序, 它內(nèi)部組織的代碼數(shù)據(jù)都有固定的地址(或相對于基地址的偏移),系統(tǒng)可根據(jù)這些地址信息把程序加載到內(nèi)存執(zhí)行。 這種文件一般由鏈接器根據(jù)可重定位文件鏈接而成,它主要是組織各個可重定位文件,給它們的代碼及數(shù)據(jù)一一打上地址標(biāo)號,固定其在程序內(nèi)部
    的位置,鏈接后,程序內(nèi)部各種代碼及數(shù)據(jù)段不可再重定位(即不能再參與鏈接器
    的鏈接)。
    例如 MDK 的 armlink 生成的*.elf 及*.axf 文件, (使用 gcc 編譯工具可生成
    *.elf 文件,用 armlink 生成的是*.axf 文件, *.axf 文件在*.elf 之外,增加了調(diào)試使用的信息,其余區(qū)別不大,后面我們僅講解*.axf 文件),另外還有 Linux 的/bin/bash 文件, Windows 的*.exe 文件。
    (3) 共享目標(biāo)文件(Shared Object File), 它的定義比較難理解,我們直接舉例, MDK
    生成的*.lib 文件就屬于共享目標(biāo)文件,它可以繼續(xù)參與鏈接,加入到可執(zhí)行文件
    之中。 另外, Linux 的.so,如/lib/ glibc-2.5.so, Windows 的 DLL 都屬于這一類。

    • o 文件與 axf 文件的關(guān)系
      根據(jù)上面的分類,我們了解到, *.axf 文件是由多個*.o 文件鏈接而成的,而*.o 文件由相應(yīng)的源文件編譯而成,一個源文件對應(yīng)一個*.o 文件。它們的關(guān)系見圖 48-31。

    ?

    ?

    圖中的中間代表的是 armlink 鏈接器,在它的右側(cè)是輸入鏈接器的*.o 文件,左側(cè)是它輸出的*axf 文件。
    可以看到,由于都使用 ELF 文件格式, *.o 與*.axf 文件的結(jié)構(gòu)是類似的,它們包含ELF 文件頭、程序頭、節(jié)區(qū)(section)以及節(jié)區(qū)頭部表。各個部分的功能說明如下:
    q ELF 文件頭用來描述整個文件的組織,例如數(shù)據(jù)的大小端格式,程序頭、節(jié)區(qū)頭
    在文件中的位置等。
    q 程序頭告訴系統(tǒng)如何加載程序,例如程序主體存儲在本文件的哪個位置,程序的大小,程序要加載到內(nèi)存什么地址等等。 MDK 的可重定位文件*.o 不包含這部分內(nèi)容,因為它還不是可執(zhí)行文件,而 armlink 輸出的*.axf 文件就包含該內(nèi)容了。
    q 節(jié)區(qū)是*.o 文件的獨(dú)立數(shù)據(jù)區(qū)域,它包含提供給鏈接視圖使用的大量信息,如指令(Code)、數(shù)據(jù)(RO、 RW、 ZI-data)、符號表(函數(shù)、變量名等)、重定位信息等,例如每個由 C 語言定義的函數(shù)在*.o 文件中都會有一個獨(dú)立的節(jié)區(qū);
    q 存儲在最后的節(jié)區(qū)頭則包含了本文件節(jié)區(qū)的信息,如節(jié)區(qū)名稱、大小等等。
    總的來說,鏈接器把各個*.o 文件的節(jié)區(qū)歸類、排列,根據(jù)目標(biāo)器件的情況編排地址生成輸出,匯總到*.axf 文件。例如,見圖 48-32,“多彩流水燈”工程中在“bsp_led.c”文件中有一個 LED_GPIO_Config 函數(shù),而它內(nèi)部調(diào)用了“stm32f4xx_gpio.c”的 GPIO_Init 函數(shù),經(jīng)過 armcc 編譯后, LED_GPIO_Config 及 GPIO_Iint 函數(shù)都成了指令代碼,分別存儲在 bsp_led.o 及 stm32f4xx_gpio.o 文件中,這些指令在*.o 文件都沒有指定地址,僅包含了內(nèi)容、大小以及調(diào)用的鏈接信息,而經(jīng)過鏈接器后,鏈接器給它們都分配了特定的地址,并且把地址根據(jù)調(diào)用指向鏈接起來。

    ?

    ELF 文件頭
    接下來我們看看具體文件的內(nèi)容,使用 fromelf 文件可以查看*.o、 *.axf 及*.lib 文件的
    ELF 信息。
    使用命令行,切換到文件所在的目錄,輸入“fromelf –text –v bsp_led.o”命令,可控
    制輸出 bsp_led.o 的詳細(xì)信息,見圖 48-33。 利用“-c、 -z”等選項還可輸出反匯編指令文
    件、代碼及數(shù)據(jù)文件等信息,請親手嘗試一下。

    ?

    生成 bin 文件
    使用 MDK 生成 bin 文件需要使用 fromelf 命令,在 MDK 的“Options For Target->Users”中加入圖 48-35 中的命令。

    ?

    ?

    圖中的指令內(nèi)容為:
    “fromelf --bin --output ..\..\Output\多彩流水燈.bin ..\..\Output\多彩流水燈.axf”
    該指令是根據(jù)本機(jī)及工程的配置而寫的,在不同的系統(tǒng)環(huán)境或不同的工程中,指令內(nèi)容都不一樣,我們需要理解它,才能為自己的工程定制指令,首先看看 fromelf 的幫助,見圖 48-36。

    ?

    ?

    我們在 MDK 輸入的指令格式是遵守 fromelf 幫助里的指令格式說明的,其格式為:
    “fromelf [options] input_file”
    其中 optinos 是指令選項,一個指令支持輸入多個選項,每個選項之間使用空格隔開,我們的實例中使用“--bin”選項設(shè)置輸出 bin 文件,使用“--output file”選項設(shè)置輸出文件的名字為“..\..\Output\多彩流水燈.bin”,這個名字是一個相對路徑格式,如果不了解如何使用“..\”表示路徑,可使用 MDK 命令輸入框后面的文件夾圖標(biāo)打開文件瀏覽器選擇文件,在命令的最后使用“..\..\Output\多彩流水燈.axf”作為命令的輸入文件。具體的格式分解見圖 48-37。

    ?

    ?

    fromelf 需要根據(jù)工程的*.axf 文件輸入來轉(zhuǎn)換得到 bin 文件,所以在命令的輸入文件參數(shù)中要選擇本工程對應(yīng)的*.axf 文件,在 MDK 命令輸入欄中,我們把 fromelf 指令放置在“After Build/Rebuild” (工程構(gòu)建完成后執(zhí)行)一欄也是基于這個考慮,這樣設(shè)置后,工程構(gòu)建完成生成了最新的*.axf 文件, MDK 再執(zhí)行 fromelf 指令,從而得到最新的 bin 文件。設(shè)置完成生成 hex 的選項或添加了生成 bin 的用戶指令后,點擊工程的編譯(build)按鈕,重新編譯工程,成功后可看到圖 48-38 中的輸出。打開相應(yīng)的目錄即可找到文件,若找不到 bin 文件,請查看提示輸出欄執(zhí)行指令的信息,根據(jù)信息改正 fromelf 指令。

    ?

    ?

    hex 文件格式
    hex 是 Intel 公司制定的一種使用 ASCII 文本記錄機(jī)器碼或常量數(shù)據(jù)的文件格式,這種文件常常用來記錄將要存儲到 ROM 中的數(shù)據(jù),絕大多數(shù)下載器支持該格式。
    一個 hex 文件由多條記錄組成,而每條記錄由五個部分組成,格式形如
    “:llaaaatt[dd…]cc”,例如本“多彩流水燈”工程生成的 hex 文件前幾條記錄見代碼清單48-9。

    ?

    ?

    ?

    例如, 代碼清單 48-9 中的第一條記錄解釋如下:
    (1) 02:表示這條記錄數(shù)據(jù)區(qū)的長度為 2 字節(jié);
    (2) 0000:表示這條記錄要存儲到的地址;
    (3) 04:表示這是一條擴(kuò)展線性地址記錄;

    (4) 0800:由于這是一條擴(kuò)展線性地址記錄,所以這部分表示地址的高 16 位,與前面的“0000”結(jié)合在一起,表示要擴(kuò)展的線性地址為“0x0800 0000”,這正好是
    STM32 內(nèi)部 FLASH 的首地址;
    (5) F2:表示校驗和,它的值為(0x02+0x00+0x00+0x04+0x08+0x00)%256 的值再取補(bǔ)碼。
    再來看第二條記錄:
    (1) 10:表示這條記錄數(shù)據(jù)區(qū)的長度為 2 字節(jié);
    (2) 0000:表示這條記錄所在的地址,與前面的擴(kuò)展記錄結(jié)合,表示這條記錄要存儲的 FLASH 首地址為(0x0800 0000+0x0000);
    (3) 00:表示這是一條數(shù)據(jù)記錄,數(shù)據(jù)區(qū)的是地址;
    (4) 00040020C10100081B030008A3020008:這是要按地址存儲的數(shù)據(jù);
    (5) 2F:校驗和為了更清楚地對比 bin、 hex 及 axf 文件的差異,我們來查看這些文件內(nèi)部記錄的信息來進(jìn)行對比。

    ?

    ?

    如果您想要親自閱讀自己電腦上的 bin 文件,推薦使用sublime 軟件打開,它可以把二進(jìn)制數(shù)以 ASCII 碼呈現(xiàn)出來,便于閱讀。

    htm 靜態(tài)調(diào)用圖文件
    在 Output 目錄下,有一個以工程文件命名的后綴為*.bulid_log.htm 及*.htm 文件,如“多彩流水燈.bulid_log.htm”及“多彩流水燈.htm”,它們都可以使用瀏覽器打開。其中*.build_log.htm 是工程的構(gòu)建過程日志,而*.htm 是鏈接器生成的靜態(tài)調(diào)用圖文件。
    在靜態(tài)調(diào)用圖文件中包含了整個工程各種函數(shù)之間互相調(diào)用的關(guān)系圖,而且它還給出了靜態(tài)占用最深的棧空間數(shù)量以及它對應(yīng)的調(diào)用關(guān)系鏈。
    例如圖 48-43 是“多彩流水燈.htm”文件頂部的說明。

    ?

    ?

    該文件說明了本工程的靜態(tài)棧空間最大占用 56 字節(jié)(Maximum Stack Usage:56bytes),這個占用最深的靜態(tài)調(diào)用為“main->LED_GPIO_Config->GPIO_Init”。注意這里給出的空間只是靜態(tài)的棧使用統(tǒng)計,鏈接器無法統(tǒng)計動態(tài)使用情況,例如鏈接器無法知道遞歸函數(shù)的遞歸深度。在本文件的后面還可查詢到其它函數(shù)的調(diào)用情況及其它細(xì)節(jié)。利用這些信息,我們可以大致了解工程中應(yīng)該分配多少空間給棧,有空間余量的情況下,一般會設(shè)置比這個靜態(tài)最深棧使用量大一倍,在 STM32 中可修改啟動文件改變堆棧的大小;如果空間不足,可從本文件中了解到調(diào)用深度的信息,然后優(yōu)化該代碼。
    注意:
    查看了各個工程的靜態(tài)調(diào)用圖文件統(tǒng)計后,我們發(fā)現(xiàn)本書提供的一些比較大規(guī)模的工程例子,靜態(tài)棧調(diào)用最大深度都已超出 STM32 啟動文件默認(rèn)的棧空間大小 0x00000400,即 1024 字節(jié),但在當(dāng)時的調(diào)試過程中卻沒有發(fā)現(xiàn)錯誤,因此我們也沒有修改棧的默認(rèn)大小(有一些工程調(diào)試時已發(fā)現(xiàn)問題,它們的棧空間就已經(jīng)被我們改大了),雖然這些工程實際運(yùn)行并沒有錯誤,但這可能只是因為它使用的棧溢出 RAM 空間恰好沒被程序其它部分修改而已。所以,建議您在實際的大型工程應(yīng)用中(特別是使用了各種外部庫時,如Lwip/emWin/Fatfs 等),要查看本靜態(tài)調(diào)用圖文件,了解程序的棧使用情況,給程序分配合適的棧空間。

    Listing 目錄下的文件
    在 Listing 目錄下包含了*.map 及*.lst 文件,它們都是文本格式的,可使用 Windows 的記事本軟件打開。其中 lst 文件僅包含了一些匯編符號的鏈接信息,我們重點分析 map 文件。

  • map 文件說明
    map 文件是由鏈接器生成的,它主要包含交叉鏈接信息,查看該文件可以了解工程中各種符號之間的引用以及整個工程的 Code、 RO-data、 RW-data 以及 ZI-data 的詳細(xì)及匯總信息。它的內(nèi)容中主要包含了“節(jié)區(qū)的跨文件引用”、“刪除無用節(jié)區(qū)”、“符號映像表”、“存儲器映像索引”以及“映像組件大小”,各部分介紹如下:
  • ?

    在這部分中,詳細(xì)列出了各個*.o 文件之間的符號引用。由于*.o 文件是由 asm 或 c/c++源文件編譯后生成的,各個文件及文件內(nèi)的節(jié)區(qū)間互相獨(dú)立,鏈接器根據(jù)它們之間的互相引用鏈接起來,鏈接的詳細(xì)信息在這個“Section Cross References”一一列出。例如,開頭部分說明的是 startup_stm32f429_439xx.o 文件中的“RESET”節(jié)區(qū)分為它使用的“__initial_sp” 符號引用了同文件“STACK”節(jié)區(qū)。也許我們對啟動文件不熟悉,不清楚這究竟是什么,那我們繼續(xù)瀏覽,可看到 main.o文件的引用說明,如說明 main.o 文件的 i.main 節(jié)區(qū)為它使用的 LED_GPIO_Config 符號引用了 bsp_led.o 文件的 i.LED_GPIO_Config 節(jié)區(qū)。同樣地,下面還有 bsp_led.o 文件的引用說明,如說明了 bsp_led.o 文件的i.LED_GPIO_Config 節(jié)區(qū)為它使用的 GPIO_Init 符號引用了 stm32f4xx_gpio.o 文件的i.GPIO_Init 節(jié)區(qū)。
    可以了解到,這些跨文件引用的符號其實就是源文件中的函數(shù)名、變量名。有時在構(gòu)
    建工程的時候,編譯器會輸出 “Undefined symbol xxx (referred from xxx.o)” 這樣的提示,該提示的原因就是在鏈接過程中,某個文件無法在外部找到它引用的標(biāo)號,因而產(chǎn)生鏈接錯誤。例如,見圖 48-44,我們把 bsp_led.c 文件中定義的函數(shù) LED_GPIO_Config 改名為LED_GPIO_ConfigABCD,而不修改 main.c 文件中的調(diào)用,就會出現(xiàn) main 文件無法找到LED_GPIO_Config 符號的提示。

    刪除無用節(jié)區(qū)
    map 文件的第二部分是刪除無用節(jié)區(qū)的說明(Removing Unused input sections from theimage.),見代碼清單 48-11。

    ?

    ?

    這部分列出了在鏈接過程它發(fā)現(xiàn)工程中未被引用的節(jié)區(qū),這些未被引用的節(jié)區(qū)將會被
    刪除(指不加入到*.axf 文件,不是指在*.o 文件刪除),這樣可以防止這些無用數(shù)據(jù)占用程序空間。

    例如,上面的信息中說明 startup_stm32f429_439xx.o 中的 HEAP(在啟動文件中定義的用于動態(tài)分配的“堆”區(qū))以及 stm32f4xx_adc.o 的各個節(jié)區(qū)都被刪除了,因為在我們這個工程中沒有使用動態(tài)內(nèi)存分配,也沒有引用任何 stm32f4xx_adc.c 中的內(nèi)容。由此也可以知道,雖然我們把 STM32 標(biāo)準(zhǔn)庫的各個外設(shè)對應(yīng)的 c 庫文件都添加到了工程,但不必?fù)?dān)心這會使工程變得臃腫,因為未被引用的節(jié)區(qū)內(nèi)容不會被加入到最終的機(jī)器碼文件中。

    符號映像表
    map 文件的第三部分是符號映像表(Image Symbol Table), 見代碼清單 48-12。

    ?

    ?

    這個表列出了被引用的各個符號在存儲器中的具體地址、占據(jù)的空間大小等信息。如
    我們可以查到 LED_GPIO_Config 符號存儲在 0x080002a5 地址,它屬于 Thumb Code 類型,大小為 106 字節(jié),它所在的節(jié)區(qū)為 bsp_led.o 文件的 i.LED_GPIO_Config 節(jié)區(qū)。
    存儲器映像索引
    map 文件的第四部分是存儲器映像索引(Memory Map of the image), 見代碼清單 48-13。

    ?

    本工程的存儲器映像索引分為 ER_IROM1 及 RW_IRAM1 部分,它們分別對應(yīng) STM32
    內(nèi)部 FLASH 及 SRAM 的空間。相對于符號映像表,這個索引表描述的單位是節(jié)區(qū),而且
    它描述的主要信息中包含了節(jié)區(qū)的類型及屬性,由此可以區(qū)分 Code、 RO-data、 RW-data
    及 ZI-data。
    例如,從上面的表中我們可以看到 i.LED_GPIO_Config 節(jié)區(qū)存儲在內(nèi)部 FLASH 的
    0x080002a4 地址,大小為 0x00000074,類型為 Code,屬性為 RO。而程序的 STACK 節(jié)區(qū)(棧空間)存儲在 SRAM 的 0x20000000 地址,大小為 0x00000400,類型為 Zero,屬性為RW(即 RW-data) 。

    映像組件大小

    map 文件的最后一部分是包含映像組件大小的信息(Image component sizes),這也是最常
    查詢的內(nèi)容,見代碼清單 48-14。

    ?

    這部分包含了各個使用到的*.o 文件的空間匯總信息、整個工程的空間匯總信息以及占
    用不同類型存儲器的空間匯總信息,它們分類描述了具體占據(jù)的 Code、 RO-data、 RW-data及 ZI-data 的大小,并根據(jù)這些大小統(tǒng)計出占據(jù)的 ROM 總空間。
    我們僅分析最后兩部分信息,如 Grand Totals 一項,它表示整個代碼占據(jù)的所有空間
    信息,其中 Code 類型的數(shù)據(jù)大小為 1012 字節(jié),這部分包含了 84 字節(jié)的指令數(shù)據(jù)(inc .data)已算在內(nèi),另外 RO-data 占 444 字節(jié), RW-data 占 0 字節(jié), ZI-data 占 1024 字節(jié)。在它的下面兩行有一項 ROM Totals 信息,它列出了各個段所占據(jù)的 ROM 空間,除了 ZI-data 不占ROM 空間外,其余項都與 Grand Totals 中相等(RW-data 也占據(jù) ROM 空間,只是本工程中沒有 RW-data 類型的數(shù)據(jù)而已)。
    最后一部分列出了只讀數(shù)據(jù)(RO)、可讀寫數(shù)據(jù)(RW)及占據(jù)的 ROM 大小。其中只讀數(shù)
    據(jù)大小為 1456 字節(jié), 它包含 Code 段及 RO-data 段; 可讀寫數(shù)據(jù)大小為 1024 字節(jié),它包含RW-data 及 ZI-data 段;占據(jù)的 ROM 大小為 1456 字節(jié),它除了 Code 段和 RO-data 段,還包含了運(yùn)行時需要從 ROM 加載到 RAM 的 RW-data 數(shù)據(jù)。
    綜合整個 map 文件的信息,可以分析出,當(dāng)程序下載到 STM32 的內(nèi)部 FLASH 時,需
    要使用的內(nèi)部 FLASH 是從 0x0800 0000 地址開始的大小為 1456 字節(jié)的空間;當(dāng)程序運(yùn)行時,需要使用的內(nèi)部 SRAM 是從 0x20000000 地址開始的大小為 1024 字節(jié)的空間。
    粗略一看, 發(fā)現(xiàn)這個小程序竟然需要 1024 字節(jié)的 SRAM,實在說不過去,但仔細(xì)分析
    map 文件后,可了解到這 1024 字節(jié)都是 STACK 節(jié)區(qū)的空間(即棧空間),棧空間大小是在
    啟動文件中定義的,這 1024 字節(jié)是默認(rèn)值(0x00000400)。它是提供給 C 語言程序局部變量申請使用的空間,若我們確認(rèn)自己的應(yīng)用程序不需要這么大的棧,完全可以修改啟動文件,
    把它改小一點,查看前面講解的 htm 靜態(tài)調(diào)用圖文件可了解靜態(tài)的棧調(diào)用情況,可以用它
    作為參考。

    sct 分散加載文件的格式與應(yīng)用
    1. sct 分散加載文件簡介

    當(dāng)工程按默認(rèn)配置構(gòu)建時, MDK 會根據(jù)我們選擇的芯片型號,獲知芯片的內(nèi)部
    FLASH 及內(nèi)部 SRAM 存儲器概況,生成一個以工程名命名的后綴為*.sct 的分散加載文件
    (Linker Control File, scatter loading),鏈接器根據(jù)該文件的配置分配各個節(jié)區(qū)地址,生成分
    散加載代碼,因此我們通過修改該文件可以定制具體節(jié)區(qū)的存儲位置。
    例如可以設(shè)置源文件中定義的所有變量自動按地址分配到外部 SDRAM,這樣就不需
    要再使用關(guān)鍵字“__attribute__”按具體地址來指定了;利用它還可以控制代碼的加載區(qū)與
    執(zhí)行區(qū)的位置,例如可以把程序代碼存儲到單位容量價格便宜的 NAND-FLASH 中,但在
    NAND-FLASH 中的代碼是不能像內(nèi)部 FLASH 的代碼那樣直接提供給內(nèi)核運(yùn)行的,這時可
    通過修改分散加載文件,把代碼加載區(qū)設(shè)定為 NAND-FLASH 的程序位置,而程序的執(zhí)行
    區(qū)設(shè)定為 SDRAM 中的位置,這樣鏈接器就會生成一個配套的分散加載代碼,該代碼會把
    NAND-FLASH 中的代碼加載到 SDRAM 中,內(nèi)核再從 SDRAM 中運(yùn)行主體代碼,大部分
    運(yùn)行 Linux 系統(tǒng)的代碼都是這樣加載的。

    分散加載文件的格式
    下面先來看看 MDK 默認(rèn)使用的 sct 文件,在 Output 目錄下可找到“多彩流水燈.sct”,
    該文件記錄的內(nèi)容見代碼清單 48-15。

    ?

    ?

    在默認(rèn)的 sct 文件配置中僅分配了 Code、 RO-data、 RW-data 及 ZI-data 這些大區(qū)域的地址,鏈接時各個節(jié)區(qū)(函數(shù)、變量等)直接根據(jù)屬性排列到具體的地址空間。
    sct 文件中主要包含描述加載域及執(zhí)行域的部分,一個文件中可包含有多個加載域,而
    一個加載域可由多個部分的執(zhí)行域組成。同等級的域之間使用花括號“{}”分隔開,最外
    層的是加載域,第二層“{}”內(nèi)的是執(zhí)行域,其整體結(jié)構(gòu)見圖 48-45。

    ?

    ?

    加載域
    sct 文件的加載域格式見代碼清單 48-16。

    ?

    ?

    q 加載域名:名稱,在 map 文件中的描述會使用該名稱來標(biāo)識空間。如本例中只有
    一個加載域,該域名為 LR_IROM1。
    q 基地址+地址偏移:這部分說明了本加載域的基地址,可以使用+號連接一個地址
    偏移,算進(jìn)基地址中,整個加載域以它們的結(jié)果為基地址。如本例中的加載域基
    地址為 0x08000000,剛好是 STM32 內(nèi)部 FLASH 的基地址。
    q 屬性列表:屬性列表說明了加載域的是否為絕對地址、 N 字節(jié)對齊等屬性,該配
    置是可選的。本例中沒有描述加載域的屬性。
    q 最大容量:最大容量說明了這個加載域可使用的最大空間,該配置也是可選的,
    如果加上這個配置后,當(dāng)鏈接器發(fā)現(xiàn)工程要分配到該區(qū)域的空間比容量還大,它
    會在工程構(gòu)建過程給出提示。本例中的加載域最大容量為 0x00100000,即 1MB,正是本型號 STM32 內(nèi)部 FLASH 的空間大小。

    ?

    ?

    輸入節(jié)區(qū)描述
    配合加載域及執(zhí)行域的配置,在相應(yīng)的域配置“輸入節(jié)區(qū)描述”即可控制該節(jié)區(qū)存儲
    到域中,其格式見代碼清單 48-18。

    ?

    ?

    q 模塊選擇樣式:模塊選擇樣式可用于選擇 o 及 lib 目標(biāo)文件作為輸入節(jié)區(qū),它可以
    直接使用目標(biāo)文件名或“*”通配符,也可以使用“.ANY”。例如,使用語句“bsp_led.o”可以選擇 bsp_led.o 文件,使用語句“*.o”可以選擇所有 o 文件,使用“*.lib”可以選擇所有 lib 文件,使用“*”或“.ANY”可以選擇所有的 o 文件及 lib 文件。其中“.ANY”選擇語句的優(yōu)先級是最低的,所有其它選擇語句選擇完剩下的數(shù)據(jù)才會被“.ANY”語句選中。

    q 輸入節(jié)區(qū)樣式:我們知道在目標(biāo)文件中會包含多個節(jié)區(qū)或符號,通過輸入節(jié)區(qū)樣
    式可以選擇要控制的節(jié)區(qū)。
    示例文件中“(RESET, +First)”語句的 RESET 就是輸入節(jié)區(qū)樣式,它選擇
    了名為 RESET 的節(jié)區(qū),并使用后面介紹的節(jié)區(qū)特性控制字“+First”表示它要存
    儲到本區(qū)域的第一個地址。示例文件中的“*(InRoot$$Sections)”是一個鏈接器支
    持的特殊選擇符號,它可以選擇所有標(biāo)準(zhǔn)庫里要求存儲到 root 區(qū)域的節(jié)區(qū),如
    __main.o、 __scatter*.o 等內(nèi)容。

    q 輸入符號樣式:同樣地,使用輸入符號樣式可以選擇要控制的符號,符號樣式需
    要使用“:gdef:”來修飾。例如可以使用“*(:gdef:Value_Test)”來控制選擇符號“Value_Test”。
    q 輸入節(jié)區(qū)屬性:通過在模塊選擇樣式后面加入輸入節(jié)區(qū)屬性,可以選擇樣式中不
    同的內(nèi)容,每個節(jié)區(qū)屬性描述符前要寫一個“+”號,使用空格或“,”號分隔開,可以使用的節(jié)區(qū)屬性描述符見表 48-6。

    ??

    例如,示例文件中使用“.ANY(+RO)”選擇剩余所有節(jié)區(qū) RO 屬性的內(nèi)容都分配
    到執(zhí)行域 ER_IROM1 中,使用“.ANY(+RW +ZI)”選擇剩余所有節(jié)區(qū) RW 及 ZI 屬性
    的內(nèi)容都分配到執(zhí)行域 RW_IRAM1 中。
    q 節(jié)區(qū)特性:節(jié)區(qū)特性可以使用“+FIRST”或“+LAST”選項配置它要存儲到的位置,
    FIRST 存儲到區(qū)域的頭部, LAST 存儲到尾部。通常重要的節(jié)區(qū)會放在頭部,而
    CheckSum(校驗和)之類的數(shù)據(jù)會放在尾部。
    例如示例文件中使用“(RESET,+First)”選擇了 RESET 節(jié)區(qū),并要求把它放置到
    本區(qū)域第一個位置,而 RESET 是工程啟動代碼中定義的向量表,見代碼清單 48-19,
    該向量表中定義的堆棧頂和復(fù)位向量指針必須要存儲在內(nèi)部 FLASH 的前兩個地址,
    這樣 STM32 才能正常啟動,所以必須使用 FIRST 控制它們存儲到首地址。

    ?

    ?

    ?

    總的來說,我們的 sct 示例文件配置如下:程序的加載域為內(nèi)部 FLASH 的 0x08000000,
    最大空間為 0x00100000;程序的執(zhí)行基地址與加載基地址相同,其中 RESET 節(jié)區(qū)定義的
    向量表要存儲在內(nèi)部 FLASH 的首地址,且所有 o 文件及 lib 文件的 RO 屬性內(nèi)容都存儲在內(nèi)部 FLASH 中;程序執(zhí)行時 RW 及 ZI 區(qū)域都存儲在以 0x20000000 為基地址,大小為
    0x00030000 的空間(192KB),這部分正好是 STM32 內(nèi)部主 SRAM 的大小。
    鏈接器根據(jù) sct 文件鏈接,鏈接后各個節(jié)區(qū)、符號的具體地址信息可以在 map 文件
    中查看。

    通過 MDK 配置選項來修改 sct 文件
    了解 sct 文件的格式后,可以手動編輯該文件控制整個工程的分散加載配置,但 sct 文
    件格式比較復(fù)雜,所以 MDK 提供了相應(yīng)的配置選項可以方便地修改該文件,這些選項配
    置能滿足基本的使用需求,本小節(jié)將對這些選項進(jìn)行說明。
    選擇 sct 文件的產(chǎn)生方式
    首先需要選擇 sct 文件產(chǎn)生的方式,選擇使用 MDK 生成還是使用用戶自定義的 sct 文
    件。在 MDK 的“Options for Target->Linker->Use Memory Layout from Target Dialog”選項
    即可配置該選擇,見圖 48-46。

    ?

    ?

    該選項的譯文為“是否使用 Target 對話框中的存儲器分布配置”,勾選后,它會根據(jù)
    “Options for Target”對話框中的選項生成 sct 文件,這種情況下,即使我們手動打開它生
    成的 sct 文件編輯也是無效的,因為每次構(gòu)建工程的時候, MDK 都會生成新的 sct 文件覆蓋舊文件。該選項在 MDK 中是默認(rèn)勾選的,若希望 MDK 使用我們手動編輯的 sct 文件構(gòu)建工程,需要取消勾選,并通過 Scatter File 框中指定 sct 文件的路徑,見圖 48-47。

    ?

    ?

    通過 Target 對話框控制存儲器分配
    若我們在 Linker 中勾選了“使用 Target 對話框的存儲器布局”選項,那么“Options
    for Target”對話框中的存儲器配置就生效了。主要配置是在 Device 標(biāo)簽頁中選擇芯片的類
    型,設(shè)定芯片基本的內(nèi)部存儲器信息以及在 Target 標(biāo)簽頁中細(xì)化具體的存儲器配置(包括外部存儲器),見圖 48-48 及圖 48-49。

    ?

    ?

    圖中 Device 標(biāo)簽頁中選定了芯片的型號為 STM32F429IGTx,選中后,在 Target 標(biāo)簽
    頁中的存儲器信息會根據(jù)芯片更新。

    ?

    ?

    在 Target 標(biāo)簽頁中存儲器信息分成只讀存儲器(Read/Only Memory Areas)和可讀寫存儲
    器(Read/Write Memory Areas)兩類,即 ROM 和 RAM,而且它們又細(xì)分成了片外存儲器
    (off-chip)和片內(nèi)存儲器(on-chip)兩類。
    例如,由于我們已經(jīng)選定了芯片的型號, MDK 會自動根據(jù)芯片型號填充片內(nèi)的 ROM
    及 RAM 信息,其中的 IROM1 起始地址為 0x80000000,大小為 0x100000,正是該 STM32
    型號的內(nèi)部 FLASH 地址及大小;而 IRAM1 起始地址為 0x20000000,大小為 0x30000,正是該 STM32 內(nèi)部主 SRAM 的地址及大小。圖中的 IROM1 及 IRAM1 前面都打上了勾,表示這個配置信息會被采用,若取消勾選,則該存儲配置信息是不會被使用的。
    在標(biāo)簽頁中的 IRAM2 一欄默認(rèn)也填寫了配置信息,它的地址為 0x10000000,大小為0x10000,這是 STM32F4 系列特有的內(nèi)部 64KB 高速 SRAM(被稱為 CCM)。當(dāng)我們希望使
    用這部分存儲空間的時候需要勾選該配置,另外要注意這部分高速 SRAM 僅支持 CPU 總
    線的訪問,不能通過外設(shè)訪問。
    下面我們嘗試修改 Target 標(biāo)簽頁中的這些存儲信息,例如,按照圖 48-50 中的 1 配置,
    把 IRAM1 的基地址改為 0x20001000,然后編譯工程,查看到工程的 sct 文件如代碼清單
    48-20 所示;當(dāng)按照圖 48-50 中的 2 配置時,同時使用 IRAM1 和 IRAM2,然后編譯工程,可查看到工程的 sct 文件如代碼清單 48-21 所示。

    ?

    ?

    可以發(fā)現(xiàn), sct 文件都根據(jù) Target 標(biāo)簽頁做出了相應(yīng)的改變,除了這種修改外,在
    Target 標(biāo)簽頁上還控制同時使用 IRAM1 和 IRAM2、加入外部 RAM(如外接的 SDRAM),
    外部 FLASH 等。
    控制文件分配到指定的存儲空間
    設(shè)定好存儲器的信息后,可以控制各個源文件定制到哪個部分存儲器,在 MDK 的工
    程文件欄中,選中要配置的文件,右鍵,并在彈出的菜單中選擇“Options for File xxxx”
    即可彈出一個文件配置對話框,在該對話框中進(jìn)行存儲器定制,見圖 48-51。

    ?

    ?

    在彈出的對話框中有一個“Memory Assignment”區(qū)域(存儲器分配),在該區(qū)域中可以
    針對文件的各種屬性內(nèi)容進(jìn)行分配,如 Code/Const 內(nèi)容(RO)、 Zero Initialized Data 內(nèi)容(ZIdata)以及 Other Data 內(nèi)容(RW-data),點擊下拉菜單可以找到在前面 Target 頁面配置的IROM1、 IRAM1、 IRAM2 等存儲器。例如圖中我們把這個 bsp_led.c 文件的 Other Data 屬性的內(nèi)容分配到了 IRAM2 存儲器(在 Target 標(biāo)簽頁中我們勾選了 IRAM1 及 IRAM2),當(dāng)在bsp_led.c 文件定義了一些 RW-data 內(nèi)容時(如初值非 0 的全局變量),該變量將會被分配到IRAM2 空間,配置完成后點擊 OK,然后編譯工程,查看到的 sct 文件內(nèi)容見代碼清單48-22。

    ?

    ?

    可以看到在 sct 文件中的 RW_IRAM2 執(zhí)行域中增加了一個選擇 bsp_led.o 中 RW 內(nèi)容
    的語句。
    類似地,我們還可以設(shè)置某些文件的代碼段被存儲到特定的 ROM 中,或者設(shè)置某些
    文件使用的 ZI-data 或 RW-data 存儲到外部 SDRAM 中(控制 ZI-data 到 SDRAM 時注意還需要修改啟動文件設(shè)置堆棧對應(yīng)的地址,原啟動文件中的地址是指向內(nèi)部 SRAM 的)。
    雖然 MDK 的這些存儲器配置選項很方便,但有很多高級的配置還是需要手動編寫 sct
    文件實現(xiàn)的,例如 MDK 選項中的內(nèi)部 ROM 選項最多只可以填充兩個選項位置,若想把內(nèi)部 ROM 分成多片地址管理就無法實現(xiàn)了;另外 MDK 配置可控的最小粒度為文件,若想控制特定的節(jié)區(qū)也需要直接編輯 sct 文件。
    ?

    -------------——————內(nèi)容若有錯誤,請您務(wù)必指出,感謝讓我提高并給予我建議的你---———————————---轉(zhuǎn)載請注明出處——————————---——------

    ?

    MDK(Keil) 自動生成bin文件、匯編文件或者HEX文件、ASM文件

    我們在使用MDK(Keil)編譯代碼時,可能會需要把代碼產(chǎn)生成一些不同的文件如生成庫文件、Hex文件,或者Bin文件,Asm文件

    1,需要生成庫文件:

    2,需要生成HEX文件:

    3,需要生成Bin文件:

    4:需要生成Asm匯編文件。

    總結(jié)

    以上是生活随笔為你收集整理的MDK 的编译过程及文件类型全解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。