第八章建立HAL版本MDK工程
在前面的章節我們介紹了STM32H7xx官方固件包的一些知識,本章我們将講解建立HAL庫版本的MDK工程的詳細步驟。我們把本章建立好的工程放在CD光牒裡,路徑:4,程式源碼\2,标準例程-HAL庫版本\實驗0 基礎入門實驗\實驗0-3,建立工程實驗-HAL庫版本,大家在學習建立工程過程中間遇到一些問題,可以直接打開這個工程,然後對比學習。
本章将分為如下三個小節:
8.1 建立HAL庫版本MDK工程
8.2 下載下傳驗證
8.3 分散加載檔案簡介
8.1 建立HAL庫版本MDK工程
本節我們将教大家如何建立一個STM32H750的HAL庫版本MDK5工程。為了友善大家參考,我們将本節最終建立好的工程模闆存放在A盤:4、程式源碼\2,标準例程-HAL庫版本\實驗0 基礎入門實驗\實驗0-3,建立工程實驗-HAL庫版本,如遇建立工程問題,請打開該實驗對比。
整個建立過程比較複雜,我們将其拆分為5個步驟進行講解,請準備大概2個小時時間,耐心細緻的做完!對你後續的學習非常有幫助!
在建立工程之前,首先我們要做如下準備:
1、 STM32Cube官方固件包:我們使用的固件包版本是STM32Cube_FW_H7_V1.6.0,固件包路徑:A盤à8,STM32參考資料à1,STM32CubeH7固件包。
2、開發環境搭建:參考本書第三章相關内容。
8.1.1建立工程檔案夾
建立工程檔案夾分為2個步驟:1,建立工程檔案夾;2,拷貝工程相關檔案。
1. 建立工程檔案夾
首先我們在桌面建立一個工程根目錄檔案夾,後續的工程檔案都将在這個檔案夾裡建立,我們把這個檔案夾重命名為:實驗0-3,建立工程實驗-HAL庫版本。如圖8.1.1.1所示:
圖8.1.1.1 建立工程根目錄檔案夾
為了讓工程的檔案目錄結構更加清晰易懂,我們會在工程根目錄檔案夾下建立以下幾個檔案夾,每個檔案夾名稱及其作用如表8.1.1.1所示:
名稱 | 作用 |
Drivers | 存放與硬體相關的驅動層檔案 |
Middlewares | 存放正點原子提供的中間層元件檔案和第三方中間層檔案 |
Output | 存放工程編譯輸出檔案 |
Projects | 存放MDK工程檔案 |
User | 存放HAL庫使用者配置檔案、main.c、中斷處理檔案,以及分散加載檔案 |
表8.1.1.1 工程根目錄建立檔案夾及其作用
建立完成以後,最後得到我們的工程根目錄檔案夾如圖8.1.1.2所示。
圖8.1.1.2 工程根目錄檔案夾
另外我們的工程根檔案目錄下還有一個名為keilkill.bat的可執行檔案,輕按兩下便可執行。其作用是删除編譯器編譯後的無關檔案,減少工程占用的記憶體,友善打包。還有一個名為readme的記事本檔案,其作用是介紹本實驗的各種資訊。
工程根目錄及其相關檔案夾建立好以後,我們需要拷貝一些工程相關檔案過來(主要是在Drivers檔案夾裡面),以便等下的建立工程需要。
2. 拷貝工程相關檔案
接下來,我們按圖8.1.1.2的根目錄檔案夾順序介紹每個檔案夾及其需要拷貝的檔案。
Drivers檔案夾
該檔案夾用于存放與硬體相關的驅動層檔案,一般包括如表8.1.1.2所示的三個檔案夾:
檔案夾名稱 | 作用 |
BSP | 存放開發闆闆級支援包驅動代碼,如各種外設驅動 |
CMSIS | 存放CMSIS底層代碼,如啟動檔案(.s檔案)、stm32h7xx.h等 |
STM32H7xx_HAL_Driver | 存放HAL庫驅動代碼源檔案 |
SYSTEM | 存放正點原子系統級核心驅動代碼,如sys.c、delay.c和usart.c |
表8.1.1.2 Drivers包含檔案夾
BSP檔案夾,用于存放正點原子提供的闆級支援包驅動代碼,如:LED、蜂鳴器、按鍵等。本章我們暫時用不到該檔案夾,不過可以先建好備用。
CMSIS檔案夾,用于存放CMSIS底層代碼(ARM和ST提供),如:啟動檔案(.s檔案)、stm32h7xx.h等各種頭檔案。該檔案夾我們可以直接從STM32CubeH7固件包(路徑:A盤à8,STM32參考資料à1,STM32CubeH7固件包)裡面拷貝,不過由于固件包裡面的CMISIS相容了太多晶片,導緻非常大(300多MB),是以我們根據實際情況,對其進行了大幅精簡,精簡後的CMSIS檔案夾大小為2.3MB左右。精簡後的CMSIS檔案夾大家可以在:A盤à4,程式源碼à1,标準例程-HAL庫版本檔案夾裡面的任何一個實驗的Drivers檔案夾裡面拷貝過來。
SYSTEM檔案夾,用于存放正點原子提供的系統級核心驅動代碼,如:sys.c、delay.c和usart.c等,友善大家快速搭建自己的工程。該檔案同樣可以從:A盤à4,程式源碼à1,标準例程-HAL庫版本檔案夾裡面的任何一個實驗的Drivers檔案夾裡面拷貝過來。
STM32H7xx_HAL_Driver檔案夾,用于存放ST提供的H7xx HAL庫驅動代碼。該檔案夾我們可以直接從STM32CubeH7固件包裡面拷貝。直接拷貝“STM32CubeH7固件包àDrivers”路徑下的“STM32H7xx_HAL_Driver”檔案夾到我們工程的Drivers下,隻保留Inc和Src檔案夾即可。
執行完以上操作後,Drivers檔案夾最終結構如圖8.1.1.3所示:
圖8.1.1.3 工程根目錄下的Drivers檔案夾
關于工程根目錄下的Drivers檔案操作到這裡就完成了,可以說步驟是有點多。在此過程遇到問題的話,請大家多參考我們提供的實驗0-3,建立工程實驗-HAL庫版本工程,一步步操作。
Middlewares檔案夾
該檔案夾用于存放正點原子和其他第三方提供的中間層代碼(元件/Lib等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各種OS、各種GUI等等。本章我們暫時用不到該檔案夾,不過可以先建好備用,後面的實驗将會陸續添加各種檔案。
Output檔案夾
該檔案夾用于存放編譯器編譯工程輸出的中間檔案,比如:.hex、.bin、.o檔案等等。這裡不需要操作,後面隻需要在MDK裡面設定該檔案夾為編譯過程中間檔案的存放檔案夾就行。
Projects檔案夾
該檔案夾用于存放編譯器(MDK、IAR等)工程檔案,我們主要用MDK,為了友善區分,我們在該檔案夾下建立:MDK-ARM檔案夾,用于存放MDK的工程檔案,如圖8.1.1.4所示:
圖8.1.1.4 在Projects檔案夾下建立MDK-ARM檔案夾
User檔案夾
User檔案夾用于存放HAL庫使用者配置檔案、main.c檔案、中斷處理檔案,以及分散加載檔案。
我們首先從官方固件包裡面直接拷貝官方的模闆工程下的HAL庫使用者配置檔案和中斷處理檔案到我們的User檔案夾裡。官方的模闆工程路徑:STM32Cube_FW_H7_V1.6.0\Projects\ STM32H750B-DK\Templates\Template_Project,打開Template_Project檔案夾,如圖8.1.1.8所示。
圖8.1.1.8 官方模闆工程根目錄
我們需要的檔案就在Inc和Src檔案夾裡面,在這兩個檔案夾裡面找到:stm32h7xx_it.c、stm32h7xx_it.h、stm32h7xx_hal_conf.h這三個檔案,并且拷貝到我們的User檔案夾下。
最後在User檔案夾下建立一個命名為SCRIPT的檔案夾,用于存放分散加載檔案。分散加載檔案直接在我們的實驗0-2,建立工程實驗-HAL庫版本工程對應位置拷貝過來,後面再給大家講解。main.c檔案我們也是放在User檔案夾裡面的,後面在MDK裡面教大家建立.c檔案并儲存。
8.1.2建立一個工程架構
首先,打開MDK軟體。然後點選ProjectàNew uVision Project如圖8.1.2.1所示:
圖8.1.2.1 建立MDK工程
然後彈出工程命名和儲存的操作視窗,我們将工程檔案儲存路徑設定在上一節建立的工程檔案夾内,具體路徑為:桌面à實驗0-2,建立工程實驗-寄存器版本àProjectsàMDK-ARM,工程名字我們取:atk_h750,最後點選儲存即可。具體操作視窗如圖8.1.2.2所示:
圖8.1.2.2 儲存工程界面
之後,彈出器件選擇對話框,如圖8.1.2.3所示。因為MiniPRO STM32H750開發闆所使用的STM32型号為STM32H750VBT6,是以我們選擇:STMicroelectronicsàSTM32H7 SeriesàSTM32H750àSTM32H750VBTx(如果使用的是其他系列的晶片,選擇相應的型号就可以了,特别注意:一定要安裝對應的器件pack才會顯示這些内容哦!!如果沒得選擇,請關閉MDK,然後安裝 A盤:6,軟體資料\1,軟體\MDK5\ Keil.STM32H7xx_DFP.2.5.0.pack這個安裝包後重試)。
圖8.1.2.3 器件選擇界面
點選OK,MDK會彈出Manage Run-Time Environment對話框,如圖8.1.2.4所示:
圖8.2.1.4 Manage Run-Time Environment界面
這是MDK5新增的一個功能,在這個界面,我們可以添加自己需要的元件,進而友善建構開發環境,不過這裡我們不做介紹。是以在圖8.1.2.4所示界面,我們直接點選Cancel,即可,得到如圖8.1.2.5所示界面:
圖8.1.2.5 工程初步建立
此時,我們打開MDK-ARM檔案夾,會看到MDK在該檔案夾下自動建立了3個檔案夾(DebugConfig、Listings和Objects),如圖8.1.2.6所示:
圖8.1.2.6 MDK建立工程時自動建立的檔案夾
這三個檔案夾的作用如表8.1.2.1所示:
檔案夾 | 作用 |
DebugConfig | 用于存放調試設定資訊檔案(.dbgconf),不可删除! |
Listings | 用于存放編譯過程産生的連結清單等檔案 |
Objects | 用于存放編譯過程産生的調試資訊、.hex、預覽、.lib檔案等 |
表8.1.2.1 三個檔案夾及其作用
編譯過程産生的連結清單、調試資訊、預覽、lib等檔案,統稱為中間檔案。為了統一管理,友善使用,我們會把輸出在Listings和Objects檔案夾的内容,統一改為輸出到Output檔案夾(通過魔術棒設定),我們先把MDK自動生成的這兩個檔案夾(Listings和Objects)删除。
至此,我們還隻是建了一個架構,還有好幾個步驟要做,比如添加檔案、魔術棒設定、編寫main.c等。
8.1.3添加檔案
本節将分5個步驟:1,設定工程名和分組名;2,添加啟動檔案;3,添加SYSTEM源碼4,添加 User 源碼;5,添加 STM32H7xx_HAL_Driver 源碼。
1. 設定工程名和分組名
在ProjectàTarget上右鍵,選擇Manage Project Items…(方法一)或在菜單欄點選品字形紅綠白圖示(方法二)進入工程管理界面,如圖8.1.3.1所示:
圖8.1.3.1 進入工程管理界面
在工程管理界面,我們可以執行設定工程名字(Project Targets)、分組名字(Groups)以及添加每個分組的檔案(Files)等操作。我們設定工程名字為:Template,并設定四個分組:Startup(存放啟動檔案)、User(存放main.c等使用者代碼)、Drivers/SYSTEM(存放系統級驅動代碼)、Readme(存放工程說明檔案),如圖8.1.3.2所示:
圖8.1.3.2 設定工程名和分組名
設定好之後,我們點選OK,回到MDK主界面,可以看到我們設定的工程名和分組名如圖8.1.3.3所示:
圖8.1.3.3 設定成功
這裡我們隻是建立了一個簡單的工程,并沒有添加BSP、Middlewares等分組,後面随着工程複雜程度的增加,我們需要一步步添加對應的分組。
注意:為了讓工程結構清晰,我們會盡量讓MDK的工程分組和我們前面建立的工程檔案夾對應起來,由于MDK分組不支援多級目錄,是以我們将路徑也帶入分組命名裡面,以便區分。如:User分組對應User檔案夾裡面的源碼,Drivers/SYSTEM分組,對應Drivers/SYSTEM檔案夾裡面的源碼,Drivers/BSP分組對應Drivers/BSP檔案夾裡面的源碼等。
2. 添加啟動檔案
啟動檔案(.s檔案)包含STM32的啟動代碼,其主要作用包括:1、堆棧(SP)的初始化;2、初始化程式計數器(PC);3、設定向量表異常事件的入口位址;4、調用main函數等,是每個工程必不可少的一個檔案,我們在本書第九章會有詳細介紹。
啟動檔案由ST官方提供,存放在STM32CubeH7軟體包的:DriversàCMSISàDevice àSTàSTM32H7xxàSourceàTemplatesàarm檔案夾下。因為我們開發闆使用的是STM32H750VBT6,對應的啟動檔案為:startup_stm32h750xx.s,為了節省空間,在精簡版CMSIS檔案夾裡面我們把其他啟動檔案都删了。
關于啟動檔案的說明,我們就介紹這麼多,接下來我們看如何添加啟動檔案到工程裡面。我們有兩種方法給MDK的分組添加檔案:1,輕按兩下Project下的分組名添加。2,進入工程管理界面添加。
這了我們使用方法1添加(路徑:實驗0-3,建立工程實驗-HAL庫版本\Drivers\CMSIS\
Device\ST\STM32H7xx\Source\Templates\arm),如圖8.1.3.4所示:
圖8.1.3.4 輕按兩下分組添加啟動檔案(startup_stm32h750xx.s)
上圖中,我們也可以點選Add按鈕進行檔案添加。添加完後,點選Close,完成啟動檔案
添加,得到工程分組如圖8.1.3.5所示:
圖8.1.3.5 啟動檔案添加成功
3. 添加SYSTEM源碼
這裡我們在工程管理界面(方法2)進行SYSTEM源碼添加。點選:
按鈕,進入工程管理界面,選中Drivers/SYSTEM分組,然後點選:Add Files,進入檔案添加對話框,依次添加delay.c、sys.c和usart.c到該分組下,如圖8.1.3.6所示:
圖8.1.3.6 添加SYSTEM源碼
注意:這些源碼都是在第8.1.1小節的第二步拷貝過來的,如果之前沒拷貝,是找不到這些源碼的。添加完成後,如圖8.1.3.7所示:
圖8.1.3.7 SYSTEM源碼添加完成
4. 添加User源碼
這裡我們在工程管理界面(方法2)進行User源碼添加。點選:
按鈕,進入工程管理界面,選中User分組,然後點選:Add Files,進入檔案添加對話框,依次添加stm32h7xx_it.c和system_stm32h7xx.c到該分組下,如圖8.1.3.8所示:
圖8.1.3.8 添加User源碼
注意:這些源碼都是在第8.1.1小節的第二步拷貝過來的,如果之前沒拷貝,是找不到這些源碼的。添加完成後,如圖8.1.3.9所示:
圖8.1.3.9 User源碼添加完成
5. 添加STM32H7xx_HAL_Driver源碼
接下來我們往Drivers/STM32H7xx_HAL_Driver分組裡添加檔案。點選:
按鈕,進入工程管理界面,選中Drivers/STM32H7xx_HAL_Driver分組,然後點選:Add Files,進入檔案添加對話框,依次添加stm32h7xx_hal.c、stm32h7xx_hal_cortex.c、stm32h7xx_hal_dma.c、stm32h7xx_hal_gpio.c、stm32h7xx_hal_pwr.c、stm32h7xx_hal_pwr_ex.c、stm32h7xx_hal_rcc.c、stm32h7xx_hal_rcc_ex.c、stm32h7xx_hal_uart.c、stm32h7xx_hal_uart_ex.c、stm32h7xx_hal_usart.c和stm32h7xx_hal_usart_ex.c到該分組下,如圖8.1.3.10所示:
圖8.1.3.10 添加STM32H7xx_HAL_Driver源碼
添加完成後,如圖8.1.3.11所示:
圖8.1.3.11 STM32H7xx_HAL_Driver源碼添加完成
可以看到分組中有些.c檔案有個小鑰匙的符号,這是因為官方的固件包的檔案設定了隻讀權限,我們取消隻讀權限就好了,方法如圖8.1.2.12所示。
圖8.1.2.12 取消工程檔案夾的隻讀權限
8.1.4魔術棒設定
為避免編寫代碼和編譯報錯,我們需要通過魔術棒對MDK工程進行相關設定。在MDK主界面,點選:
(魔術棒圖示,即Options for Target按鈕),進入工程設定對話框,我們将進行如下幾個頁籤的設定。
1. 設定Target頁籤
在魔術棒àTarget頁籤裡面,我們進行如圖8.1.4.1所示設定:
圖8.1.4.1 Target頁籤設定
上圖中,我們設定晶片所使用的外部晶振頻率為8Mhz,選擇ARM Compiler版本為:Use default compiler version 5(即AC5編譯器)。
這裡我們說明一下AC5和AC6編譯的差異,如表8.1.4.2所示:
對比項 | AC5 | AC6 | 說明 |
中文支援 | 較好 | 較差 | AC6對中文支援極差,gotodefinition無法使用,誤報等 |
代碼相容性 | 較好 | 較差 | AC6對某些代碼優化可能導緻運作異常,需慢慢調試 |
編譯速度 | 較慢 | 較快 | AC6編譯速度比AC5快 |
文法檢查 | 一般 | 嚴格 | AC6文法檢查非常嚴格,代碼嚴謹性較好 |
表8.1.4.1 AC5&AC6簡單對比
由于AC5對中文支援比較好,且相容性相對好一點,為了避免不必要的麻煩,我們推薦大家使用AC5編譯器。為了讓大家自由選擇,我們正點原子的源碼,也是支援AC6編譯器的,不過在頁籤設定上稍有差異,具體差異如表8.1.4.2所示:
頁籤 | AC5 | AC6 | 說明 |
Target | 選擇AC5編譯器 | 選擇AC6編譯器 | 選擇對應的編譯器 |
C/C++ | Misc Controls 無需設定 | Misc Controls設定: -Wno-invalid-source-encoding | AC6需設定編譯選項以關閉對漢字的錯誤警告,AC5則不要 |
表8.1.4.2 AC5&AC6設定差異
2. 設定Output頁籤
在魔術棒àOutput頁籤裡面,進行如圖8.1.4.2所示設定:
圖8.1.4.2 設定Output頁籤
注意,我們勾選:Browse Information,用于輸出浏覽資訊,這樣就可以使用go to definition檢視函數/變量的定義,對我們後續調試代碼比較有幫助,如果不需要調試代碼,則可以去掉這個勾選,以提高編譯速度。
3. 設定Listing頁籤
在魔術棒àListing頁籤裡面,進行如圖8.1.4.3所示設定:
圖8.1.4.3 設定Listing頁籤
經過Output和Listing這兩步設定,原來存儲在Objects和Listings檔案夾的内容(中間檔案)就都改為輸出到Output檔案夾了。
4. 設定C/C++頁籤
在魔術棒àC/C++頁籤裡面,進行如圖8.1.4.4所示設定:
圖8.1.4.4 設定C/C++頁籤
在②處設定了全局宏定義:STM32H750xx,用于定義所用STM32型号,在stm32h7xx.h裡面會用到該宏定義。
在③處設定了優化等級為-O0,可以得到最好的調試效果,當然為了提高優化效果提升性能并降低代碼量,可以設定-O1~-O3,數字越大效果越明顯,不過也越容易出問題。注意:當使用AC6編譯器的時候,這裡推薦預設使用-O1優化。
在④處勾選C99模式,即使用C99 C語言标準。
在⑤處,我們可以進行頭檔案包含路徑設定,點選此按鈕,進行如圖8.1.4.5所示設定:
圖8.1.4.5 設定頭檔案包含路徑
上圖中我們設定了4個頭檔案包含路徑,其中3個在Drivers檔案夾下,一個在User檔案夾下。為避免頻繁設定頭檔案包含路徑,正點原子最新源碼的include全部使用相對路徑,也就是我們隻需要在頭檔案包含路徑裡面指定一個檔案夾,那麼該檔案夾下的其他檔案夾裡面的源碼,如果全部是使用相對路徑,則無需再設定頭檔案包含路徑了,直接在include裡面就指明了頭檔案所在。
關于相對路徑,這裡大家記住3點:
1,預設路徑就是指MDK工程所在的路徑,即.uvprojx檔案所在路徑(檔案夾)
2,“./”表示目前目錄(相對目前路徑,也可以寫做“.\”)
3,“../”表示目前目錄的上一層目錄(也可以寫做“..\”)
舉例來說,上圖中:..\..\Drivers\CMSIS\Device\ST\STM32H7xx\Include,前面兩個“..\”,表示Drivers檔案夾在目前MDK工程所在檔案夾(MDK-ARM)的上2級目錄下,具體解釋如圖8.1.4.6所示:
圖8.1.4.6 ..\..\Drivers\CMSIS\Device\ST\STM3H7xx\Include的解釋
上圖表示根據頭檔案包含路徑:..\..\Drivers\CMSIS\Device\ST\STM32H7xx\Include,編譯器可以找到⑥處所包含的這些頭檔案,即代碼裡面可以直接include這些頭檔案使用。
再舉個例子,在完成如圖6.1.4.5所示的頭檔案包含路徑設定以後,我們在代碼裡面編寫:
#include "./SYSTEM/sys/sys.h"
即表示目前頭檔案包含路徑所訓示的4個檔案夾裡面,肯定有某一個檔案夾包含了:SYSTEM/sys/sys.h的路徑,實際上就是在Drivers檔案夾下面,兩者結合起來就相當于:
#include "../../Drivers/SYSTEM/sys/sys.h"
這就是相對路徑。它既可以減少頭檔案包含路徑設定(即減少MDK配置步驟,免去頻繁設定頭檔案包含路徑的麻煩),同時又可以很友善的知道頭檔案具體在那個檔案夾,是以我們推薦在編寫代碼的時候使用相對路徑。
關于相對路徑,我們就介紹這麼多,大家搞不明白的可以在網上搜尋相關資料學習,也可以在後面的學習,分析我們其他源碼,慢慢體會,總之不難,但是好用。
最後,我們如果使用AC6編譯器,則在圖6.1.4.4的Misc Controls處需要設定:-Wno-invalid-source-encoding,避免中文編碼報錯,如果使用AC5編譯器,則不需要該設定!!
5. 設定Debug頁籤
在魔術棒àDebug頁籤裡面,進行如圖8.1.4.7所示設定:
圖8.1.4.7 Debug頁籤設定
圖中,我們選擇使用:CMSIS-DAP仿真器,使用SW模式,并設定最大時鐘頻率為10Mhz,以得到最高下載下傳速度。當我們将仿真器和開發闆連接配接好,并給開發闆供電以後,仿真器就會找到開發闆晶片,并在SW Device視窗顯示晶片的IDCODE、Device Name等資訊(圖中⑤處),當無法找到時,請檢查供電和仿真器連接配接狀況。
6. 設定Utilities頁籤
在魔術棒àDebug頁籤裡面,進行如圖8.1.4.8所示設定:
圖8.1.4.8 Utilities頁籤設定
圖中⑥處下載下傳算法STM32H750,是MDK預設添加的,針對STM32H750系列産品。除此之外,我們還要添加[email protected]算法,點選⑦處按鈕添加即可。添加好算法後,設定算法使用的 RAM 位址和大小,這裡設定的起始位址為:0X2000 0000(DTCM),大小為:0X3000。必須按這個大小設定,否則下載下傳會出錯(無法加載算法)。
7.添加分散加載檔案
由于STM32H750VBT6晶片内部的FLASH的空間比較少(隻有128KB)。對于大的工程,這個FLASH空間是不夠用的,為了解決這個問題,同時友善後續工程的建立,我們統一使用分散加載的方式來決定FLASH記憶體的配置設定,而不用MDK預設的設定。關于分散加載是什麼?我們後面8.3小節會講解,請大家先跟着我們把建立工程完成。
分散加載的檔案已經為大家準備好了,可以在實驗0-3,建立工程實驗-HAL庫版本\User\SCRIPT,或者在(A盤)/程式源碼/STM32啟動檔案/分散加載_HAL庫版本/SCRIPT中拷貝qspi_code.scf檔案到我們的工程User\SCRIPT路徑下,如圖8.1.4.9所示。
圖8.1.4.9 拷貝分散加載檔案到工程
注意:這裡的分散加載檔案寄存器跟HAL庫是不一樣的,我們建立HAL庫工程,是以必需用HAL庫版本的分散加載檔案。
接下來我們需要對MDK進行配置,相當于把分散加載檔案關聯到工程裡。方法:點選魔術棒
,inker頁籤à取消勾選:Use Memory Layout from Target DialogàScatter File路徑à選擇SCRIPT檔案夾à 選擇qspi_code.scf檔案,然後,在disable Warnings一欄,添加:6314,6329,屏蔽6314和6329這兩個警告。如不屏蔽,當分散加載裡面有某些段(section)沒用到,則會報警告,是以我們需要屏蔽這兩個警告。如圖8.1.4.10所示。
圖8.1.4.10 添加分散加載檔案
至此,添加分散加載檔案的相應操作就完成了。
8.1.5添加main.c,并編寫代碼
在MDK主界面,點選:
,建立一個main.c檔案,并儲存在User檔案夾下。然後輕按兩下User分組,彈出添加檔案的對話框,将User檔案夾下的main.c檔案添加到User分組下。得到如圖8.1.5.1所示的界面:
圖8.1.5.1在User分組下加入main.c檔案
至此,我們就可以開始編寫我們自己的代碼了。我們在main.c檔案裡面輸入如下代碼:
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
void led_init(void); /* LED初始化函數聲明 */
int main(void)
{
sys_cache_enable(); /* 打開L1-Cache */
HAL_Init(); /* 初始化HAL庫 */
sys_stm32_clock_init(240, 2, 2, 4); /* 設定時鐘, 480Mhz */
delay_init(480); /* 延時初始化 */
led_init(); /* LED初始化 */
while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET); /* PB4置1 */
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET); /* PE5置0 */
delay_ms(500);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); /* PE5置1 */
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_RESET); /* PE6置0 */
delay_ms(500);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_SET); /* PE6置1 */
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET); /* PB4置0 */
delay_ms(500);
}
}
/**
初始化LED相關IO口, 并使能時鐘
無
無
*/
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE(); /* PB4時鐘使能 */
__HAL_RCC_GPIOE_CLK_ENABLE(); /* PE6時鐘使能 */
__HAL_RCC_GPIOE_CLK_ENABLE(); /* PE5時鐘使能 */
gpio_init_struct.Pin = GPIO_PIN_4; /* LED0引腳 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽輸出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOB, &gpio_init_struct); /* 初始化LED0引腳 */
gpio_init_struct.Pin = GPIO_PIN_6; /* LED1引腳 */
HAL_GPIO_Init(GPIOE, &gpio_init_struct); /* 初始化LED1引腳 */
gpio_init_struct.Pin = GPIO_PIN_5; /* LED2引腳 */
HAL_GPIO_Init(GPIOE, &gpio_init_struct); /* 初始化LED2引腳 */
}
此部分代碼,在A盤à4,程式源碼à1,标準例程-HAL庫版本à實驗0 基礎入門實驗à實驗0-3,建立最工程實驗-HAL庫版本àUseràmain.c裡面有,大家可以自己輸入,也可以直接拷貝。強烈建議自己輸入,以加深對程式的了解和印象!!
注意,這裡的include就是使用的相對路徑,關于相對路徑,請參考前面C/C++頁籤設定章節進行學習。
編寫完main.c,我們點選:
(Rebuild)按鈕,編譯整個工程,編譯結果如下圖所示:
圖8.1.5.2 編譯結果
編譯結果可以看到1個錯誤,0個警告。這個錯誤說找不到main.h,因為我們也不需要用到main.h,輕按兩下這個錯誤會彈出下面的stm32h7xx_it.c檔案對應包含main.h的語句。我們隻需要把它删除,然後重新編譯,如圖8.1.5.3所示。
圖8.1.5.3 删除包含main.h的語句
編譯後發現又有一個警告,警告HAL_IncTick函數沒有聲明,如圖8.1.5.4所示。
圖8.1.5.4 編譯報警告
因為這個函數是在stm32h7xx_hal.c定義了,并且在stm32h7xx_hal.h聲明了。我們把stm32h7xx_hal.h包含進來即可。這裡還有一個原因是整個工程沒有包含stm32h7xx_hal.h的語句,我們需要用到它,是以在這裡把它包含進來。官方的main.h是有包含這個頭檔案的。我們不用main.h檔案,我們在stm32h7xx_it.c檔案剛才删除包含main.h的語句的位置,編寫包含stm32h7xx_hal.h語句,如圖8.1.5.5所示。
圖8.1.5.5 包含stm32h7xx_hal.h頭檔案到工程
再進行編譯就會發現0錯誤0警告,結果如圖8.1.5.6所示。
圖8.1.5.6 編譯結果
編譯結果提示:代碼總大小(Porgram Size)為:FLASH占用13520位元組(Code + RO + RW),SRAM占用2008位元組(RW + ZI);并成功建立了Hex檔案(可執行檔案,放在Output目錄下)。
總結:如果編譯提示有錯誤/警告,請根據提示,從第一個錯誤/警告開始解決,直到0錯誤0警告。如果出錯,很有可能是之前的操作存在問題,請對照教程找問題。
另外,我們在Readme分組下還沒有添加任何檔案,由于隻是添加一個說明性質的檔案(.txt),并不是工程必備檔案,是以這裡我們就不添加了,開發闆CD光牒的源碼我們是有添加的,大家可以去參考一下。
至此,建立HAL庫版本MDK工程完成。
8.2 下載下傳驗證
這裡我們繼續使用DAP仿真器下載下傳,在MDK主界面,點選:
(下載下傳按鈕,也可以按鍵盤快捷鍵:F8),就可以将代碼下載下傳到開發闆,如圖8.2.1所示:
圖8.2.1 下載下傳成功
上圖提示:Application running…,則表示代碼下載下傳成功,且開始運作。此時,看到RGB的紅、藍、綠三種燈輪流亮,類跑馬燈。如果有朋友沒能下載下傳成功,請看第四章尋找問題,或者直接對照我們提供的實驗0-3,建立工程實驗-HAL庫版本工程設定。
8.3 分散加載檔案簡介
ARM晶片用于在連結時指定存儲器配置設定方案的檔案,稱之為分散加載檔案(.sct),它可以将不同的代碼(.o檔案)放在不同的存儲空間。關于分散加載的詳細介紹,請參考:A盤à8,STM32參考資料à4,分散加載à分散加載檔案淺釋.pdf,這是周立功公司的一份文檔資料,詳細介紹了.sct檔案的基礎概念、文法及應用執行個體說明,對學習分散加載非常有幫助,請大家務必先學習一下這個文檔。
本節,我們僅對.sct檔案進行一個簡介,友善大家學習。
首先明确一個概念:MDK編譯任何STM32工程,都會需要用到分散加載檔案。分散加載檔案的來源有兩種方式:
- 通過MDK自己生成;
2,通過使用者指定(使用者自己編寫);
首先,我們來看MDK自己生成的.sct檔案。
選擇本章建立工程àMDK的魔術棒àLinker頁籤裡面,進行如圖8.3.1所示的設定:
圖8.3.1 勾選Use Memory Layout from Target Dialog選項
勾選Use Memory Layout from Target Dialog選項後,MDK就會根據Target頁籤裡面的相關設定來決定存儲器配置設定,如圖8.3.2所示:
圖8.2.2 MDK預設的存儲器配置設定
标号①,是MDK的隻讀存儲區域(ROM)和可讀寫存儲區域(RAM)的配置區域。
标号②處,說明預設将所有的代碼(Code + RO Data + RW Data)都存放到IROM1指定的位址範圍上。其起始位址為:0X0800 0000,大小為:0X20000。
标号③處,說明預設将所有的變量及堆棧(RW Data + ZI Data)都存放在IRAM1和IRAM2指定的位址範圍上。IRAM1的起始位址為:0X2000 0000,大小為:0X20000;IRAM2的起始位址為:0X2400 0000,大小為:0X80000;變量的具體位置由編譯器自動配置設定。
在完成以上兩步操作以後,對MDK進行一次編譯,在編譯成功後,MDK會自動生成一個以工程名命名的.sct檔案,存放在Output檔案夾裡面,如圖8.2.3所示:
圖8.2.3 MDK自動生成的分散加載檔案
打開上圖中的test.sct檔案,其内容如下:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00020000 { ; load region size_region
ER_IROM1 0x08000000 0x00020000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM2 0x24000000 0x00080000 {
.ANY (+RW +ZI)
}
}
LR_IROM1是一個加載域,起始位址為:0X0800 0000,大小為:0X0002 0000。它包含三個運作域分别是:
ER_IROM1運作域,起始位址為:0X0800 0000,大小為:0X0002 0000。
RW_IRAM1運作域,起始位址為:0X2000 0000,大小為:0X0002 0000。
RW_IRAM2運作域,起始位址為:0X2400 0000,大小為:0X0008 0000。
其中:
ER_IROM1為ROM區域,存放:Code + RO Data + RW Data等隻讀資料,由:.ANY (+RO)指定,即所有隻讀資料,都存放在這個區域。
RW_IRAM1和RW_IRAM2為RAM區域,存放:RW Data + ZI Data等可讀寫資料,由:.ANY (+RW +ZI)指定,即所有的可讀寫資料,都存放在這兩個區域,具體存放位置由MDK編譯器自動配置設定。
*.o (RESET, +First):表示優先(+FIRST)将RESET(即中斷向量表)段放這個域的起始位置,實際上就是把中斷向量表拷貝到最開始的位置。
* (InRoot$$Sections):表示将所有用到的庫段放到root區,如:__main.o、__scatter*.o和__dc*.o等。
以上,就是MDK自動生成的.sct檔案簡介。
接下來,我們看使用者指定.sct檔案的實作方式。選擇本章建立工程àMDK的魔術棒àLinker頁籤裡面,進行如圖8.3.4所示的設定:
圖8.2.4 取消Use Memory Layout from Target Dialog選項
标号①,取消Use Memory Layout from Target Dialog選項,使用使用者自定義分散加載檔案。
标号②,建立工程時,已選擇SCRIPT\qspi_code_scf.scf分散加載檔案。
标号③,點選Edit按鈕,即可在MDK裡面打開qspi_code_scf.scf分散加載檔案。
qspi_code.scf的内容如下:
#! armcc -E
;#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc
/* 使用說明
! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, 用于AC6編譯報錯(L6709E錯誤)時,請使用此設定
!armcc -E, 用于AC5編譯報錯(L6709E錯誤)時,請使用此設定
注意,設定必須放本檔案第一行!否則還是報錯!請注意調整順序!
*/
/**
**********************************************************************************************
* @file main.c
正點原子團隊(ALIENTEK)
* @version V1.0
* @date 2020-03-13
分散加載檔案(.scf檔案)
廣州市星翼電子科技有限公司
**********************************************************************************************
* @attention
*
**********************************************************************************************
*/
#define m_stmflash_start 0X08000000 /* m_stmflash(STM32内部FLASH)域起始位址 */
#define m_stmflash_size 0X20000 /* m_stmflash(STM32内部FLASH)大小,H750是128KB */
#define m_qspiflash_start 0X90000000 /* m_qspiflash(外擴QSPI FLASH)域起始位址 */
#define m_qspiflash_size 0X800000 /* m_qspiflash(外擴QSPI FLASH)大小,W25Q64是8MB */
#define m_stmsram_start 0X24000000 /* m_stmsram(STM32内部RAM)域起始位址,定義在D1,AXI SRAM */
#define m_stmsram_size 0X80000 /* m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB */
LR_m_stmflash m_stmflash_start m_stmflash_size /* LR_m_stmflash加載域 */
{
/* ER_m_stmfalsh運作域,起始位址為:m_stmflash_start,大小為:m_stmflash_size */
ER_m_stmflash m_stmflash_start m_stmflash_size {
/* 優先(+FIRST)将RESET(中斷向量表)段放這個域,實際上就是把中斷向量表拷貝到m_stmflash_start */
是一個段名,表示中斷向量表(在.s檔案定義);+FIRST表示時第一個要加載的. */
*.o (RESET, +First)
将所有的庫段(C/C++标準庫)放在root region.如__main.o,__scatter*.o等 */
* (InRoot$$Sections)
* (Veneer$$Code)
libinit.o
libinit2.o
libshutdown.o
libshutdown2.o
__rtentry.o
__rtentry2.o
__rtentry4.o
rtexit.o
rtexit2.o
use_no_semi_2.o
heapauxi.o
use_no_semi.o
sys_stackheap_outer.o
exit.o
libspace.o
fpinit.o
lludivv7m.o
startup_stm32h750xx.o
rt_locale_intlibspace.o
lc_numeric_c.o
lc_ctype_c.o
main.o
sys.o
usart.o
delay.o
pwr.o
的QSPI接口不支援讀時寫,是以必須把以下3個檔案放到内部FLASH,以保證可以
對QSPI FLASH的寫入 */
qspi.o
norflash.o
norflash_ex.o
針對HAL庫驅動添加到内部的檔案 */
system_stm32h7xx.o
stm32h7xx_hal.o
stm32h7xx_hal_cortex.o
stm32h7xx_hal_rcc.o
stm32h7xx_hal_rcc_ex.o
stm32h7xx_hal_gpio.o
stm32h7xx_hal_dma.o
stm32h7xx_hal_dma_ex.o
stm32h7xx_hal_qspi.o
stm32h7xx_hal_pwr.o
stm32h7xx_hal_pwr_ex.o
}
/* RW_m_stmsram運作域,起始位址為:m_stmsram_start,大小為:m_stmsram_size. */
RW_m_stmsram m_stmsram_start m_stmsram_size {
将所有用到的RAM都放在這個區域 */
}
}
LR_m_qspiflash m_qspiflash_start m_qspiflash_size /* LR_m_qspiflash加載域 */
{
/* ER_m_qspiflash加載域,起始位址為:m_qspiflash_start,大小為:m_qspiflash_size */
ER_m_qspiflash m_qspiflash_start m_qspiflash_size {
将隻讀資料(+RO)放這個域,任意配置設定.相當于程式就是存放在這個域的 */
}
}
相比于MDK自己生成的分散加載檔案,我們自己編寫的相對複雜一些,qspi_code.scf分散加載檔案包含2個加載域,3個運作域,分别是:
LR_m_stmflash加載域,起始位址為:m_stmflash_start(宏定義,實際值:0X0800 0000),大小為:m_stmflash_size(宏定義,實際值:0X20000)。它包含二個運作域分别是:
ER_m_stmflash運作域,起始位址為:m_stmflash_start(宏定義,實際值:0X0800 0000),大小為:m_stmflash_size(宏定義,實際值:0X20000)。
RW_m_stmsram運作域,起始位址為:m_stmsram_start(宏定義,實際值:0X2400 0000)大小為:m_stmsram_size(宏定義,實際值:0X80000)。
LR_m_qspiflash加載域,起始位址為:m_qspiflash_start(宏定義,實際值:0X9000 0000)大小為:m_qspiflash_size(宏定義,實際值:0X80000)。它包含一個運作域:
ER_m_qspiflash運作域,起始位址為:m_qspiflash_start(宏定義,實際值:0X9000 0000)大小為:m_qspiflash_size(宏定義,實際值:0X80000)。
具體的存儲器配置設定情況為:
ER_m_stmflash運作域,包含:*.o (RESET, +First)開始到delay.o結束的相關代碼,這些代碼運作在内部FLASH,可以得到最佳的性能。需要注意的是:這些代碼大部分都是必須放到内部FLASH,否則無法正常運作!!
ER_m_qspiflash運作域,所有沒有在ER_m_stmflash運作域指定的代碼,都被放在這個運作域,這些代碼運作在外部SPI FLASH,速度比内部FLASH慢一些。
RW_m_stmsram運作域,所有變量及堆棧(RW Data + ZI Data)都存放在這個運作域。
以上分散加載檔案,由正點原子編寫,為了友善大家使用,不用頻繁修改.sct檔案,特意将.ANY ROM區域放在外部SPI FLASH,這樣大家在新增.c參與編譯時,預設就是存放在外部SPI FLASH的,這樣使用起來就更友善。
注意事項:
1、如果你新增的代碼,對速度有要求,可以将其對應的.o添加到内部FLASH,即放在:ER_m_stmflash運作域。