你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

MiniPro STM32H750 开发指南_V1.1-新建HAL版本MDK工程

[复制链接]
STMCU小助手 发布时间:2022-10-5 21:06
新建HAL版本MDK工程: x( n! Q  {; \8 k' L
在前面的章节我们介绍了STM32H7xx官方固件包的一些知识,本章我们将讲解新建HAL库版本的MDK工程的详细步骤。我们把本章新建好的工程放在光盘里,路径:4,程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,大家在学习新建工程过程中间遇到一些问题,可以直接打开这个工程,然后对比学习。
9 U, O9 c9 e* ^! F5 T8 `
, L* c5 a2 l- K& Y6 O6 f8.1 新建HAL库版本MDK工程7 H% y& F) q; \1 u2 T# T7 H
本节我们将教大家如何新建一个STM32H750的HAL库版本MDK5工程。为了方便大家参考,我们将本节最终新建好的工程模板存放在A盘:4、程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,如遇新建工程问题,请打开该实验对比。" \- E" C4 M/ s. J$ e( l
整个新建过程比较复杂,我们将其拆分为5个步骤进行讲解,请准备大概2个小时时间,耐心细致的做完!对你后续的学习非常有帮助!
, E0 _0 w. {: n' Z" a在新建工程之前,首先我们要做如下准备:
! g* P7 b' l3 Q3 P' {% }; s- g1、 STM32Cube官方固件包:我们使用的固件包版本是STM32Cube_FW_H7_V1.6.0,固件包路径:A盘8,STM32参考资料1,STM32CubeH7固件包。4 m6 n3 b+ h* W% d
2、开发环境搭建:参考本书第三章相关内容。
' S1 C  ]5 W3 Y8 \5 U8.1.1 新建工程文件夹/ b3 t1 e0 l6 R5 N/ c
新建工程文件夹分为2个步骤:1,新建工程文件夹;2,拷贝工程相关文件。* f/ `( p* E' ~+ Z9 r3 Z
1.新建工程文件夹1 ~: Y$ a4 I7 S6 {& k
首先我们在桌面新建一个工程根目录文件夹,后续的工程文件都将在这个文件夹里建立,我们把这个文件夹重命名为:实验0-3,新建工程实验-HAL库版本。如图8.1.1.1所示:+ k: B% s. d( W
c07d881ff02749af9842e8d3614198f4.png $ q9 g/ F/ e" C% e
/ ]. P2 v: c& |2 u/ J; `+ j* _8 @
图8.1.1.1 新建工程根目录文件夹
' _" Q+ A  D) X  W- M! u! A为了让工程的文件目录结构更加清晰易懂,我们会在工程根目录文件夹下建立以下几个文件夹,每个文件夹名称及其作用如表8.1.1.1所示:
, t8 L* ^0 `) Q  q
2 Z0 g2 M2 v2 ^/ H* c$ I 09bd521756de4e4ca5b578aa5591d9b2.png 8 u' \0 m& m6 a% f  b9 V4 v
1 H/ a8 S% p- R# \
表8.1.1.1 工程根目录新建文件夹及其作用
. D# a3 z/ k: d: Y新建完成以后,最后得到我们的工程根目录文件夹如图8.1.1.2所示。, k- ?+ ?% K7 B7 M
, s# R* D8 D( z2 o! J- |8 v1 n5 I- t
d83446698d7e4a6181e1d40778b8742c.png 0 m! d, ^9 g/ g$ d
  D/ D' J  [  S
图8.1.1.2 工程根目录文件夹! \1 R# O3 ]% p0 e- B# a. Z! n' X' |
另外我们的工程根文件目录下还有一个名为keilkill.bat的可执行文件,双击便可执行。其作用是删除编译器编译后的无关文件,减少工程占用的内存,方便打包。还有一个名为readme的记事本文件,其作用是介绍本实验的各种信息。2 \; D& q; G0 O
工程根目录及其相关文件夹新建好以后,我们需要拷贝一些工程相关文件过来(主要是在Drivers文件夹里面),以便等下的新建工程需要。0 C. @% v8 l! r3 @" h9 n5 Y
2. 拷贝工程相关文件; N- D( V" ?, ]; i
接下来,我们按图8.1.1.2的根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。
% E- L8 C/ P9 ]5 z% F7 x7 b  KDrivers文件夹
; L- f4 w; I5 `5 c0 T7 A& f2 j该文件夹用于存放与硬件相关的驱动层文件,一般包括如表8.1.1.2所示的三个文件夹:
" |" f; w9 Q9 b6 k2 r' t
+ g& y8 x: R$ z  y9 P5 p* P0 N a28caaec3fcb44039e3f6b6858907daf.png
6 q5 |& {5 }* N* y; E1 r
  M  ]  b- y8 E3 B: c: y2 Q表8.1.1.2 Drivers包含文件夹
+ k& M8 R1 m; D7 U8 ^% }! MBSP文件夹,用于存放正点原子提供的板级支持包驱动代码,如:LED、蜂鸣器、按键等。本章我们暂时用不到该文件夹,不过可以先建好备用。
: a9 Z$ \* i1 k+ @: M4 rCMSIS文件夹,用于存放CMSIS底层代码(ARM和ST提供),如:启动文件(.s文件)、stm32h7xx.h等各种头文件。该文件夹我们可以直接从STM32CubeH7固件包(路径:A盘8,STM32参考资料1,STM32CubeH7固件包)里面拷贝,不过由于固件包里面的CMISIS兼容了太多芯片,导致非常大(300多MB),因此我们根据实际情况,对其进行了大幅精简,精简后的CMSIS文件夹大小为2.3MB左右。精简后的CMSIS文件夹大家可以在:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。
0 a- x) n, D4 p1 vSYSTEM文件夹,用于存放正点原子提供的系统级核心驱动代码,如:sys.c、delay.c和usart.c等,方便大家快速搭建自己的工程。该文件同样可以从:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。
" o( k& b7 L! |0 K' |STM32H7xx_HAL_Driver文件夹,用于存放ST提供的H7xx HAL库驱动代码。该文件夹我们可以直接从STM32CubeH7固件包里面拷贝。直接拷贝“STM32CubeH7固件包Drivers”路径下的“STM32H7xx_HAL_Driver”文件夹到我们工程的Drivers下,只保留Inc和Src文件夹即可。+ _4 A: h$ E2 c# }
执行完以上操作后,Drivers文件夹最终结构如图8.1.1.3所示:
, g; K2 z( h3 v, r4 ]6 v
4 z+ k5 w; z1 G8 @ 89aca4a79e13457088ecf50d8d500e3d.png " R6 q. i2 V$ }8 U. v% q1 u( @

/ D, k  f$ j( `4 f  {7 V图8.1.1.3 工程根目录下的Drivers文件夹
# q# P5 t$ H  d- E2 P* [9 ~# T关于工程根目录下的Drivers文件操作到这里就完成了,可以说步骤是有点多。在此过程遇到问题的话,请大家多参考我们提供的实验0-3,新建工程实验-HAL库版本工程,一步步操作。
% G1 C9 v6 |0 W* P% NMiddlewares文件夹6 A3 \! `3 f' _8 C' l
该文件夹用于存放正点原子和其他第三方提供的中间层代码(组件/Lib等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各种OS、各种GUI等等。本章我们暂时用不到该文件夹,不过可以先建好备用,后面的实验将会陆续添加各种文件。3 e8 f1 X# L2 `
Output文件夹. _  J: u5 P/ [" r$ i' b% l
该文件夹用于存放编译器编译工程输出的中间文件,比如:.hex、.bin、.o文件等等。这里不需要操作,后面只需要在MDK里面设置该文件夹为编译过程中间文件的存放文件夹就行。' S3 I% B' x" X. M$ Q
Projects文件夹
) K% A# v( M; m8 y2 U该文件夹用于存放编译器(MDK、IAR等)工程文件,我们主要用MDK,为了方便区分,我们在该文件夹下新建:MDK-ARM文件夹,用于存放MDK的工程文件,如图8.1.1.4所示:
! r6 T& i4 c6 n; H
" \3 O0 ~$ r" P: m6 ]- r6 ?+ _7 Q5 s 2278dc824fc0410f95513f3bb54003ca.png 1 k- ^( _4 j. B. B1 r" u

, X% b0 `4 r: T) Y: _图8.1.1.4 在Projects文件夹下新建MDK-ARM文件夹; D3 L0 `" w# d1 P0 n
User文件夹
3 h8 |* A% y( K5 Y6 q. jUser文件夹用于存放HAL库用户配置文件、main.c文件、中断处理文件,以及分散加载文件。
; S( l7 r' a6 Y" G8 _5 z我们首先从官方固件包里面直接拷贝官方的模板工程下的HAL库用户配置文件和中断处理文件到我们的User文件夹里。官方的模板工程路径:STM32Cube_FW_H7_V1.6.0\Projects\ STM32H750B-DK\Templates\Template_Project,打开Template_Project文件夹,如图8.1.1.8所示。
* j, w' q. ?( Q) m  U: p7 t
5 b4 h8 f- S, Z4 v3 {; M b80bce4e05ec4463bca87c2ea099755e.png
5 J& y3 d. T/ Y# ~+ I: E: u
; M7 {' u( c9 D# g0 D# l1 k0 ^4 u图8.1.1.8 官方模板工程根目录
( P. ]" x/ O5 G# e& ~我们需要的文件就在Inc和Src文件夹里面,在这两个文件夹里面找到:stm32h7xx_it.c、stm32h7xx_it.h、stm32h7xx_hal_conf.h这三个文件,并且拷贝到我们的User文件夹下。1 R: r  [1 G1 d. C  w2 j
最后在User文件夹下新建一个命名为SCRIPT的文件夹,用于存放分散加载文件。分散加载文件直接在我们的实验0-2,新建工程实验-HAL库版本工程对应位置拷贝过来,后面再给大家讲解。main.c文件我们也是放在User文件夹里面的,后面在MDK里面教大家新建.c文件并保存。
2 B0 g2 b# {  b! G/ S8.1.2 新建一个工程框架
! J) x% e; G( t) w, D5 i/ S首先,打开MDK软件。然后点击ProjectNew uVision Project如图8.1.2.1所示:/ f# z# t  v) @7 @% M" b

" t! @$ S6 {! t1 H4 p d7b3678492b147a4b3afd58c9fc1a07f.png
3 {$ _) U' w* n6 y' ~! W; K
0 b. B+ Q6 ?1 b; Z9 v5 ]图8.1.2.1 新建MDK工程
, m7 k# G1 d# Q* ]4 M然后弹出工程命名和保存的操作窗口,我们将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:桌面实验0-2,新建工程实验-寄存器版本ProjectsMDK-ARM,工程名字我们取:atk_h750,最后点击保存即可。具体操作窗口如图8.1.2.2所示:
" t2 A* ^/ o+ z# r7 _! \
- p& C& n: g$ F% v4 N 6b119a5b802c457692bf3e22588dd8bc.png
- b$ h2 @2 z9 U  j9 J8 N" x# h9 t$ L
图8.1.2.2 保存工程界面
; W$ X. o& c7 |/ [之后,弹出器件选择对话框,如图8.1.2.3所示。因为MiniPRO STM32H750开发板所使用的STM32型号为STM32H750VBT6,所以我们选择:STMicroelectronicsSTM32H7 SeriesSTM32H750STM32H750VBTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件pack才会显示这些内容哦!!如果没得选择,请关闭MDK,然后安装 A盘:6,软件资料\1,软件\MDK5\ Keil.STM32H7xx_DFP.2.5.0.pack这个安装包后重试)。
: `1 q: A# r+ F( J1 U/ u7 f3 v$ w! N8 X
c25315eb813e47abacc2163ff53014bb.png 6 P$ ~- X$ Y7 C. P5 P* k$ j" u
% D- i4 I! q' c
                       图8.1.2.3 器件选择界面) Q4 G- t- d1 y6 Q/ \6 @1 b# \

4 S& V+ `0 W0 w3 Y& C7 J  Y点击OK,MDK会弹出Manage Run-Time Environment对话框,如图8.1.2.4所示:2 E$ u5 A- L* L6 K+ k7 z
- U7 k7 M: I3 z% t5 ]
840c8d7777c748d4ba3c8480058b0bc9.png & f& L. Q5 @3 O

8 j; M  G6 @2 G5 l! u图8.2.1.4 Manage Run-Time Environment界面
+ T0 |- h2 Q8 K3 g* l! h- `这是MDK5新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。所以在图8.1.2.4所示界面,我们直接点击Cancel,即可,得到如图8.1.2.5所示界面:9 o9 u6 [" f4 C5 ?+ M

. T- z" {- R; o1 r' [* V! |4 m 01c6e443fd774a919f4afe3eff0d363e.png
& ^; a; a, ^. t  ?! s- F; a  N0 Q1 w3 A* A8 K3 n+ y# i
图8.1.2.5 工程初步建立# _5 ]. p; _3 {: j
此时,我们打开MDK-ARM文件夹,会看到MDK在该文件夹下自动创建了3个文件夹(DebugConfig、Listings和Objects),如图8.1.2.6所示:
+ {9 z! n0 r- H' S" p& n8 h2 ]9 x
4 b8 b+ Q3 [0 T+ ~8 G' E cf22cd3c423d441398f1363d26b3d632.png
9 u( P. y" X/ C: b: g. E! S1 k  e6 N1 C" s: \& D
图8.1.2.6 MDK新建工程时自动创建的文件夹4 @4 I0 y: G5 e# M5 |3 K
这三个文件夹的作用如表8.1.2.1所示:
3 q8 s. C: f. F6 {/ ^$ w% S. u8 L  H! N5 F# R( w( \
5021687c5d93429da588e41d6ac541f5.png   Y5 z) g3 e3 `& t3 i
. F, r" p' }6 p+ G, t  U4 K* ^
表8.1.2.1 三个文件夹及其作用
9 j' ?( S3 V9 C2 v, c编译过程产生的链接列表、调试信息、预览、lib等文件,统称为中间文件。为了统一管理,方便使用,我们会把输出在Listings和Objects文件夹的内容,统一改为输出到Output文件夹(通过魔术棒设置),我们先把MDK自动生成的这两个文件夹(Listings和Objects)删除。
  L: h5 }% H$ m- k4 y% R至此,我们还只是建了一个框架,还有好几个步骤要做,比如添加文件、魔术棒设置、编写main.c等。
3 @" W, o1 e1 ^7 ^/ u1 D8.1.3 添加文件
* }, s, m+ b# I6 T5 L9 _5 A本节将分5个步骤:1,设置工程名和分组名;2,添加启动文件;3,添加SYSTEM源码4,添加 User 源码;5,添加 STM32H7xx_HAL_Driver 源码。: x4 ~* |) N6 H
1.设置工程名和分组名
. w! h4 N- _. e! j7 i" {) N. |0 N在ProjectTarget上右键,选择Manage Project Items…(方法一)或在菜单栏点击品字形红绿白图标(方法二)进入工程管理界面,如图8.1.3.1所示:; I; k* E/ ]# K
' H4 a+ ?8 j3 [" N0 F# h
43f3eccd0c0a49038e00b8140982a73a.png
9 h% \7 v) z' ]4 f: a# J  ?
; I( K2 z* i. x图8.1.3.1 进入工程管理界面8 Z5 v5 J' X, x
在工程管理界面,我们可以执行设置工程名字(Project Targets)、分组名字(Groups)以及添加每个分组的文件(Files)等操作。我们设置工程名字为:Template,并设置四个分组:Startup(存放启动文件)、User(存放main.c等用户代码)、Drivers/SYSTEM(存放系统级驱动代码)、Readme(存放工程说明文件),如图8.1.3.2所示:9 t9 e; K, ^8 M: i  `0 l

, p. e4 ?5 o1 l* u0 O5 j  P fd29f722719047778a894ec11dc15ac3.png
2 B/ b: ?) ^' S+ }7 r
8 m0 `. [" K$ W8 E图8.1.3.2 设置工程名和分组名3 e' @" w; P. s- p
设置好之后,我们点击OK,回到MDK主界面,可以看到我们设置的工程名和分组名如图8.1.3.3所示:
! h$ Y7 y+ I0 f, G( S4 q' U1 e# g3 ?- K4 Y9 ?5 n8 P
9575821896784b26965cf3d769e26165.png
! j) p! Q3 L* H7 c( c
0 ?  A* b6 [# ~2 n+ x/ n4 @- O图8.1.3.3 设置成功5 D6 `: @' m0 P3 z  @# E# f
这里我们只是新建了一个简单的工程,并没有添加BSP、Middlewares等分组,后面随着工程复杂程度的增加,我们需要一步步添加对应的分组。4 H/ y6 w# ~5 P. ^5 [
注意:为了让工程结构清晰,我们会尽量让MDK的工程分组和我们前面新建的工程文件夹对应起来,由于MDK分组不支持多级目录,因此我们将路径也带入分组命名里面,以便区分。如:User分组对应User文件夹里面的源码,Drivers/SYSTEM分组,对应Drivers/SYSTEM文件夹里面的源码,Drivers/BSP分组对应Drivers/BSP文件夹里面的源码等。
3 o. Z% b: C# c1 J5 }) |8 \2. 添加启动文件' p$ Y5 v# N5 c. V* d, ^
启动文件(.s文件)包含STM32的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用main函数等,是每个工程必不可少的一个文件,我们在本书第九章会有详细介绍。/ I3 h/ J/ H( B. ~% d1 Y. L: Y
启动文件由ST官方提供,存放在STM32CubeH7软件包的:DriversCMSISDevice STSTM32H7xxSourceTemplatesarm文件夹下。因为我们开发板使用的是STM32H750VBT6,对应的启动文件为:startup_stm32h750xx.s,为了节省空间,在精简版CMSIS文件夹里面我们把其他启动文件都删了。
# ^3 `1 b. h3 c  }- S+ [' G" g关于启动文件的说明,我们就介绍这么多,接下来我们看如何添加启动文件到工程里面。我们有两种方法给MDK的分组添加文件:1,双击Project下的分组名添加。2,进入工程管理界面添加。
2 D8 C5 I/ \2 b6 k: C" z7 F这了我们使用方法1添加(路径:实验0-3,新建工程实验-HAL库版本\Drivers\CMSIS1 ^! ~. `; k4 i6 t: O- B
Device\ST\STM32H7xx\Source\Templates\arm),如图8.1.3.4所示:% [- h6 w0 k3 ]& \% Y. R" H
, g+ \% V/ U" R/ g( ]
faa546005287411f83b7561ea1f6003a.png ) f2 \( a: z1 t
0 t- F4 Q8 r0 M1 T
图8.1.3.4 双击分组添加启动文件(startup_stm32h750xx.s)9 O- d7 ]. U# [3 E+ w
上图中,我们也可以点击Add按钮进行文件添加。添加完后,点击Close,完成启动文件9 {" I) ^4 ~* o5 x9 l. m
添加,得到工程分组如图8.1.3.5所示:
+ `- @8 f9 o7 |. p3 ]* ^9 v$ o' H8 V# ]; B- z/ @1 s
8647718b231747e6a03964b009c2efa6.png
# U3 m. v' h7 p' c
/ Q& o9 T8 s- d" B, I图8.1.3.5 启动文件添加成功9 m) e# Z- B9 V, g" @! J. J
3. 添加SYSTEM源码1 [3 N) h# L1 g4 c- N  ~
这里我们在工程管理界面(方法2)进行SYSTEM源码添加。点击:按钮,进入工程管理界面,选中Drivers/SYSTEM分组,然后点击:Add Files,进入文件添加对话框,依次添加delay.c、sys.c和usart.c到该分组下,如图8.1.3.6所示:
# o6 S6 E8 `3 d( D( B% j: L1 V, E0 n* [. c7 Q) U
884e3508a6444b5cb08f234ae2a0499b.png % H# }" X; O$ W; o0 s

# K% @0 s. O: F图8.1.3.6 添加SYSTEM源码
4 l$ G+ z) J' s* R注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.7所示:
% l' R! W" y8 h  r$ u
1 v! M8 d! d) B/ r5 s- i a9bc3490a642456e8e6c7062e85dceaf.png
8 D% B( f, k' c; m+ G
5 s8 E+ I; G. ~2 x/ E$ x0 y, E图8.1.3.7 SYSTEM源码添加完成8 T8 f5 u" _* N, \# m$ R. m" [. `
4. 添加User源码. R1 e3 Q3 e* Q% ~- j' t0 C
这里我们在工程管理界面(方法2)进行User源码添加。点击:按钮,进入工程管理界面,选中User分组,然后点击:Add Files,进入文件添加对话框,依次添加stm32h7xx_it.c和system_stm32h7xx.c到该分组下,如图8.1.3.8所示:
8 M0 e$ c. k  @4 k- @! c, m) ]9 v# ~) q0 f) e" L
83627a883d394387b9a69b3f97ca5d0d.png
: a4 V1 c8 w7 @6 L4 [' h: B6 f" N( L8 ~  B' O
图8.1.3.8 添加User源码4 Q" o4 n6 }- B9 K) S& _5 Y% O
注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.9所示:. T2 Q" a: y! y# G- {- ?# i6 I: _
# x/ p# N9 }( \7 z. F
c7f798b6bbac4d329ef22a34f0da7a35.png
; Y! Y' G& X0 a- V/ ~7 f* u5 p% L. _3 P0 k6 a
图8.1.3.9 User源码添加完成: m5 }3 k6 j2 J, Q5 a" l
5. 添加STM32H7xx_HAL_Driver源码) b+ a- \4 }; b* [6 B1 a( U
接下来我们往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所示:
% O! T, _3 m. y6 L7 l4 y. I! ^$ A9 j8 A. y# C/ z9 V1 z& @( u
5f1b4fe0439b4dfea7d7dc9ed6ceb634.png
/ \  x$ ^8 z$ k2 H
, r8 i9 I- N3 m/ l3 e7 q图8.1.3.10 添加STM32H7xx_HAL_Driver源码
) K, ^' Z/ _' q2 N$ V添加完成后,如图8.1.3.11所示:9 c$ p# Y" N7 P3 f. a4 [- l8 e

# [' Q8 \4 X. @. ?3 g( k6 h, f bcb17b5b492a4a80a630f98abbad546e.png 6 n4 M* g, A2 f7 b& Q

, Z0 |9 Z, q) j0 Q/ Y& l2 d图8.1.3.11 STM32H7xx_HAL_Driver源码添加完成: c2 E$ W% _5 C# J7 Q0 J% M9 W
可以看到分组中有些.c文件有个小钥匙的符号,这是因为官方的固件包的文件设置了只读权限,我们取消只读权限就好了,方法如图8.1.2.12所示。5 P8 H; s$ J! K1 E

4 [8 \0 U7 [3 N! N- t0 t( a0 ~  X cd4fff494965426d861a066a1ab4e118.png
% K! v: I3 G# j4 y
9 ]& o9 ]! U4 \2 B& |图8.1.2.12 取消工程文件夹的只读权限+ |: {6 L/ E( L
8.1.4 魔术棒设置: K0 |9 b! @# w2 }$ }; |) j
为避免编写代码和编译报错,我们需要通过魔术棒对MDK工程进行相关设置。在MDK主界面,点击:(魔术棒图标,即Options for Target按钮),进入工程设置对话框,我们将进行如下几个选项卡的设置。
# n* R* R  x) r6 Q8 ]- ^1.设置Target选项卡
- A6 \# y2 s2 e( C% k在魔术棒Target选项卡里面,我们进行如图8.1.4.1所示设置:' {# P( d, c, h. {) `, n: N& k
" B+ X( @- \( }: u  B) b
aa079a0**74261b7926c03ee69b908.png 5 U, E4 a/ b$ [6 _/ @1 W
: e2 o: Y. J2 @
图8.1.4.1 Target选项卡设置1 m0 o0 @# B: S8 B1 a/ x  w( h$ P
上图中,我们设置芯片所使用的外部晶振频率为8Mhz,选择ARM Compiler版本为:Use default compiler version 5(即AC5编译器)。+ B# {! @% ?# b
这里我们说明一下AC5和AC6编译的差异,如表8.1.4.2所示:8 M8 g1 X6 l9 g! ^

' d: F7 e9 {2 }# [9 k4 m: |/ M ec16655ac2564428a4614ce6ade99f64.png
3 Y2 e, \6 `& A7 P- Y; k8 `  C+ V2 Y
9 ~& k% P& ]$ M& d0 c# X表8.1.4.1 AC5&AC6简单对比$ S! L" V8 H/ o8 f! e
由于AC5对中文支持比较好,且兼容性相对好一点,为了避免不必要的麻烦,我们推荐大家使用AC5编译器。为了让大家自由选择,我们正点原子的源码,也是支持AC6编译器的,不过在选项卡设置上稍有差异,具体差异如表8.1.4.2所示:
+ \5 W2 e! m5 j
( ]+ g$ }8 w; | ac1ed12294bf4652b48a2b8b2cfdd5ba.png $ G8 z. o' [& l& \' V  ?% V
, M1 g) d- ], n7 J
表8.1.4.2 AC5&AC6设置差异$ k4 V0 L, A6 U/ f1 r- B  d# Q, k5 b
2. 设置Output选项卡
. Q; g% D! z: D: ?5 ?" ~! ]- a在魔术棒Output选项卡里面,进行如图8.1.4.2所示设置:+ V, F( [/ j8 P4 T' I% S
9 \. D' P, U; J- Y3 x: H
eb9687db7b964a33bad72c5485588208.png / v* Z) E( g4 Y* d! t
9 x: w. C& h: }: h) f, g: }( Q: g
图8.1.4.2 设置Output选项卡
1 B# {! n% [. }1 [$ B! V5 F注意,我们勾选:Browse Information,用于输出浏览信息,这样就可以使用go to definition查看函数/变量的定义,对我们后续调试代码比较有帮助,如果不需要调试代码,则可以去掉这个勾选,以提高编译速度。
& p  H: Y  t& @+ P! b+ K/ D' w3. 设置Listing选项卡) }6 O6 Z0 D" i- P. ]9 D4 a
在魔术棒Listing选项卡里面,进行如图8.1.4.3所示设置:+ a& v8 {! ?0 C' G8 ?

. z: E  `" v2 G4 f1 `9 j c997f30d6c3d4504a4ec90f2c42edb29.png $ i: |* |, A5 X0 [% c" t; k. k: Q
. u- c  U) `) b7 b* {* W
图8.1.4.3 设置Listing选项卡
" i( ]4 j, G/ b& ?3 d经过Output和Listing这两步设置,原来存储在Objects和Listings文件夹的内容(中间文件)就都改为输出到Output文件夹了。( D: y6 R0 q4 `' E  y
4. 设置C/C++选项卡& U) d0 [* E0 I% H* H8 [
在魔术棒C/C++选项卡里面,进行如图8.1.4.4所示设置:
7 X7 a" k' E! j+ ^$ ^: f6 ]/ r% D
" w# m* A  A# A1 _  M3 m ff4c06ba4f6742998cfa29c00c5ae22f.png
8 e$ t3 P) E- \, ^5 d/ k/ A
! r: Z# ~( F3 O6 H! n" U* G图8.1.4.4 设置C/C++选项卡" v' U$ o" D4 r7 i; g
在②处设置了全局宏定义:STM32H750xx,用于定义所用STM32型号,在stm32h7xx.h里面会用到该宏定义。( l! l/ Q' ]7 L4 s' a0 j  Y+ G
在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6编译器的时候,这里推荐默认使用-O1优化。9 l3 t* p+ M2 X* i
在④处勾选C99模式,即使用C99 C语言标准。
8 c8 l) g) M0 W* Z7 d, h在⑤处,我们可以进行头文件包含路径设置,点击此按钮,进行如图8.1.4.5所示设置:" ]) e. _% k3 M7 i/ P% E, }
. P9 |: z! Z! t  A$ c  i
042d59760099402eb057ab905f45cf9e.png
' o* x9 M) d/ h' w! w$ X) c# L; y) i0 i% Q1 D- E
图8.1.4.5 设置头文件包含路径
6 j( f; g0 O$ H上图中我们设置了4个头文件包含路径,其中3个在Drivers文件夹下,一个在User文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的include全部使用相对路径,也就是我们只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码,如果全部是使用相对路径,则无需再设置头文件包含路径了,直接在include里面就指明了头文件所在。9 W8 \/ @. h" t6 L9 y
关于相对路径,这里大家记住3点:
& N7 F, D- ~) H0 q3 J1,默认路径就是指MDK工程所在的路径,即.uvprojx文件所在路径(文件夹)+ e* _$ _/ C: N" u1 v3 K( C$ o; O
2,“./”表示当前目录(相对当前路径,也可以写做“.\”)
  S1 S, q" R0 {8 V, O3,“…/”表示当前目录的上一层目录(也可以写做“…\”)
9 q, e' L$ |5 C+ g# _  h# q& I; |6 d举例来说,上图中:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,前面两个“…\”,表示Drivers文件夹在当前MDK工程所在文件夹(MDK-ARM)的上2级目录下,具体解释如图8.1.4.6所示:
/ `8 ?' P' f; m! E5 f% Z5 M
  ]" H$ Q: s6 [ 9958cf50da964d39ac15e517a4bdc8dc.png . _2 ?# y# R6 S# T
8 Q. T$ r, ]4 g8 p% l
图8.1.4.6 …\Drivers\CMSIS\Device\ST\STM3H7xx\Include的解释
0 d! i( L/ C3 V; j上图表示根据头文件包含路径:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,编译器可以找到⑥处所包含的这些头文件,即代码里面可以直接include这些头文件使用。
4 i0 E: I$ z7 Z, V) N6 U5 J8 H再举个例子,在完成如图6.1.4.5所示的头文件包含路径设置以后,我们在代码里面编写:* A: [2 w; V& |
#include “./SYSTEM/sys/sys.h”
5 O  _6 f  c- y9 y2 P1 V即表示当前头文件包含路径所指示的4个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h的路径,实际上就是在Drivers文件夹下面,两者结合起来就相当于:
4 ^2 D2 c0 g5 H% l% Q2 ]#include “…/…/Drivers/SYSTEM/sys/sys.h”2 x) ?) n' P8 K! B
这就是相对路径。它既可以减少头文件包含路径设置(即减少MDK配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在那个文件夹,因此我们推荐在编写代码的时候使用相对路径。* M% t! ^4 @; l9 T4 @
关于相对路径,我们就介绍这么多,大家搞不明白的可以在网上搜索相关资料学习,也可以在后面的学习,分析我们其他源码,慢慢体会,总之不难,但是好用。: x' ^# Z/ K9 Y# n; N' h
最后,我们如果使用AC6编译器,则在图6.1.4.4的Misc Controls处需要设置:-Wno-invalid-source-encoding,避免中文编码报错,如果使用AC5编译器,则不需要该设置!!
3 A5 X0 J1 U1 j" c5 x  t/ B5 g3 l5. 设置Debug选项卡6 E' w: _9 I! Z  t. s
在魔术棒Debug选项卡里面,进行如图8.1.4.7所示设置:) z$ s9 {* p# A5 M2 m

, ~& n# h5 u1 e% ]  g' Q 663ae5fcb1cb4a64abe8ee0f5360fdeb.png & H; |  ?0 l, i2 m9 |: \
: c0 y8 k+ `6 E% j1 q
图8.1.4.7 Debug选项卡设置8 K7 K  u; u- l7 T/ a
图中,我们选择使用:CMSIS-DAP仿真器,使用SW模式,并设置最大时钟频率为10Mhz,以得到最高下载速度。当我们将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在SW Device窗口显示芯片的IDCODE、Device Name等信息(图中⑤处),当无法找到时,请检查供电和仿真器连接状况。; d$ n2 }# v+ K$ q* O  H. L
6. 设置Utilities选项卡# n3 j6 \0 w5 }0 I. ~- y
在魔术棒Debug选项卡里面,进行如图8.1.4.8所示设置:
+ Y' F; {7 a; \$ S3 P4 _
7 i1 i# a! J' p3 C1 K* l 6cb0e659dc6846e5998d10fb61975fc7.png % H4 N7 R7 e- I7 V$ ?* d

: v+ y7 _/ {( S1 Q# ^/ a6 F9 r图8.1.4.8 Utilities选项卡设置
" V& C# d0 P4 ~  o* w图中⑥处下载算法STM32H750,是MDK默认添加的,针对STM32H750系列产品。除此之外,我们还要添加算法,点击⑦处按钮添加即可。添加好算法后,设置算法使用的 RAM 地址和大小,这里设置的起始地址为:0X2000 0000(DTCM),大小为:0X3000。必须按这个大小设置,否则下载会出错(无法加载算法)。
/ Q: B7 ]. |: T2 G6 d( N# C' q  W7. 添加分散加载文件2 M! d6 N6 P' l7 x9 h* V
由于STM32H750VBT6芯片内部的FLASH的空间比较少(只有128KB)。对于大的工程,这个FLASH空间是不够用的,为了解决这个问题,同时方便后续工程的新建,我们统一使用分散加载的方式来决定FLASH内存的分配,而不用MDK默认的设置。关于分散加载是什么?我们后面8.3小节会讲解,请大家先跟着我们把新建工程完成。
0 ?- w' V9 a  N+ i6 W6 a分散加载的文件已经为大家准备好了,可以在实验0-3,新建工程实验-HAL库版本\User\SCRIPT,或者在(A盘)/程序源码/STM32启动文件/分散加载_HAL库版本/SCRIPT中拷贝qspi_code.scf文件到我们的工程User\SCRIPT路径下,如图8.1.4.9所示。0 s/ U  M3 x  ?

6 Q& U) Q' Y. t5 {7 c 0362a607c804410ea055be3879718082.png
* l/ F4 s: o7 K6 d! T  n9 Y& R% G9 M+ S, i
图8.1.4.9 拷贝分散加载文件到工程
/ v& \, u4 B: g1 G* h, s% J注意:这里的分散加载文件寄存器跟HAL库是不一样的,我们建立HAL库工程,所以必需用HAL库版本的分散加载文件。6 V9 l. ?6 s- Y5 P1 W; m* P
接下来我们需要对MDK进行配置,相当于把分散加载文件关联到工程里。方法:点击魔术棒, Linker选项卡取消勾选:Use Memory Layout from Target DialogScatter File路径选择SCRIPT文件夹 选择qspi_code.scf文件,然后,在disable Warnings一栏,添加:6314,6329,屏蔽6314和6329这两个警告。如不屏蔽,当分散加载里面有某些段(section)没用到,则会报警告,所以我们需要屏蔽这两个警告。如图8.1.4.10所示。
8 F! X& ~- h  a5 \; v( a7 U; ~0 D9 i
241659f1cccf4ec799607dbe4bcc40ed.png $ Y: k0 _, Y* F2 S9 U: V* W
# x' C( F* a4 C- _& b
图8.1.4.10 添加分散加载文件+ L0 W+ ]( M6 E  i$ J. b
至此,添加分散加载文件的相应操作就完成了。5 I& Z$ L; ^2 ?  ]! p/ N& i
8.1.5 添加main.c,并编写代码8 L  X6 i8 N# E) g4 J, K
在MDK主界面,点击:,新建一个main.c文件,并保存在User文件夹下。然后双击User分组,弹出添加文件的对话框,将User文件夹下的main.c文件添加到User分组下。得到如图8.1.5.1所示的界面:  _2 _) K: [* D$ j. Z$ w

' e) l1 G0 F9 V8 j! S b1dd0efba9964663a212af58f8129af6.png $ a5 T7 M; e/ I- ~" t! A

7 O* Q8 h" U9 K+ G  p8 b图8.1.5.1 在User分组下加入main.c文件0 x3 o0 d. U" A" V  N- R) r
至此,我们就可以开始编写我们自己的代码了。我们在main.c文件里面输入如下代码:
8 W, E/ J" w- j3 _6 n4 `8 Q  t
! E  ?+ A* E5 f+ e. ^9 X2 }, Y
  1. #include "./SYSTEM/sys/sys.h"
    8 D4 L# ^" J) B3 L# C. k
  2. #include "./SYSTEM/usart/usart.h"
    8 i( |* v/ P& l- D: c
  3. #include "./SYSTEM/delay/delay.h"
    . x0 u; N+ C8 @5 g7 _

  4. ) b( d8 A7 ?5 r' X* ?
  5. void led_init(void);                               /* LED初始化函数声明 */; t/ }7 B& n$ h7 Y& U" ?. V8 n

  6. , O% C2 |/ w$ k( p: m7 ?/ R: C
  7. int main(void)& o' a5 O# P6 r' l% a5 p2 t9 c: c
  8. {
    2 T7 C8 F8 R! I- Z* r# k) j# U
  9.     sys_cache_enable();                            /* 打开L1-Cache */
    * W; n7 B; V4 h4 s
  10.     HAL_Init();                                             /* 初始化HAL库 */  X1 J/ k7 y7 `& j; ~4 G% U
  11.     sys_stm32_clock_init(240, 2, 2, 4);                /* 设置时钟, 480Mhz */* M7 ?/ b/ v9 e- s
  12.     delay_init(480);                                       /* 延时初始化 */
    0 J1 Q3 P% f2 t' R7 O
  13.     led_init();                                             /* LED初始化 */& c$ m/ k6 V9 P
  14.     while(1)7 A+ v/ ?- K/ l) S
  15.     {
    ) E1 K  n5 P) p- f/ N, J! w
  16.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);            /* PB4置1 */ 3 o. T. V! k) I
  17.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);         /* PE5置0 */
    9 B: L# B( Y$ e- v, F! r) w& Q9 l  V
  18.         delay_ms(500);/ Z  @9 u9 A% h) j8 \: n
  19.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);            /* PE5置1 */: d9 [. l4 |' a/ m7 h
  20.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_RESET);         /* PE6置0 */
    2 F, W  {, d6 s% _. J5 z; K" G4 ~
  21.         delay_ms(500); 7 {& m2 T: s$ A- X1 D, x. M/ W
  22.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_SET);            /* PE6置1 */
    ! ~" r* `: g9 t, o' k6 O! F
  23.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);         /* PB4置0 */
    " q$ N( X* R1 i) ?+ q
  24.         delay_ms(500);
    3 A  ~' L, p; Y' s/ ]7 _( o4 x7 g
  25.     }2 l% y/ F# l1 A8 U  o: y! F7 I
  26. }
    4 J2 \6 Q; ^7 z$ j

  27. % {5 B) C' p1 B& r- V' f1 _
  28. /**" d) j% ~  w. h, v, [
  29. * @brief       初始化LED相关IO口, 并使能时钟
    ' u$ J! x* u  k
  30. * @param       无
    & y- D) f" }3 @5 b! r
  31. * @retval      无
    " N8 Z7 G0 L  {9 S" N+ B
  32. */
    7 o/ V, n$ y4 `5 |. |9 H" h
  33. void led_init(void)! [! S& D% g( F# T; H
  34. {
    - i* g9 i. E! U, y4 h
  35.     GPIO_InitTypeDef gpio_init_struct;
    ) l8 a4 O& t5 R
  36.     __HAL_RCC_GPIOB_CLK_ENABLE(); /* PB4时钟使能 */
    - @. d' r, U. }/ {1 F
  37.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE6时钟使能 */
    ( n- \. ?( ]- Z1 D$ ]
  38.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE5时钟使能 */) {/ J- X/ m2 q2 X% d4 u6 X" O+ u5 c

  39. ! g6 W5 m2 y' D, V& |
  40.     gpio_init_struct.Pin = GPIO_PIN_4;                                /* LED0引脚 */
    , s. r% p; W3 Q0 `$ F9 t6 S7 U
  41.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */
    * K7 H7 q0 F% r* a+ `
  42.     gpio_init_struct.Pull = GPIO_PULLUP;                              /* 上拉 */8 N- o& B( |1 [! B' E; \
  43.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;            /* 高速 */' \$ B5 Y" t. K4 t% Q: H% O6 y
  44.     HAL_GPIO_Init(GPIOB, &gpio_init_struct);                         /* 初始化LED0引脚 */
    8 R6 G6 }, D: y1 N

  45. / Z; x9 {2 \- C9 y( d1 h- p. a
  46.     gpio_init_struct.Pin = GPIO_PIN_6;                                /* LED1引脚 */1 z) c8 Z9 e+ i0 {8 D
  47.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED1引脚 */
    , K5 z5 T# }1 {
  48. $ I( [# m% d) [9 b# I" B
  49.     gpio_init_struct.Pin = GPIO_PIN_5;                                /* LED2引脚 */
    ) U* }3 y. H4 Z% I* b
  50.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED2引脚 */
    3 P$ }1 f; d9 m
  51. }
复制代码
" Q  z' T, Y6 [- M/ n8 w( H( v
此部分代码,在A盘4,程序源码1,标准例程-HAL库版本 实验0 基础入门实验实验0-3,新建最工程实验-HAL库版本Usermain.c里面有,大家可以自己输入,也可以直接拷贝。强烈建议自己输入,以加深对程序的理解和印象!!
; W# ~! a: \$ N注意,这里的include就是使用的相对路径,关于相对路径,请参考前面C/C++选项卡设置章节进行学习。
  L+ k# E+ I8 B; n. ~" P) ?* L编写完main.c,我们点击:(Rebuild)按钮,编译整个工程,编译结果如下图所示:: ~8 G# M$ x% ?1 ^9 O8 O

+ u& M/ d) }$ ]' m0 z7 H( L图8.1.5.2 编译结果
, {1 E# U0 Q: N7 ^编译结果可以看到1个错误,0个警告。这个错误说找不到main.h,因为我们也不需要用到main.h,双击这个错误会弹出下面的stm32h7xx_it.c文件对应包含main.h的语句。我们只需要把它删除,然后重新编译,如图8.1.5.3所示。) C5 G& ^9 R/ v1 L. K' V: D

8 D. k0 ]; K4 H 68deb7ad5549455cad3065d60e1f0432.png ' l2 D* y1 z+ E+ \. L7 F+ W! l$ f

, s* F$ s0 q0 i图8.1.5.3 删除包含main.h的语句8 S% E/ R/ T3 o  a3 _
编译后发现又有一个警告,警告HAL_IncTick函数没有声明,如图8.1.5.4所示。
! U. K  c* t$ F7 j/ U8 ?1 C' w$ f) i* O4 Q* f9 m9 h' }
84622d45998148969e1d60df31ad2d5a.png " G4 Y7 }* ]& T1 P3 r( h
5 C. M- |+ E! ?" K. G6 P$ ~& H
图8.1.5.4 编译报警告' m' j# R' B1 L' g7 V, h; S
因为这个函数是在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所示。
5 S% V+ G, L* @* q  H/ R  C: I1 S1 d# }
844b5d27125c4d708677dc0b95b9a39d.png
: U4 s* R+ t: U5 S6 o
( d" Q2 o$ [% k3 ^3 Z: C, u图8.1.5.5 包含stm32h7xx_hal.h头文件到工程0 p% l/ k5 L  F/ @8 t# `: E
再进行编译就会发现0错误0警告,结果如图8.1.5.6所示。
8 T7 \' w% U" S$ z6 v/ {5 D
9 u' D: p- o, ~2 v$ Z& B7 g* k 0f3099f2b6e64aa286997c8756220211.png
: D/ {* y3 `$ @2 u. L1 S5 Z% b; j0 H4 g4 O/ R6 ~; U9 ^: O
图8.1.5.6 编译结果: ]3 k- |7 c9 y3 ^( V! r3 K8 N8 p
编译结果提示:代码总大小(Porgram Size)为:FLASH占用13520字节(Code + RO + RW),SRAM占用2008字节(RW + ZI);并成功创建了Hex文件(可执行文件,放在Output目录下)。% T3 ^6 {8 ~1 |. l0 C, J2 ^
总结:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到0错误0警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。+ E) a# g* h: B+ \
另外,我们在Readme分组下还没有添加任何文件,由于只是添加一个说明性质的文件(.txt),并不是工程必备文件,因此这里我们就不添加了,开发板光盘的源码我们是有添加的,大家可以去参考一下。3 S+ l2 z/ z4 d* n6 w. P" \8 ?
至此,新建HAL库版本MDK工程完成。
0 h, k5 [, p  F$ a6 y* F8.2 下载验证
, h0 \. F% B$ j8 z+ g; c. O这里我们继续使用DAP仿真器下载,在MDK主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,如图8.2.1所示:/ G& ]6 v) j$ e
& L- G5 u$ i0 X4 Y& T2 k
c780acf342a14739aaffe4cac5f5d48f.png $ E7 [1 D" T( a5 O, s* r1 n! _
4 a$ X  E" ]0 l9 S" q- t
图8.2.1 下载成功
8 o7 V% J) p  @2 u上图提示:Application running…,则表示代码下载成功,且开始运行。此时,看到RGB的红、蓝、绿三种灯轮流亮,类跑马灯。如果有朋友没能下载成功,请看第四章寻找问题,或者直接对照我们提供的实验0-3,新建工程实验-HAL库版本工程设置。
% R8 B, q+ a. v+ b/ A' I- [
6 Y9 ~' `+ U& i; M" I8.3 分散加载文件简介/ O. }: \6 A! q6 z: n4 V& t6 Q
ARM芯片用于在链接时指定存储器分配方案的文件,称之为分散加载文件(.sct),它可以将不同的代码(.o文件)放在不同的存储空间。关于分散加载的详细介绍,请参考:A盘8,STM32参考资料4,分散加载分散加载文件浅释.pdf,这是周立功公司的一份文档资料,详细介绍了.sct文件的基础概念、语法及应用实例说明,对学习分散加载非常有帮助,请大家务必先学习一下这个文档。
0 x6 l% a/ ]4 h/ N0 J! d. ~本节,我们仅对.sct文件进行一个简介,方便大家学习。3 f3 y& X- U8 x! c
首先明确一个概念:MDK编译任何STM32工程,都会需要用到分散加载文件。分散加载文件的来源有两种方式:0 A1 P) i: z' p$ L/ f4 \/ h
1,通过MDK自己生成;* w6 f2 c3 u! R. h" r/ ]0 u8 Z, t
2,通过用户指定(用户自己编写);
9 _3 L. T5 g8 M7 {( P* w首先,我们来看MDK自己生成的.sct文件。
2 c4 P9 f* K4 M, n3 G9 y1 z选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.1所示的设置:9 }8 K& o) W* }& A8 I7 [0 C
: y" C3 c9 c6 F8 [, e& U
1b2f6b2ec058411b9335ca44636b00c8.png
  a! `- R$ Y" h; ?* G2 d! f
( T2 Y  A. V8 q, ?$ J* J8 R图8.3.1 勾选Use Memory Layout from Target Dialog选项
  q* {& P' a1 ?. M9 c6 a, d! X勾选Use Memory Layout from Target Dialog选项后,MDK就会根据Target选项卡里面的相关设置来决定存储器分配,如图8.3.2所示:8 W! M+ y  f3 V' E! K1 c; Q5 ]

- `" n/ D2 h; O- F 07e46a153a124ba6bec9f183314042fd.png 0 E. `: B: X1 w: \; f2 c3 L

6 k$ E8 b( q1 V图8.2.2 MDK默认的存储器分配
4 f9 z0 q/ W6 u  r标号①,是MDK的只读存储区域(ROM)和可读写存储区域(RAM)的配置区域。
8 k$ ~, M* t$ G* A- V标号②处,说明默认将所有的代码(Code + RO Data + RW Data)都存放到IROM1指定的地址范围上。其起始地址为:0X0800 0000,大小为:0X20000。
: Y) m9 z* R9 X# \) U4 V% c标号③处,说明默认将所有的变量及堆栈(RW Data + ZI Data)都存放在IRAM1和IRAM2指定的地址范围上。IRAM1的起始地址为:0X2000 0000,大小为:0X20000;IRAM2的起始地址为:0X2400 0000,大小为:0X80000;变量的具体位置由编译器自动分配。
" J4 J: Y. {- u% D! C在完成以上两步操作以后,对MDK进行一次编译,在编译成功后,MDK会自动生成一个以工程名命名的.sct文件,存放在Output文件夹里面,如图8.2.3所示:
: t4 e' U4 d! p. p# g# [7 E
4 R% n* x0 d$ t* r9 a2 F ef00c14f4d1b4f88a3910a68e4d8deb1.png # P8 C1 v4 b/ _3 v1 @

( v! s; L! O0 Y! i0 ~- d. ~- J0 [图8.2.3 MDK自动生成的分散加载文件; n9 F8 z: v6 R) J- j
打开上图中的test.sct文件,其内容如下:+ ]. q( `  }+ O* D8 y: p) G0 i% E& t: b
' o* H9 F- ^7 `5 y" e* K4 [/ f/ \
  1. ; *************************************************************
    4 v7 z$ x2 F. a0 ?) P6 h
  2. ; *** Scatter-Loading Description File generated by uVision ***
    - ~9 _. q' s; U. W  y4 N- W
  3. ; *************************************************************
    " p) l( o1 k. H: j

  4. 2 f% q' O- q* i6 n$ J) t" c
  5. LR_IROM1 0x08000000 0x00020000  {    ; load region size_region; p. v- j. s0 s
  6.   ER_IROM1 0x08000000 0x00020000  {  ; load address = execution address" r) V. U2 g& E" r
  7.    *.o (RESET, +First). E5 d. r6 E8 Z2 R/ N
  8.    *(InRoot$Sections)
    ' f1 t$ N4 G! H5 s" T
  9.    .ANY (+RO)/ }1 K. }5 w& E( d
  10.    .ANY (+XO)
    6 S6 B1 z* L3 Y0 N( \- S7 s7 J
  11.   }- v; F1 b1 U7 J8 m0 }
  12.   RW_IRAM1 0x20000000 0x00020000  {  ; RW data# n* b( c9 Y, y# Q9 L4 F; b
  13.    .ANY (+RW +ZI)5 S( I) J2 e' q, m
  14.   }
    / ~3 N3 M8 y( \7 F4 N' M3 u0 ~! i
  15.   RW_IRAM2 0x24000000 0x00080000  {1 {; I% o) D, U
  16.    .ANY (+RW +ZI)
    3 i3 D& m. i; R" @
  17.   }8 Q4 a0 y1 Y+ T2 U3 N. L. X
  18. }
    , Z% r. I7 S# z& ^7 o0 g6 C  S
  19.         LR_IROM1是一个加载域,起始地址为:0X0800 0000,大小为:0X0002 0000。它包含三个运行域分别是:- P) o5 @  N( v+ Z  a
  20.         ER_IROM1运行域,起始地址为:0X0800 0000,大小为:0X0002 0000。: g  j( P' n) o, X( o# E
  21.         RW_IRAM1运行域,起始地址为:0X2000 0000,大小为:0X0002 0000。5 R8 F+ E4 K- ?3 ?
  22.         RW_IRAM2运行域,起始地址为:0X2400 0000,大小为:0X0008 0000。
复制代码
# t1 w6 v  `% \" D4 v' a
其中:
3 c# H) ?. T1 ^. [$ G; Y. T- s# r4 m9 z$ v
( c9 J9 X: M+ t- n- Z& hER_IROM1为ROM区域,存放:Code + RO Data + RW Data等只读数据,由:.ANY (+RO)指定,即所有只读数据,都存放在这个区域。% V$ d8 F* M! \5 Z" r5 S1 [* o. F( i5 M
RW_IRAM1和RW_IRAM2为RAM区域,存放:RW Data + ZI Data等可读写数据,由:.ANY (+RW +ZI)指定,即所有的可读写数据,都存放在这两个区域,具体存放位置由MDK编译器自动分配。6 b$ b" ?5 q0 H' u7 P! H
*.o (RESET, +First):表示优先(+FIRST)将RESET(即中断向量表)段放这个域的起始位置,实际上就是把中断向量表拷贝到最开始的位置。9 X" r7 F6 u+ ]/ J4 B" o

: B- B7 g0 i5 V, `6 r& i7 M(InRoot$$Sections):表示将所有用到的库段放到root区,如:__main.o、__scatter*.o和__dc*.o等。
: }& u4 ]; F- f- ?& F  x以上,就是MDK自动生成的.sct文件简介。
  E3 \0 ?1 t9 {$ R9 j接下来,我们看用户指定.sct文件的实现方式。选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.4所示的设置:. d" `0 d. s  {$ M1 B) B+ A
  ~9 \- f: p, n3 \  x# Z
54ff6c39cc27474493884470322305e5.png   w' z# W6 G9 i
% ]- c! ~2 o0 K1 @
图8.2.4 取消Use Memory Layout from Target Dialog选项7 y9 d. I! c) x- m' b6 g6 T
标号①,取消Use Memory Layout from Target Dialog选项,使用用户自定义分散加载文件。- ]; [( P% }, b, T- O9 D
标号②,新建工程时,已选择SCRIPT\qspi_code_scf.scf分散加载文件。
  ]& T! P1 U3 L9 S8 V1 p, ~( W, I标号③,点击Edit按钮,即可在MDK里面打开qspi_code_scf.scf分散加载文件。' ^! T& n7 J+ M: }, C
qspi_code.scf的内容如下:
9 j' m- R- L* H4 N; U( }
/ |9 I  w$ M5 ^3 Q% e
  1. #! armcc -E
    . r" W1 }* \! A7 D. K- R, L" x  |0 J
  2. ;#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc
    2 C& I1 O5 w, l/ q( A  C0 e
  3. /* 使用说明 7 @1 B9 ?' {) E8 \0 X. j% q" d
  4. ! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, 用于AC6编译报错(L6709E错误)时,请使用此设置7 J: p9 ]4 I% B! `6 q4 S; q$ f9 C; o8 C
  5. !armcc -E, 用于AC5编译报错(L6709E错误)时,请使用此设置& j" Z/ q# E  N& G1 L, I9 W8 m
  6. 注意,设置必须放本文件第一行!否则还是报错!请注意调整顺序!
      m" O$ T5 Z6 T+ W0 X+ s3 v
  7. */% l0 b( f- B5 G8 |1 @8 F: z
  8. . z5 ?) [& ?! D$ `7 l4 h/ b/ ?' e3 y3 w7 i
  9. /**
    & ~4 e" n1 F# h8 B. k0 d
  10. **********************************************************************************************
    ! [3 U$ y0 ]0 L) ]* h
  11. ) M% l0 t# _; J# E7 ]: ?/ @/ k
  12. **********************************************************************************************  V, W: w/ I) n: {8 L, Z
  13. * @attention+ x/ _; ~6 R! p0 H
  14. *" W1 @" y% ^3 D7 q& _. s
  15. *8 Y, k- p, d$ w. N4 |
  16. **********************************************************************************************
    4 D' r8 b- k7 j
  17. */+ e. z! p6 k! ]' s1 l( C

  18.   {) e3 `. S; R, z
  19. #define m_stmflash_start    0X08000000  /* m_stmflash(STM32内部FLASH)域起始地址 */" H& a8 ^1 I0 ^' |8 i
  20. #define m_stmflash_size    0X20000     /* m_stmflash(STM32内部FLASH)大小,H750是128KB */
    9 o7 K# x* F; T! E" n% d
  21. 5 l3 B: U  j  s
  22. #define m_qspiflash_start   0X90000000  /* m_qspiflash(外扩QSPI FLASH)域起始地址 */
    , x/ P; x) C( H9 v7 e  P4 _
  23. #define m_qspiflash_size   0X800000    /* m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB */
    8 P+ M+ f/ G4 ~2 y1 W. ^4 n: S

  24. 4 f% \, E+ ~, U/ B2 U3 T
  25. #define m_stmsram_start   0X24000000  /* m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM */& R# R% B- f! a! t3 E' }3 {
  26. #define m_stmsram_size   0X80000     /* m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB */4 V! v$ \  `6 d" J

  27. 5 Q& p2 \* c6 ~, p3 H- s" f7 i; u
  28. LR_m_stmflash m_stmflash_start m_stmflash_size          /* LR_m_stmflash加载域  */
    ' S& f. s* s, F, D* }
  29. {
    , n4 Z. b$ J- ]7 V( I' ^
  30. /* ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size  */
    3 [6 V8 E3 Z6 O9 Y
  31. ER_m_stmflash m_stmflash_start m_stmflash_size {   
    ) C! o2 i. |9 K) k- u: G. V& [
  32. /* 优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start */, _/ _1 e/ r" G8 |- U2 v
  33.       /* RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的. */
    . _4 s( Y2 u" q0 L# H) h
  34. *.o (RESET, +First)                            2 R& P+ C2 ^/ z4 j) f! o
  35.         /* 将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等 */
    ! [7 F; c) h# a6 \7 d: L- |( h
  36.         * (InRoot$Sections)                            ! p. t# X" b1 n6 B$ n: u
  37.         * (Veneer$Code)
    $ w( w  M: J6 F) K. [( D$ w
  38.         libinit.o) N2 ?3 r+ ?6 Y0 A/ \7 j
  39.         libinit2.o
    3 h' O2 H  [. `
  40.         libshutdown.o
    . C& C% Y$ Z- y& B$ M
  41.         libshutdown2.o
    / f/ G0 @" K- A/ i  h5 `
  42.         __rtentry.o6 q8 O2 o5 j3 f, X9 g
  43.         __rtentry2.o, g, R) o7 L& T% Q* W9 b( G
  44.         __rtentry4.o/ X2 d+ o, x/ u, W" H
  45.         rtexit.o
    # F, }) ~( i9 V$ ~
  46.         rtexit2.o+ Y" [  P3 U( f2 w" o$ I
  47. ; S% n( d' q* `8 W4 x- z
  48.         use_no_semi_2.o
    ; |! _6 D" H1 R1 D/ R
  49.         heapauxi.o
    4 v8 g) y; n' y7 V6 \* N9 @% G  R$ {
  50.         use_no_semi.o- o$ z! N1 G4 z% ?' e# ~# E6 G
  51.         sys_stackheap_outer.o
    8 U7 |* F* q/ I0 Y+ D! R
  52.         exit.o
    6 Z- d3 U( W; r0 R2 @, m
  53.         libspace.o  q( S8 u$ B% C, D, |" Q, u( v
  54.         fpinit.o
    ( a1 g  v+ o1 ?
  55.         lludivv7m.o- Q/ |$ ?. p9 \1 l0 d
  56.         startup_stm32h750xx.o
    ( {  h, T+ r$ V& d( x9 N) i
  57. * t. H+ ~* x* k) A, U
  58.         rt_locale_intlibspace.o. y1 o! W4 T, G0 `% @7 X
  59.         lc_numeric_c.o
    : N( ]8 {1 g: T0 r7 x6 C: p7 y
  60.         lc_ctype_c.o
    4 f3 g+ P) w8 _

  61. * H9 S0 X$ q6 N' E" o
  62.         main.o
    ) P% C+ K( q& s- @, l0 o4 T+ v
  63.         sys.o
    8 K" H: M6 ?7 U
  64.         usart.o
    4 H1 I* }$ V3 N+ q
  65.         delay.o
    $ b& P5 r+ n, Z7 r! k4 g% |
  66.         pwr.o2 r2 Z6 y6 x  ]
复制代码
1 o6 u2 K+ d0 L' c% B& I
   /* H7的QSPI接口不支持读时写,因此必须把以下3个文件放到内部FLASH,以保证可以( l+ U1 g  q; F9 o& y
对QSPI FLASH的写入 */  E9 p1 a, y. t

$ e  X( d* D! y! h( T
  1.         qspi.o
    + _' [* w" B( m
  2.         norflash.o
    + z6 Q" N9 z$ A( o1 a
  3.         norflash_ex.o: w: [. q5 c/ E% L- l6 @6 k
  4. * `4 K% y- ]: f
  5.         /* 针对HAL库驱动添加到内部的文件 */3 `2 M8 j+ v1 ~6 R- i
  6.         system_stm32h7xx.o
    ; w) Q3 F' p9 }4 N! w4 g1 L
  7.         stm32h7xx_hal.o
    ' o/ I& F0 D& K: _# P; i9 A8 S
  8.         stm32h7xx_hal_cortex.o
    2 C) ~3 t+ r9 a/ ]
  9.         stm32h7xx_hal_rcc.o7 i  Q$ `1 R# }$ [- c3 R+ v
  10.         stm32h7xx_hal_rcc_ex.o
    3 l3 @7 H' e4 _9 ~/ q( W6 I
  11.         stm32h7xx_hal_gpio.o% N8 B4 G. n1 R! f
  12.         stm32h7xx_hal_dma.o
    8 T" i& ^7 N) _4 A, Q! k; N
  13.         stm32h7xx_hal_dma_ex.o
    / y) G) `9 x9 D7 `$ f1 K
  14.         stm32h7xx_hal_qspi.o
    ' {( j1 n5 b3 @: z1 l$ U  o
  15.         stm32h7xx_hal_pwr.o+ p. k0 d; k- R6 q
  16.         stm32h7xx_hal_pwr_ex.o1 _1 J. C: E( [5 k0 e1 E
  17. }
    . P4 M+ ^7 Q8 \# D0 H
  18. /* RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size. */9 H& C- r% W, V
  19.     RW_m_stmsram m_stmsram_start m_stmsram_size {       + \& h9 U% S: e3 N' g( w: H
  20.         .ANY (+RW + ZI)                           /* 将所有用到的RAM都放在这个区域 */
    - U4 h5 I" |1 T* ]: a* z, Z
  21.     }
    ( I( d# F( e- i. E8 r* ~' A
  22. }
    ( |2 Y6 b- r2 [+ A' h5 w# f( O
  23. 5 }* N, Y& D9 S$ k9 c# Q
  24. LR_m_qspiflash m_qspiflash_start m_qspiflash_size       /* LR_m_qspiflash加载域 */
    2 g) R2 L/ G8 k0 }
  25. {
    : o% R; y) |. E5 b: O! h( D, x+ I
  26. /* ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size */
    & @# i' x. G5 g; j4 O' g/ S* ?
  27.     ER_m_qspiflash m_qspiflash_start m_qspiflash_size {
    5 ?% J( i. B9 a4 v3 }+ r+ w
  28.         .ANY (+RO)     /* 将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的 */, Y- U  r4 J! a. `/ p+ w6 U1 a
  29.     }2 k* R9 }& i3 L: |: K0 E
  30. }
复制代码
5 H: Q- t, @* s. P5 c
相比于MDK自己生成的分散加载文件,我们自己编写的相对复杂一些,qspi_code.scf分散加载文件包含2个加载域,3个运行域,分别是:
; e; [, i4 Q, eLR_m_stmflash加载域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。它包含二个运行域分别是:: `* b2 C- n0 e1 z# p, r1 G
ER_m_stmflash运行域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。  G6 I; }2 J, S8 w& B8 Y9 O* _
RW_m_stmsram运行域,起始地址为:m_stmsram_start(宏定义,实际值:0X2400 0000)大小为:m_stmsram_size(宏定义,实际值:0X80000)。% k: q8 ]* p2 |( l1 B' F/ ~% A; ?' D0 N
LR_m_qspiflash加载域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。它包含一个运行域:
& ^$ y  B4 J2 s/ r- L- o8 _' |% `' d8 AER_m_qspiflash运行域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。
. Z& k  @0 N9 A1 c3 @具体的存储器分配情况为:
$ F! O( r" ]7 v8 j4 ~ER_m_stmflash运行域,包含:*.o (RESET, +First)开始到delay.o结束的相关代码,这些代码运行在内部FLASH,可以得到最佳的性能。需要注意的是:这些代码大部分都是必须放到内部FLASH,否则无法正常运行!!5 Y9 y' y' d7 W9 R; O3 ~
ER_m_qspiflash运行域,所有没有在ER_m_stmflash运行域指定的代码,都被放在这个运行域,这些代码运行在外部SPI FLASH,速度比内部FLASH慢一些。  t6 f8 N$ D5 W: c' K, c- J/ s
RW_m_stmsram运行域,所有变量及堆栈(RW Data + ZI Data)都存放在这个运行域。2 I( p( u; _4 ]5 K2 r3 f
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct文件,特意将.ANY ROM区域放在外部SPI FLASH,这样大家在新增.c参与编译时,默认就是存放在外部SPI FLASH的,这样使用起来就更方便。
* z6 S3 m9 k. t% g" ?
! f6 \5 d# @$ e4 u0 W" D注意事项:& s7 b) |' V0 p' [+ p; w
1、如果你新增的代码,对速度有要求,可以将其对应的.o添加到内部FLASH,即放在:ER_m_stmflash运行域。2 h6 y9 |& R3 N
2、如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增的.o放到ER_m_stmflash运行域后(重新编译)再尝试。如果还不行,可以尝试将所有代码都放到ER_m_stmflash运行域后再尝试。! l3 u% ]8 y. |- v& p- j
至此,分散加载文件就给大家介绍完了。& z2 p8 d: C9 c1 T* B' G3 K
————————————————8 o: O" S; W" ~% s7 R  D( c! ~
版权声明:正点原子* I1 P: Z/ u3 U; Z6 T) c

# N  X0 c' s; _# {+ F7 x
) h( J; {, Q9 `
% g  J8 i8 V& m% W; M, L( W3 b% {
赞 收藏 评论0 发布时间:2022-10-5 21:06

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版