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

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

[复制链接]
STMCU小助手 发布时间:2022-10-5 21:06
新建HAL版本MDK工程: Q1 M- }* g2 ~, \
在前面的章节我们介绍了STM32H7xx官方固件包的一些知识,本章我们将讲解新建HAL库版本的MDK工程的详细步骤。我们把本章新建好的工程放在光盘里,路径:4,程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,大家在学习新建工程过程中间遇到一些问题,可以直接打开这个工程,然后对比学习。
$ v; N7 m; u: J; O" E) M0 _# c9 q( T+ j
8.1 新建HAL库版本MDK工程
) A% Y, H. P7 S# |% ?: y( M本节我们将教大家如何新建一个STM32H750的HAL库版本MDK5工程。为了方便大家参考,我们将本节最终新建好的工程模板存放在A盘:4、程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,如遇新建工程问题,请打开该实验对比。
0 C2 d1 T% q1 l# h" Z. N整个新建过程比较复杂,我们将其拆分为5个步骤进行讲解,请准备大概2个小时时间,耐心细致的做完!对你后续的学习非常有帮助!# B/ P! H2 p9 s( [, y0 m4 S$ a
在新建工程之前,首先我们要做如下准备:
" x+ |; X. `0 ]) i) ^2 Y1、 STM32Cube官方固件包:我们使用的固件包版本是STM32Cube_FW_H7_V1.6.0,固件包路径:A盘8,STM32参考资料1,STM32CubeH7固件包。
, d/ L/ V- d5 L5 N2、开发环境搭建:参考本书第三章相关内容。
  B3 _$ k3 E# \' @1 M8.1.1 新建工程文件夹1 U) U7 A$ H- Y# ^0 m* |: `
新建工程文件夹分为2个步骤:1,新建工程文件夹;2,拷贝工程相关文件。
4 q, ?9 x: t5 N9 l1.新建工程文件夹6 u9 |# G' I. _( K7 r8 n! |" e( G2 F- p
首先我们在桌面新建一个工程根目录文件夹,后续的工程文件都将在这个文件夹里建立,我们把这个文件夹重命名为:实验0-3,新建工程实验-HAL库版本。如图8.1.1.1所示:2 @2 A( y+ g' s- u! a
c07d881ff02749af9842e8d3614198f4.png 5 G4 o( W' |$ b3 a/ Q

# g/ K# v2 N+ E! t) h; B" e" n, ]图8.1.1.1 新建工程根目录文件夹; R) u/ w8 a; P* o4 L
为了让工程的文件目录结构更加清晰易懂,我们会在工程根目录文件夹下建立以下几个文件夹,每个文件夹名称及其作用如表8.1.1.1所示:8 z. s& l# V) F1 ]& W& z  r& n7 x/ M1 i2 W

1 y  [: h4 U8 E* s' } 09bd521756de4e4ca5b578aa5591d9b2.png
& E0 R5 N/ g* M  ?! N: H$ P7 d9 |% `; L! r3 K4 E, t' G
表8.1.1.1 工程根目录新建文件夹及其作用
3 \1 |3 T3 f" b- i7 V. O) r/ p新建完成以后,最后得到我们的工程根目录文件夹如图8.1.1.2所示。
: R, S' Q/ X% C; X% [1 c# q6 V" D7 }; @8 s* E- I
d83446698d7e4a6181e1d40778b8742c.png * Q7 s8 ]9 u3 y8 U2 g; R' m% K

, }8 E+ n4 S9 e1 J+ l7 f图8.1.1.2 工程根目录文件夹& n5 D: R* }# X
另外我们的工程根文件目录下还有一个名为keilkill.bat的可执行文件,双击便可执行。其作用是删除编译器编译后的无关文件,减少工程占用的内存,方便打包。还有一个名为readme的记事本文件,其作用是介绍本实验的各种信息。
6 c) G+ a5 G  B4 h' R工程根目录及其相关文件夹新建好以后,我们需要拷贝一些工程相关文件过来(主要是在Drivers文件夹里面),以便等下的新建工程需要。
  R( b1 }0 E$ a+ E2. 拷贝工程相关文件+ N. A$ V, ^7 y4 l3 |9 v/ r0 c2 _* }: @
接下来,我们按图8.1.1.2的根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。; e6 e$ z' f6 \# U8 }3 G
Drivers文件夹- A. i" a' I# ]! H
该文件夹用于存放与硬件相关的驱动层文件,一般包括如表8.1.1.2所示的三个文件夹:, U; Z3 o! G+ w8 {2 u, ~# f2 c4 d. @

" ~8 Y6 v# b' _ a28caaec3fcb44039e3f6b6858907daf.png   a/ I2 ]- f. o# v; x; q  _

$ D4 m2 ~3 z4 A$ m) z& m表8.1.1.2 Drivers包含文件夹& g# e$ v9 @$ a; G& G9 U+ S& v  i
BSP文件夹,用于存放正点原子提供的板级支持包驱动代码,如:LED、蜂鸣器、按键等。本章我们暂时用不到该文件夹,不过可以先建好备用。, q& y" Q' m) O( @
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文件夹里面拷贝过来。* J$ {2 C& Q% J' F9 ]* u# {4 X/ U9 c9 L
SYSTEM文件夹,用于存放正点原子提供的系统级核心驱动代码,如:sys.c、delay.c和usart.c等,方便大家快速搭建自己的工程。该文件同样可以从:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。
& O/ G: P6 r& a, O5 H5 TSTM32H7xx_HAL_Driver文件夹,用于存放ST提供的H7xx HAL库驱动代码。该文件夹我们可以直接从STM32CubeH7固件包里面拷贝。直接拷贝“STM32CubeH7固件包Drivers”路径下的“STM32H7xx_HAL_Driver”文件夹到我们工程的Drivers下,只保留Inc和Src文件夹即可。
& E" [3 Z% M) T0 V执行完以上操作后,Drivers文件夹最终结构如图8.1.1.3所示:. g$ J$ l1 w. k( y; |. ^" N

: Y& G7 |6 V0 D$ q0 o( K; r 89aca4a79e13457088ecf50d8d500e3d.png + T9 g2 ?: N) I6 y/ d- S

, C+ N9 P$ L# E( D图8.1.1.3 工程根目录下的Drivers文件夹4 h( [0 @% K6 c; h" k
关于工程根目录下的Drivers文件操作到这里就完成了,可以说步骤是有点多。在此过程遇到问题的话,请大家多参考我们提供的实验0-3,新建工程实验-HAL库版本工程,一步步操作。
$ \  c4 [2 n$ v  _6 p0 o/ oMiddlewares文件夹
: X0 A# \, }1 q, g& s* J5 r  m该文件夹用于存放正点原子和其他第三方提供的中间层代码(组件/Lib等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各种OS、各种GUI等等。本章我们暂时用不到该文件夹,不过可以先建好备用,后面的实验将会陆续添加各种文件。7 u! l' a9 D- Q1 m
Output文件夹, @" s$ M5 W$ ?+ Y
该文件夹用于存放编译器编译工程输出的中间文件,比如:.hex、.bin、.o文件等等。这里不需要操作,后面只需要在MDK里面设置该文件夹为编译过程中间文件的存放文件夹就行。
- e+ H5 r* G8 r) r, n- l/ l  j( QProjects文件夹
. M% |8 x2 Q4 |7 A+ h该文件夹用于存放编译器(MDK、IAR等)工程文件,我们主要用MDK,为了方便区分,我们在该文件夹下新建:MDK-ARM文件夹,用于存放MDK的工程文件,如图8.1.1.4所示:
  _! L; }* B* s1 J2 d$ W6 _, q5 z' ?0 p- B/ n1 s, _
2278dc824fc0410f95513f3bb54003ca.png ( }, D* h5 r0 S+ J$ j* s% B' \
' f/ V8 s* I  z/ d2 Y" V
图8.1.1.4 在Projects文件夹下新建MDK-ARM文件夹
- h& N7 r9 b9 @+ }, Q. ^User文件夹* `' f) u1 o3 @: F. y8 k
User文件夹用于存放HAL库用户配置文件、main.c文件、中断处理文件,以及分散加载文件。# Y% f  ~. `0 S3 |" l# k$ [
我们首先从官方固件包里面直接拷贝官方的模板工程下的HAL库用户配置文件和中断处理文件到我们的User文件夹里。官方的模板工程路径:STM32Cube_FW_H7_V1.6.0\Projects\ STM32H750B-DK\Templates\Template_Project,打开Template_Project文件夹,如图8.1.1.8所示。
9 H2 L7 }" q6 J- F( m  W6 Y; W
% f/ S9 I/ W& z. a: l9 s1 y! i b80bce4e05ec4463bca87c2ea099755e.png
# s4 K3 v$ Q# ^- Z) ~# @8 c+ J1 J- s  Z3 [* z6 \6 k. H
图8.1.1.8 官方模板工程根目录
( M" |* P9 t/ h1 ]* H* _我们需要的文件就在Inc和Src文件夹里面,在这两个文件夹里面找到:stm32h7xx_it.c、stm32h7xx_it.h、stm32h7xx_hal_conf.h这三个文件,并且拷贝到我们的User文件夹下。! q; Z- ?/ `6 j0 E( j% s& V
最后在User文件夹下新建一个命名为SCRIPT的文件夹,用于存放分散加载文件。分散加载文件直接在我们的实验0-2,新建工程实验-HAL库版本工程对应位置拷贝过来,后面再给大家讲解。main.c文件我们也是放在User文件夹里面的,后面在MDK里面教大家新建.c文件并保存。, U4 o7 T$ }# p  E1 k: d# o
8.1.2 新建一个工程框架. ]' G- z. U, q; j5 _" q) D* ?
首先,打开MDK软件。然后点击ProjectNew uVision Project如图8.1.2.1所示:4 T8 A+ |- \* q# j

. c) U+ l! H7 B1 R1 M d7b3678492b147a4b3afd58c9fc1a07f.png 5 }6 i- B8 Z* A: [. d) s6 O+ w
; v" x/ O7 b1 y0 Z: ?
图8.1.2.1 新建MDK工程5 S+ s" i! i! |
然后弹出工程命名和保存的操作窗口,我们将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:桌面实验0-2,新建工程实验-寄存器版本ProjectsMDK-ARM,工程名字我们取:atk_h750,最后点击保存即可。具体操作窗口如图8.1.2.2所示:: J- `5 N& [, B+ ^1 {4 f) V

  Q" L: a/ [: U2 n6 ?1 h 6b119a5b802c457692bf3e22588dd8bc.png
% l. }5 f$ K  y2 j
% C1 Y% e4 l' |6 [9 {图8.1.2.2 保存工程界面
* [$ i6 p& H. o3 i$ Y之后,弹出器件选择对话框,如图8.1.2.3所示。因为MiniPRO STM32H750开发板所使用的STM32型号为STM32H750VBT6,所以我们选择:STMicroelectronicsSTM32H7 SeriesSTM32H750STM32H750VBTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件pack才会显示这些内容哦!!如果没得选择,请关闭MDK,然后安装 A盘:6,软件资料\1,软件\MDK5\ Keil.STM32H7xx_DFP.2.5.0.pack这个安装包后重试)。
1 M7 {6 J# T0 F0 s7 G% ~& s1 J; e; |+ t- B, M
c25315eb813e47abacc2163ff53014bb.png
. d% O. X/ ]8 @
! E) T6 F* s- s% E, V* v, ^& ]                       图8.1.2.3 器件选择界面
% j1 M$ `  A5 M
( J3 u" |. _. r! ?1 v点击OK,MDK会弹出Manage Run-Time Environment对话框,如图8.1.2.4所示:! |/ L/ w* q5 X) M" q* `

6 Y( [1 Y, O. R( f 840c8d7777c748d4ba3c8480058b0bc9.png
8 V. f& H, }" e
4 P+ z3 b( c/ }" I1 V  f图8.2.1.4 Manage Run-Time Environment界面
* X4 t9 b# d$ T这是MDK5新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。所以在图8.1.2.4所示界面,我们直接点击Cancel,即可,得到如图8.1.2.5所示界面:' t, W/ d" D  T( \

* |- o1 n$ ?5 I) P/ r 01c6e443fd774a919f4afe3eff0d363e.png & ~( ~7 s8 Z3 [2 z% k

  r, L  b- N1 M图8.1.2.5 工程初步建立
+ ?* W% C/ E( k8 D此时,我们打开MDK-ARM文件夹,会看到MDK在该文件夹下自动创建了3个文件夹(DebugConfig、Listings和Objects),如图8.1.2.6所示:- Y4 e( t8 u9 J2 N: p

: |. j% V* L5 ~: S5 m: X4 r% l cf22cd3c423d441398f1363d26b3d632.png . J. u* k. m6 D  r, x: G+ o& I
- ~1 `/ [) T% c& J2 f2 v4 h
图8.1.2.6 MDK新建工程时自动创建的文件夹* f0 j4 ]5 L' n; l- h; g
这三个文件夹的作用如表8.1.2.1所示:
- m& {, W6 ^! N1 L
( H+ i7 O! Y1 Z" n( W" b7 L/ x 5021687c5d93429da588e41d6ac541f5.png
! @  x7 S8 a2 g" W( ^2 P! y8 e6 b+ ^: L3 }& g& [; F
表8.1.2.1 三个文件夹及其作用
' C3 i( P0 ~% m( D" I) X编译过程产生的链接列表、调试信息、预览、lib等文件,统称为中间文件。为了统一管理,方便使用,我们会把输出在Listings和Objects文件夹的内容,统一改为输出到Output文件夹(通过魔术棒设置),我们先把MDK自动生成的这两个文件夹(Listings和Objects)删除。7 Z8 \* N6 h2 j, Y3 g* F
至此,我们还只是建了一个框架,还有好几个步骤要做,比如添加文件、魔术棒设置、编写main.c等。- t7 @7 s9 \; P
8.1.3 添加文件
# e, n# I& m$ g. ?* K2 t5 o本节将分5个步骤:1,设置工程名和分组名;2,添加启动文件;3,添加SYSTEM源码4,添加 User 源码;5,添加 STM32H7xx_HAL_Driver 源码。1 @& t. p, z- u# l% K
1.设置工程名和分组名
3 h6 e$ G% G: U  ?, w; F在ProjectTarget上右键,选择Manage Project Items…(方法一)或在菜单栏点击品字形红绿白图标(方法二)进入工程管理界面,如图8.1.3.1所示:
5 S0 S4 _0 g; ^8 J9 n% i. J
* v# w: m0 }4 `6 G; t: V; \ 43f3eccd0c0a49038e00b8140982a73a.png
( b/ f6 j* Z/ C* f: M2 n- S  ?+ ?% m3 E" o: }4 _* y
图8.1.3.1 进入工程管理界面
3 F3 L0 k1 P9 K: ]! [在工程管理界面,我们可以执行设置工程名字(Project Targets)、分组名字(Groups)以及添加每个分组的文件(Files)等操作。我们设置工程名字为:Template,并设置四个分组:Startup(存放启动文件)、User(存放main.c等用户代码)、Drivers/SYSTEM(存放系统级驱动代码)、Readme(存放工程说明文件),如图8.1.3.2所示:
* R7 f) ^6 \5 o: Z  K) y( s9 b1 ~$ s# H0 Z8 x, B
fd29f722719047778a894ec11dc15ac3.png $ z/ b! a; L, G0 g0 p

$ W* @4 y0 i0 C0 Y  c图8.1.3.2 设置工程名和分组名
6 f8 \* g3 ?$ I设置好之后,我们点击OK,回到MDK主界面,可以看到我们设置的工程名和分组名如图8.1.3.3所示:
) K* d& r6 p% e% r7 f* ?0 `) r+ O0 L4 K
9575821896784b26965cf3d769e26165.png $ f. E. j5 }) w& z6 H
; L% Q6 o9 t. i, ~& N5 J% i
图8.1.3.3 设置成功
) S2 {3 a) U( H1 p4 u这里我们只是新建了一个简单的工程,并没有添加BSP、Middlewares等分组,后面随着工程复杂程度的增加,我们需要一步步添加对应的分组。
6 _' s" y- P. [, F# c+ |9 j! |注意:为了让工程结构清晰,我们会尽量让MDK的工程分组和我们前面新建的工程文件夹对应起来,由于MDK分组不支持多级目录,因此我们将路径也带入分组命名里面,以便区分。如:User分组对应User文件夹里面的源码,Drivers/SYSTEM分组,对应Drivers/SYSTEM文件夹里面的源码,Drivers/BSP分组对应Drivers/BSP文件夹里面的源码等。- w& c$ V; _, K6 o
2. 添加启动文件: ]  w1 j" k8 p% c" S8 g4 r5 r
启动文件(.s文件)包含STM32的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用main函数等,是每个工程必不可少的一个文件,我们在本书第九章会有详细介绍。
/ K: h- r/ V) F4 B/ ?7 }3 g6 Z  I启动文件由ST官方提供,存放在STM32CubeH7软件包的:DriversCMSISDevice STSTM32H7xxSourceTemplatesarm文件夹下。因为我们开发板使用的是STM32H750VBT6,对应的启动文件为:startup_stm32h750xx.s,为了节省空间,在精简版CMSIS文件夹里面我们把其他启动文件都删了。, u! T6 m2 j* Q7 G
关于启动文件的说明,我们就介绍这么多,接下来我们看如何添加启动文件到工程里面。我们有两种方法给MDK的分组添加文件:1,双击Project下的分组名添加。2,进入工程管理界面添加。
' F' W# q7 s  N' `$ d这了我们使用方法1添加(路径:实验0-3,新建工程实验-HAL库版本\Drivers\CMSIS! A; C- o# u. ?& i0 i8 M
Device\ST\STM32H7xx\Source\Templates\arm),如图8.1.3.4所示:( {/ z4 h  X- D9 |7 R3 m* M
+ z% G/ O5 ?, F2 E+ R4 ]
faa546005287411f83b7561ea1f6003a.png ' n* P; L: {5 S# B. N* a8 ?
) Q9 z" n; d5 p8 x# N
图8.1.3.4 双击分组添加启动文件(startup_stm32h750xx.s)4 W% ^  R7 G! a* w+ x, h
上图中,我们也可以点击Add按钮进行文件添加。添加完后,点击Close,完成启动文件
0 U* _" B% Z. H5 ~4 q3 `添加,得到工程分组如图8.1.3.5所示:
5 _; ~8 e+ G8 l  T  M1 G0 d9 ?
0 i# `' J( q9 ?# u0 Y 8647718b231747e6a03964b009c2efa6.png % E' O* I. H' Y- O" k
% `2 `9 c9 B: C5 J# o' h
图8.1.3.5 启动文件添加成功
( p2 [) J/ _3 H+ D4 I% W3. 添加SYSTEM源码7 R' ~8 g: q" O  P) Q$ X
这里我们在工程管理界面(方法2)进行SYSTEM源码添加。点击:按钮,进入工程管理界面,选中Drivers/SYSTEM分组,然后点击:Add Files,进入文件添加对话框,依次添加delay.c、sys.c和usart.c到该分组下,如图8.1.3.6所示:
1 E: N- M4 ^" \! ]3 c
: ^" `9 B# ^5 a7 Q 884e3508a6444b5cb08f234ae2a0499b.png
! D+ E$ f3 F. K3 k: L9 F8 _9 I
) ?5 j+ S2 ~1 v/ f图8.1.3.6 添加SYSTEM源码8 z. v! K  _7 u* `9 m
注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.7所示:
6 w. X$ ]/ R' j, z$ D8 B2 q! ~4 ?# D0 |0 ^% P0 N
a9bc3490a642456e8e6c7062e85dceaf.png
$ M& `& T) B4 ], a
0 p* E; ]* m; X. O. ~/ K9 n图8.1.3.7 SYSTEM源码添加完成- K2 G8 q: n% ]; }9 g
4. 添加User源码
7 V9 H# x1 e0 c% i这里我们在工程管理界面(方法2)进行User源码添加。点击:按钮,进入工程管理界面,选中User分组,然后点击:Add Files,进入文件添加对话框,依次添加stm32h7xx_it.c和system_stm32h7xx.c到该分组下,如图8.1.3.8所示:
% M9 d, N6 s; j0 z( A/ D$ B# w$ F7 d! i2 n8 Y' s
83627a883d394387b9a69b3f97ca5d0d.png , ^, n) R8 Z5 d# P/ V  {: Z
7 b+ q* n" a5 r1 e/ q
图8.1.3.8 添加User源码
$ c+ C$ \. |' j3 i) N注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.9所示:
# g) D7 `- y8 z& O; ]+ Y" {
$ S" a* ~9 g( P, R) D) { c7f798b6bbac4d329ef22a34f0da7a35.png - m  ~2 y7 H1 h* w& z5 ]9 I
, k0 g$ T# U8 J6 w7 A$ [1 c
图8.1.3.9 User源码添加完成
5 I* {* Q1 l! h3 _8 R0 y, D5 {3 a5. 添加STM32H7xx_HAL_Driver源码
5 e0 a- B2 U  E- ~. H: X0 K接下来我们往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 U! ~) N' E( @9 O9 i' K5 M
% V# X7 m6 L. B; }
5f1b4fe0439b4dfea7d7dc9ed6ceb634.png
, H9 i0 _- }4 N/ }( [1 I* M5 [8 J' @' f3 x* i& Y
图8.1.3.10 添加STM32H7xx_HAL_Driver源码  H0 t9 ?3 q, \) C# \% y
添加完成后,如图8.1.3.11所示:: X/ x1 M) ~: F* ~# o+ k+ e! w) J
  U" h* g; z' a4 q
bcb17b5b492a4a80a630f98abbad546e.png
8 \# U  s2 l# U
6 T! W% M( d* q图8.1.3.11 STM32H7xx_HAL_Driver源码添加完成
( G* k' Y4 Y1 y, `& M9 U2 I( V# u可以看到分组中有些.c文件有个小钥匙的符号,这是因为官方的固件包的文件设置了只读权限,我们取消只读权限就好了,方法如图8.1.2.12所示。& L2 P; z7 d" w/ p) q
4 w# d, h6 H6 V
cd4fff494965426d861a066a1ab4e118.png
) ^( @# u( O7 W. P7 O5 z$ s9 _" C, U" |5 W& P
图8.1.2.12 取消工程文件夹的只读权限
- y1 [3 M+ ?- F+ t0 n1 V8.1.4 魔术棒设置
+ h; x. Y3 R5 [2 Q0 k2 t为避免编写代码和编译报错,我们需要通过魔术棒对MDK工程进行相关设置。在MDK主界面,点击:(魔术棒图标,即Options for Target按钮),进入工程设置对话框,我们将进行如下几个选项卡的设置。# V6 |' n, E3 V6 S
1.设置Target选项卡# @5 ]! l8 W! F; p$ s2 h
在魔术棒Target选项卡里面,我们进行如图8.1.4.1所示设置:/ ^6 m8 t5 y2 s* A
' d* p, h5 `1 c' q% `1 U/ l( q
aa079a0**74261b7926c03ee69b908.png + p; P! n$ c! K

( Q7 X4 j. M" y; T图8.1.4.1 Target选项卡设置
  o1 Y" m9 c2 }+ `+ k上图中,我们设置芯片所使用的外部晶振频率为8Mhz,选择ARM Compiler版本为:Use default compiler version 5(即AC5编译器)。
% X5 b/ E, T9 B- R. E+ T( p6 o, S这里我们说明一下AC5和AC6编译的差异,如表8.1.4.2所示:
7 _. Z& O) d' `  M5 s1 g. h0 z  d5 `# y) o0 [: ]3 j4 I7 F
ec16655ac2564428a4614ce6ade99f64.png + s7 m0 H6 S( v$ K1 l

: s% P7 Z& [3 z6 G% Z- b表8.1.4.1 AC5&AC6简单对比2 P( F  `3 {5 P
由于AC5对中文支持比较好,且兼容性相对好一点,为了避免不必要的麻烦,我们推荐大家使用AC5编译器。为了让大家自由选择,我们正点原子的源码,也是支持AC6编译器的,不过在选项卡设置上稍有差异,具体差异如表8.1.4.2所示:
8 l% `4 }* c3 l1 x" D
( `* g& }- y, R+ _: k& R: P4 p2 h ac1ed12294bf4652b48a2b8b2cfdd5ba.png * ~2 b: X, u, o% ^  f  j% ]6 `
) d8 o' f5 P, [2 t; s' `+ Z4 W
表8.1.4.2 AC5&AC6设置差异5 {; D. o/ f7 |# s" r- o' q$ [4 M5 m4 E
2. 设置Output选项卡2 R+ L) O4 t) D9 q
在魔术棒Output选项卡里面,进行如图8.1.4.2所示设置:  [0 j/ y/ D0 p2 C
9 Y; C- Z9 a! X" X1 h! R" B8 k* ]
eb9687db7b964a33bad72c5485588208.png
' O' |9 t, q: f. \! K. D$ n9 E; G. e5 B3 X$ V
图8.1.4.2 设置Output选项卡
# `8 g, Y* S" a: z3 c; g注意,我们勾选:Browse Information,用于输出浏览信息,这样就可以使用go to definition查看函数/变量的定义,对我们后续调试代码比较有帮助,如果不需要调试代码,则可以去掉这个勾选,以提高编译速度。$ x* k* n! E4 _& l6 @
3. 设置Listing选项卡* X% b" I9 ~7 e% S
在魔术棒Listing选项卡里面,进行如图8.1.4.3所示设置:! v- v. X! e& K! |! E+ a
$ ~) K3 F  K7 [( P/ B
c997f30d6c3d4504a4ec90f2c42edb29.png
3 H) c# u1 O% Y: C0 H: Q" O; T" w
图8.1.4.3 设置Listing选项卡! t: r% ]" ?0 _. B! X- J
经过Output和Listing这两步设置,原来存储在Objects和Listings文件夹的内容(中间文件)就都改为输出到Output文件夹了。" e+ Q" {7 [$ ?! n3 j+ W
4. 设置C/C++选项卡
. F! A4 t! V2 X% G( E在魔术棒C/C++选项卡里面,进行如图8.1.4.4所示设置:) J. }$ s5 l7 Q+ Q0 b
" x7 L; D/ u& o& c1 S: W
ff4c06ba4f6742998cfa29c00c5ae22f.png 5 B0 c8 }  g  n/ K

7 ]5 h% |7 i- S" _图8.1.4.4 设置C/C++选项卡
- C8 m* ^- ?9 G: `在②处设置了全局宏定义:STM32H750xx,用于定义所用STM32型号,在stm32h7xx.h里面会用到该宏定义。
9 l$ O# `& n+ U, p在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6编译器的时候,这里推荐默认使用-O1优化。; D4 q9 m9 j) d7 Q/ @0 M; J9 h
在④处勾选C99模式,即使用C99 C语言标准。
) a) j+ Z5 Y2 O在⑤处,我们可以进行头文件包含路径设置,点击此按钮,进行如图8.1.4.5所示设置:
! Z7 y5 \# D* C3 V/ d% O0 ^% @/ X
! m" d( W3 U3 q. Y% K* a# ` 042d59760099402eb057ab905f45cf9e.png 8 W# x  u3 R  K7 J+ t
4 x8 _& O( C8 W' v/ i8 W+ P) [
图8.1.4.5 设置头文件包含路径$ A- H3 H4 D4 z% O4 Z
上图中我们设置了4个头文件包含路径,其中3个在Drivers文件夹下,一个在User文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的include全部使用相对路径,也就是我们只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码,如果全部是使用相对路径,则无需再设置头文件包含路径了,直接在include里面就指明了头文件所在。% U! b: w$ @- F, ]3 j( r
关于相对路径,这里大家记住3点:) M$ `) G- P- G! |
1,默认路径就是指MDK工程所在的路径,即.uvprojx文件所在路径(文件夹)
- a' r  b: a; H$ z6 Y4 I- C( ~# [0 G3 A" N2,“./”表示当前目录(相对当前路径,也可以写做“.\”)
) d. Q9 N* k- U7 f1 F$ g& E3,“…/”表示当前目录的上一层目录(也可以写做“…\”)
& T" ?% G# N1 {1 b( H; E举例来说,上图中:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,前面两个“…\”,表示Drivers文件夹在当前MDK工程所在文件夹(MDK-ARM)的上2级目录下,具体解释如图8.1.4.6所示:
" N# K- |* E& b* f" V, f; W9 n3 t, h( Z$ x
9958cf50da964d39ac15e517a4bdc8dc.png 3 x9 y$ ~! I% X& M4 D- R$ s
% v  _' h8 {1 `) H, n
图8.1.4.6 …\Drivers\CMSIS\Device\ST\STM3H7xx\Include的解释# T% O' ?3 p2 f: e
上图表示根据头文件包含路径:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,编译器可以找到⑥处所包含的这些头文件,即代码里面可以直接include这些头文件使用。; i# `8 E4 ~2 @; ]
再举个例子,在完成如图6.1.4.5所示的头文件包含路径设置以后,我们在代码里面编写:
- T! i& c# I, K#include “./SYSTEM/sys/sys.h”, T1 S+ K7 O* p$ `6 }
即表示当前头文件包含路径所指示的4个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h的路径,实际上就是在Drivers文件夹下面,两者结合起来就相当于:* x4 D& P9 C6 X' y
#include “…/…/Drivers/SYSTEM/sys/sys.h”
, q2 z, T0 C. b  {# V* F7 z' {这就是相对路径。它既可以减少头文件包含路径设置(即减少MDK配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在那个文件夹,因此我们推荐在编写代码的时候使用相对路径。  a' u; ]; U/ z# [9 D$ I5 a  f
关于相对路径,我们就介绍这么多,大家搞不明白的可以在网上搜索相关资料学习,也可以在后面的学习,分析我们其他源码,慢慢体会,总之不难,但是好用。
0 c. j) Y/ G7 \  I最后,我们如果使用AC6编译器,则在图6.1.4.4的Misc Controls处需要设置:-Wno-invalid-source-encoding,避免中文编码报错,如果使用AC5编译器,则不需要该设置!!5 o- \2 F- R; S/ ~+ ^3 Y2 m4 ~
5. 设置Debug选项卡
# {2 W5 q6 e* @在魔术棒Debug选项卡里面,进行如图8.1.4.7所示设置:
8 b/ [- Z. h0 `' x$ v$ [8 [
6 P. X8 X( b. K$ v# L 663ae5fcb1cb4a64abe8ee0f5360fdeb.png
: n/ x: E, E; y; }3 V2 {( R' q* p. I. b0 l
图8.1.4.7 Debug选项卡设置
, N% b) p& D& G8 \6 [& f图中,我们选择使用:CMSIS-DAP仿真器,使用SW模式,并设置最大时钟频率为10Mhz,以得到最高下载速度。当我们将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在SW Device窗口显示芯片的IDCODE、Device Name等信息(图中⑤处),当无法找到时,请检查供电和仿真器连接状况。4 i" P" y1 U- l/ H, t  u$ _
6. 设置Utilities选项卡5 v) r2 ]3 x8 C2 n* _
在魔术棒Debug选项卡里面,进行如图8.1.4.8所示设置:
1 ?5 }( j" @1 G5 S
+ u% X- \6 h% B) S  W9 ^1 s7 S 6cb0e659dc6846e5998d10fb61975fc7.png
) e2 ^& k/ F0 {: U5 ~$ ~5 `. P" H' _% u
6 H! H; ?3 Z5 n: ]图8.1.4.8 Utilities选项卡设置
) A7 O  t' o3 M* C- D' v图中⑥处下载算法STM32H750,是MDK默认添加的,针对STM32H750系列产品。除此之外,我们还要添加算法,点击⑦处按钮添加即可。添加好算法后,设置算法使用的 RAM 地址和大小,这里设置的起始地址为:0X2000 0000(DTCM),大小为:0X3000。必须按这个大小设置,否则下载会出错(无法加载算法)。
- W$ N+ f" o  O5 r7. 添加分散加载文件) w! y+ U7 w# T( P; h9 A0 s& a
由于STM32H750VBT6芯片内部的FLASH的空间比较少(只有128KB)。对于大的工程,这个FLASH空间是不够用的,为了解决这个问题,同时方便后续工程的新建,我们统一使用分散加载的方式来决定FLASH内存的分配,而不用MDK默认的设置。关于分散加载是什么?我们后面8.3小节会讲解,请大家先跟着我们把新建工程完成。
" \7 C0 Q; W: k: d分散加载的文件已经为大家准备好了,可以在实验0-3,新建工程实验-HAL库版本\User\SCRIPT,或者在(A盘)/程序源码/STM32启动文件/分散加载_HAL库版本/SCRIPT中拷贝qspi_code.scf文件到我们的工程User\SCRIPT路径下,如图8.1.4.9所示。
& I" @  a- i/ y) G- ^2 T; t" R" n$ l9 V
0362a607c804410ea055be3879718082.png * h8 u* V1 j9 [/ ?# D- p

$ o$ R" g' M0 C4 N; O图8.1.4.9 拷贝分散加载文件到工程3 h: f+ t" ~' p
注意:这里的分散加载文件寄存器跟HAL库是不一样的,我们建立HAL库工程,所以必需用HAL库版本的分散加载文件。
2 s$ J3 E' w' G# T  p& u, n+ j接下来我们需要对MDK进行配置,相当于把分散加载文件关联到工程里。方法:点击魔术棒, Linker选项卡取消勾选:Use Memory Layout from Target DialogScatter File路径选择SCRIPT文件夹 选择qspi_code.scf文件,然后,在disable Warnings一栏,添加:6314,6329,屏蔽6314和6329这两个警告。如不屏蔽,当分散加载里面有某些段(section)没用到,则会报警告,所以我们需要屏蔽这两个警告。如图8.1.4.10所示。' i# r. X/ w1 l

) K! R( l1 b5 n7 h. B 241659f1cccf4ec799607dbe4bcc40ed.png
- W2 A; A9 q. j+ [. f# X- C2 U# L, Q: x) g1 }# k
图8.1.4.10 添加分散加载文件
; p9 ^7 X: ^& s! ?. v2 W至此,添加分散加载文件的相应操作就完成了。
* l8 u* v$ |. ?0 F0 J. t8.1.5 添加main.c,并编写代码
8 E4 v$ P- U# v2 }; r: H在MDK主界面,点击:,新建一个main.c文件,并保存在User文件夹下。然后双击User分组,弹出添加文件的对话框,将User文件夹下的main.c文件添加到User分组下。得到如图8.1.5.1所示的界面:
. a, u. g* F/ g- k5 M
1 J0 P) @8 ?* ? b1dd0efba9964663a212af58f8129af6.png ; O( ]! L$ f  e" _2 v# D: Z
  Z% a0 |7 e9 ]" v  I: i
图8.1.5.1 在User分组下加入main.c文件
" I: T  D% a9 L4 J, d至此,我们就可以开始编写我们自己的代码了。我们在main.c文件里面输入如下代码:
. s& F1 ~7 C, J7 s5 q8 {2 b( p& {4 ]6 h
. O# t. Q; N  z* ^
  1. #include "./SYSTEM/sys/sys.h"% m" S, [5 ^0 X/ A- n
  2. #include "./SYSTEM/usart/usart.h"& Y/ e. Y- D( P8 h
  3. #include "./SYSTEM/delay/delay.h"
    ) r! b0 f8 Z+ }) B, _
  4. ' o: b# u5 I9 Q& n" ]0 ~: \$ O" T
  5. void led_init(void);                               /* LED初始化函数声明 */1 H- g; K& {/ z. O& _

  6. ; [2 f" b8 x- c* D" k
  7. int main(void)
    + K& s- n3 u. V5 G4 r2 W3 [5 r
  8. {, z& S" T( X; J. j
  9.     sys_cache_enable();                            /* 打开L1-Cache */2 `/ P2 j( @0 g7 }. [5 a+ ?
  10.     HAL_Init();                                             /* 初始化HAL库 */2 [7 }: r. P4 R- o4 [. b' B' @
  11.     sys_stm32_clock_init(240, 2, 2, 4);                /* 设置时钟, 480Mhz */6 u% U! f* B! M# g$ h
  12.     delay_init(480);                                       /* 延时初始化 */2 L$ r, U! e7 V9 J9 x* I$ Q0 {
  13.     led_init();                                             /* LED初始化 */
    ' u% l! K0 A+ E0 X2 h7 B
  14.     while(1)3 b0 N2 ~7 [+ s( m
  15.     {
    3 O/ K# W9 M, a- S+ W+ H
  16.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);            /* PB4置1 */
    ' }% K# N4 Q! y6 y- `" O
  17.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);         /* PE5置0 */ , W8 t5 Z2 B4 y$ P% n
  18.         delay_ms(500);
    $ q# X# B  @3 ?  i
  19.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);            /* PE5置1 */
    # c! \- i3 u/ T# \( {: a
  20.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_RESET);         /* PE6置0 */3 z5 R0 q- J* [* s
  21.         delay_ms(500); $ q# l4 r, g, Y# Y' g# e+ G# \
  22.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_SET);            /* PE6置1 */
    + a6 d0 o! i. W* S
  23.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);         /* PB4置0 */& W, [  u5 C+ M# y5 `5 I
  24.         delay_ms(500);3 x6 {* _. y3 l2 r' W8 h" {
  25.     }
    ) @6 F& C5 b$ t. i
  26. }
    . @) S3 o5 P7 I4 k

  27. - s( i$ M! [3 B5 g- I
  28. /**8 @& S2 G# v/ {9 m
  29. * @brief       初始化LED相关IO口, 并使能时钟
    ' P* b; E  K6 ^
  30. * @param       无3 G' H9 P' }. _( L: H' d
  31. * @retval      无
    $ _5 Q% ^6 T! d0 J7 B4 B! R
  32. */
    ; n. K( Z6 w) Q8 `9 X
  33. void led_init(void)1 Y0 i) ~$ v( U6 R5 |/ ^
  34. {/ Y% c" a7 l6 E, p6 D: g
  35.     GPIO_InitTypeDef gpio_init_struct;
    4 g5 O& _8 r4 F
  36.     __HAL_RCC_GPIOB_CLK_ENABLE(); /* PB4时钟使能 */
    : O# Z7 m" c4 H$ s' h- k
  37.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE6时钟使能 */! W4 y, g9 s2 y3 Z
  38.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE5时钟使能 */+ u* z, f, Z5 F/ L! m' E
  39. " K8 m$ i# C4 o8 B3 s! E
  40.     gpio_init_struct.Pin = GPIO_PIN_4;                                /* LED0引脚 */* v( W. b2 U. T* l
  41.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */* N( G* w% I: k) p
  42.     gpio_init_struct.Pull = GPIO_PULLUP;                              /* 上拉 */
    5 Z  K1 }) c# C: e7 R' ], p
  43.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;            /* 高速 */
    6 X6 p9 Y0 U5 Q& r8 l0 m+ S
  44.     HAL_GPIO_Init(GPIOB, &gpio_init_struct);                         /* 初始化LED0引脚 */
    - k* B9 C# G. k7 j/ \& i, Y

  45. 1 C  v) w1 d% P  h( s
  46.     gpio_init_struct.Pin = GPIO_PIN_6;                                /* LED1引脚 */
    ( C$ v. R/ |2 p3 Q
  47.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED1引脚 */- H' Z, K3 F, F$ v6 M7 o8 g+ D3 M
  48. 2 a' I& Y& F, Z! f
  49.     gpio_init_struct.Pin = GPIO_PIN_5;                                /* LED2引脚 */) ^; ~) ~2 h$ K- [" m
  50.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED2引脚 */
    5 l. y; w! U8 v+ d+ q4 j
  51. }
复制代码

+ H- w5 _: K) ~! {) W5 c此部分代码,在A盘4,程序源码1,标准例程-HAL库版本 实验0 基础入门实验实验0-3,新建最工程实验-HAL库版本Usermain.c里面有,大家可以自己输入,也可以直接拷贝。强烈建议自己输入,以加深对程序的理解和印象!!
' C; o- X- v0 ]( L4 A" q$ G注意,这里的include就是使用的相对路径,关于相对路径,请参考前面C/C++选项卡设置章节进行学习。
  N) Y4 D0 d5 d5 V" D, Q7 g( W编写完main.c,我们点击:(Rebuild)按钮,编译整个工程,编译结果如下图所示:/ e, z5 [2 J8 m  a9 Y. H

) C; d0 t* L* q/ V' f3 b! z图8.1.5.2 编译结果4 [  |" v& E, b% ~% _' ^% Y
编译结果可以看到1个错误,0个警告。这个错误说找不到main.h,因为我们也不需要用到main.h,双击这个错误会弹出下面的stm32h7xx_it.c文件对应包含main.h的语句。我们只需要把它删除,然后重新编译,如图8.1.5.3所示。0 r2 Y8 l- k( {* ^1 |
7 R* Y: G8 p3 K  w+ e7 J
68deb7ad5549455cad3065d60e1f0432.png
% ~; g- e& K& y* N
0 s" ?* S" R- H5 f# N6 z图8.1.5.3 删除包含main.h的语句
1 T$ q- f9 h% a2 U: x8 A编译后发现又有一个警告,警告HAL_IncTick函数没有声明,如图8.1.5.4所示。1 u9 Z) q" O) |# ~
3 H0 \, P; S' s6 ^' r# Y0 e8 m7 d
84622d45998148969e1d60df31ad2d5a.png
8 B1 k8 ^$ i1 `- F! a
3 L7 e7 _1 d5 b; F图8.1.5.4 编译报警告- E+ p6 O/ K, V, Z
因为这个函数是在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所示。
# d' s$ d9 y5 T: v' z& w* i2 s$ {, r5 u# E: I
844b5d27125c4d708677dc0b95b9a39d.png
) N6 B3 M' J% n% W: K
9 p! _& t/ m* g2 t4 H. \图8.1.5.5 包含stm32h7xx_hal.h头文件到工程& I) T" f, f! A; X4 R
再进行编译就会发现0错误0警告,结果如图8.1.5.6所示。1 K; b6 h3 l" r& c7 Z. D
. m: ~6 ^, Z# u  _% E% ]
0f3099f2b6e64aa286997c8756220211.png
5 a! k) L9 l/ Z3 K* T7 h
* h$ e8 i1 u4 e8 B: F# H图8.1.5.6 编译结果
4 P8 v0 g- _: d, y/ T0 W  T编译结果提示:代码总大小(Porgram Size)为:FLASH占用13520字节(Code + RO + RW),SRAM占用2008字节(RW + ZI);并成功创建了Hex文件(可执行文件,放在Output目录下)。+ N4 E" z% A" ]( _
总结:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到0错误0警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。1 r( e7 K% x2 l7 @% ]" b
另外,我们在Readme分组下还没有添加任何文件,由于只是添加一个说明性质的文件(.txt),并不是工程必备文件,因此这里我们就不添加了,开发板光盘的源码我们是有添加的,大家可以去参考一下。% w. ]8 y2 R* h: |% u& B8 S, {+ M
至此,新建HAL库版本MDK工程完成。
7 T8 b$ `" m3 t+ l8.2 下载验证5 w9 ~! W& G5 D
这里我们继续使用DAP仿真器下载,在MDK主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,如图8.2.1所示:
0 o( x: E( ]- V( q: f
. h. p" m, `) u" }- R, w c780acf342a14739aaffe4cac5f5d48f.png 6 ?: q6 q5 S7 {; l) R5 f
6 o( O% _3 O1 o, r: b9 Z9 u, v. Q
图8.2.1 下载成功
( J: u5 N* Z: p. b/ y上图提示:Application running…,则表示代码下载成功,且开始运行。此时,看到RGB的红、蓝、绿三种灯轮流亮,类跑马灯。如果有朋友没能下载成功,请看第四章寻找问题,或者直接对照我们提供的实验0-3,新建工程实验-HAL库版本工程设置。9 h+ M* j0 R1 Y# g+ c9 k2 ?

7 n9 l7 ^9 E7 Q4 N7 F8.3 分散加载文件简介
  d4 v: r- V7 U( ?% L0 W% mARM芯片用于在链接时指定存储器分配方案的文件,称之为分散加载文件(.sct),它可以将不同的代码(.o文件)放在不同的存储空间。关于分散加载的详细介绍,请参考:A盘8,STM32参考资料4,分散加载分散加载文件浅释.pdf,这是周立功公司的一份文档资料,详细介绍了.sct文件的基础概念、语法及应用实例说明,对学习分散加载非常有帮助,请大家务必先学习一下这个文档。
  [, T4 D& t  A# `3 P! @/ S本节,我们仅对.sct文件进行一个简介,方便大家学习。
$ n( f% i$ O6 H0 [6 _2 K* R首先明确一个概念:MDK编译任何STM32工程,都会需要用到分散加载文件。分散加载文件的来源有两种方式:
+ v. Y. ~+ F7 q4 W6 o7 w1,通过MDK自己生成;
" s; V. G$ A5 K) m& R; J0 o2,通过用户指定(用户自己编写);
# y$ _' u% p/ f" ?# t首先,我们来看MDK自己生成的.sct文件。/ |4 p" X5 W" Z5 k+ d
选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.1所示的设置:# k% m6 u, }" q/ u& R# C
* y2 r( b8 O6 V0 i7 c( b" W
1b2f6b2ec058411b9335ca44636b00c8.png
4 w! U3 i& r$ S& ]4 y" `
6 W! a) Q. M; C- R2 y7 s# a9 [图8.3.1 勾选Use Memory Layout from Target Dialog选项
. Z( ]2 \: c9 r7 L% f2 v勾选Use Memory Layout from Target Dialog选项后,MDK就会根据Target选项卡里面的相关设置来决定存储器分配,如图8.3.2所示:
" L# K- e1 Z; p; X9 e6 p/ {2 l( k+ i+ Y) A# t3 I5 i
07e46a153a124ba6bec9f183314042fd.png - r! C) Q* g+ u* T3 i: L/ s# Q) z% C
2 {5 h8 |  P# Y) h4 @$ u# n2 ?' j
图8.2.2 MDK默认的存储器分配+ K9 ^2 j; }0 G* w* f# d# D
标号①,是MDK的只读存储区域(ROM)和可读写存储区域(RAM)的配置区域。
- @7 z$ q: ]$ C. g标号②处,说明默认将所有的代码(Code + RO Data + RW Data)都存放到IROM1指定的地址范围上。其起始地址为:0X0800 0000,大小为:0X20000。
0 F8 s  A$ H, @/ K* F标号③处,说明默认将所有的变量及堆栈(RW Data + ZI Data)都存放在IRAM1和IRAM2指定的地址范围上。IRAM1的起始地址为:0X2000 0000,大小为:0X20000;IRAM2的起始地址为:0X2400 0000,大小为:0X80000;变量的具体位置由编译器自动分配。! _- z6 i: @5 D' j; F, T1 t9 l
在完成以上两步操作以后,对MDK进行一次编译,在编译成功后,MDK会自动生成一个以工程名命名的.sct文件,存放在Output文件夹里面,如图8.2.3所示:9 _; l& n2 u) g. q$ n

/ Z3 ^9 j7 L2 ~! `5 W% A ef00c14f4d1b4f88a3910a68e4d8deb1.png + N" ^/ L* ^5 q  s
! r+ S1 d. m8 h
图8.2.3 MDK自动生成的分散加载文件7 V2 n/ M1 K4 Q8 T$ C' e& w- [
打开上图中的test.sct文件,其内容如下:
2 ~, {; Z7 ]0 {6 T" G1 B  N( s9 [9 |
( u, e  s2 G! o" l
  1. ; *************************************************************/ m; v, @% ^8 n9 R4 N
  2. ; *** Scatter-Loading Description File generated by uVision ***3 f6 X/ w4 M7 L" e4 S
  3. ; *************************************************************
    7 I/ q/ G5 f% t
  4. $ _# c6 [9 |  m* A6 c" N; n$ I
  5. LR_IROM1 0x08000000 0x00020000  {    ; load region size_region8 @* W  {; f* w- I9 K, V5 c9 N
  6.   ER_IROM1 0x08000000 0x00020000  {  ; load address = execution address' G2 p8 V; }, r! _2 p& G/ l; b
  7.    *.o (RESET, +First); M7 X% H* e7 p# M* ?
  8.    *(InRoot$Sections)
    ! O% c/ s# [4 }
  9.    .ANY (+RO)# T) q/ |5 `0 p$ a: u4 q9 {/ R
  10.    .ANY (+XO)3 c5 O3 t* l  ?
  11.   }7 J) e) q) [# L! z; @5 t- k
  12.   RW_IRAM1 0x20000000 0x00020000  {  ; RW data6 t! f% z4 N8 y  P# R3 V1 g
  13.    .ANY (+RW +ZI)
    5 e7 L' Q  W( }
  14.   }+ z* X& L6 u' v
  15.   RW_IRAM2 0x24000000 0x00080000  {( r3 t5 w3 M5 }
  16.    .ANY (+RW +ZI)4 d/ A* T6 s* q* ?) a. h
  17.   }0 @6 c5 W6 J6 q; g* `0 l. x4 J1 U
  18. }. O+ G9 @" ]" R/ ^! Z
  19.         LR_IROM1是一个加载域,起始地址为:0X0800 0000,大小为:0X0002 0000。它包含三个运行域分别是:; {7 R2 N0 p& C1 p8 ^
  20.         ER_IROM1运行域,起始地址为:0X0800 0000,大小为:0X0002 0000。0 T0 x! E9 D/ B- {' T# z; B* T8 j
  21.         RW_IRAM1运行域,起始地址为:0X2000 0000,大小为:0X0002 0000。
    . E, M/ k5 u( T
  22.         RW_IRAM2运行域,起始地址为:0X2400 0000,大小为:0X0008 0000。
复制代码

6 Z* p4 Y# W2 r( v0 @& K" ]- e其中:5 m  Y% n, }* k. H7 g$ a6 \! [

+ l2 @* ~( ?+ _" p. YER_IROM1为ROM区域,存放:Code + RO Data + RW Data等只读数据,由:.ANY (+RO)指定,即所有只读数据,都存放在这个区域。
8 h3 ]* \7 l3 t: j  iRW_IRAM1和RW_IRAM2为RAM区域,存放:RW Data + ZI Data等可读写数据,由:.ANY (+RW +ZI)指定,即所有的可读写数据,都存放在这两个区域,具体存放位置由MDK编译器自动分配。6 o! }% ]  _2 T+ y8 e; O: ^5 S& E
*.o (RESET, +First):表示优先(+FIRST)将RESET(即中断向量表)段放这个域的起始位置,实际上就是把中断向量表拷贝到最开始的位置。
+ t" c/ }- y- ~& q* ]3 K: M: s
; S9 \. T( x7 G/ \5 C  B(InRoot$$Sections):表示将所有用到的库段放到root区,如:__main.o、__scatter*.o和__dc*.o等。
/ C$ e+ O6 {3 o/ v* v$ R6 |以上,就是MDK自动生成的.sct文件简介。
+ m( K% Q- Y  T接下来,我们看用户指定.sct文件的实现方式。选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.4所示的设置:9 a0 E( F3 j9 J9 L

/ N. C& }3 V5 F, H# H 54ff6c39cc27474493884470322305e5.png ! j1 k  u2 D; P8 |; w& h
2 g. ^8 s5 ~* L/ @+ E
图8.2.4 取消Use Memory Layout from Target Dialog选项. H! U9 r, L/ x
标号①,取消Use Memory Layout from Target Dialog选项,使用用户自定义分散加载文件。
1 t9 N! K$ S# B: W7 o8 r标号②,新建工程时,已选择SCRIPT\qspi_code_scf.scf分散加载文件。% N! O( l# A: z1 F( F- a
标号③,点击Edit按钮,即可在MDK里面打开qspi_code_scf.scf分散加载文件。
  j" d+ i, w; F" }6 B1 P: i' i* n$ ~7 Mqspi_code.scf的内容如下:
+ F2 p+ I+ f) T3 q& X3 k1 D2 L# O, f# h2 I
  1. #! armcc -E
    " o- ^9 F9 {# {' b
  2. ;#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, Z: y& u& \9 ?6 y
  3. /* 使用说明 2 w/ y  Z7 j* w# i& {* M7 S2 l
  4. ! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, 用于AC6编译报错(L6709E错误)时,请使用此设置( a' A# m# Q: H& u, u& \& l
  5. !armcc -E, 用于AC5编译报错(L6709E错误)时,请使用此设置  A, O- Z2 p- S& G& U
  6. 注意,设置必须放本文件第一行!否则还是报错!请注意调整顺序!
    . ~0 v+ c& L0 S/ D" p
  7. */% h2 f+ {, L5 k6 n/ @* W

  8. . R! |6 P. k- C( V9 c$ \
  9. /**
    1 R1 G0 p/ B3 x+ Q6 f- w% d
  10. **********************************************************************************************% N; n4 b8 K6 W5 E
  11. 1 P9 g/ q0 }1 T- _) u) k4 K
  12. **********************************************************************************************
    ; ?1 I+ a. s+ C4 B2 n& N
  13. * @attention- z  H% |0 L+ A% V# [) c3 @
  14. *
    . K6 x# m, T' }
  15. *
    . V$ N) q& j0 t6 t3 f6 R
  16. **********************************************************************************************6 ?# x6 r7 f  Q' w9 J# }
  17. */+ _$ ?* x$ k- J0 g5 {6 R+ e  p+ `

  18. # J. K% A' M5 t
  19. #define m_stmflash_start    0X08000000  /* m_stmflash(STM32内部FLASH)域起始地址 */$ F( ^6 i* Y0 {, @; ^
  20. #define m_stmflash_size    0X20000     /* m_stmflash(STM32内部FLASH)大小,H750是128KB */
    " m: }# M1 W% d: d% m

  21. ( p' J( {9 ~* S5 Q8 g- z6 J) A
  22. #define m_qspiflash_start   0X90000000  /* m_qspiflash(外扩QSPI FLASH)域起始地址 */
    # r* G! v. O8 E5 s) o
  23. #define m_qspiflash_size   0X800000    /* m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB */
    ( m& x' \1 S/ @& q4 h- P, e4 |

  24. 5 g0 e# d) M9 _3 ]9 E7 ?4 k7 x
  25. #define m_stmsram_start   0X24000000  /* m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM */1 v+ K) p3 U- N5 @1 A0 k
  26. #define m_stmsram_size   0X80000     /* m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB */  Q* b  p: A+ D/ X! J

  27. . o1 v7 ^, ~% ~. L3 s# x% B
  28. LR_m_stmflash m_stmflash_start m_stmflash_size          /* LR_m_stmflash加载域  */& R" S- S5 F5 m( ^3 J  I5 u; F
  29. {3 v) M* r/ k& W
  30. /* ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size  */
    0 M& e! w! Y/ m0 s7 u/ k
  31. ER_m_stmflash m_stmflash_start m_stmflash_size {   
    / I% v3 I# }4 e5 T! T3 j
  32. /* 优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start */
    1 T: f3 i& C/ @3 V0 G5 W: ]
  33.       /* RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的. */! H4 \6 [/ A1 `2 G  m
  34. *.o (RESET, +First)                            " I* ^# D/ [0 m3 E
  35.         /* 将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等 */
      S/ z9 ]0 z* ?, F: ]; r8 Y
  36.         * (InRoot$Sections)                           
    " D( @9 H5 V& v( w) C, c# h- O3 l% B
  37.         * (Veneer$Code)+ p$ V- D- H7 E1 Y8 e
  38.         libinit.o
    ( Y1 s5 Z, a0 U# J2 \) s
  39.         libinit2.o
    # w- ^' K' h9 `* Y; I% Y
  40.         libshutdown.o2 E: ?9 e* v7 }; [
  41.         libshutdown2.o8 w7 P6 s5 [: J
  42.         __rtentry.o7 Q& `" U4 G% `9 P
  43.         __rtentry2.o  h/ o7 q/ n7 E0 D# z! b
  44.         __rtentry4.o
    4 z  g' L# s- W7 z
  45.         rtexit.o
    . n7 n: ^* u; e  k. }/ U" v  d
  46.         rtexit2.o
    8 s% H9 X9 G( \# V5 c( H
  47. * w  x) q3 q( R# \
  48.         use_no_semi_2.o
    & `/ F0 Y& x; A" p! ]
  49.         heapauxi.o& y+ T- K5 s6 t
  50.         use_no_semi.o+ y, k' u  c0 z
  51.         sys_stackheap_outer.o7 _+ b) P& `# m; ?3 r( t1 a
  52.         exit.o' e+ Q/ O' y/ Z6 i( V- H$ k
  53.         libspace.o! `7 r- o1 m/ \9 X8 v$ \
  54.         fpinit.o
    7 L  t: `4 S* p* w' @) J
  55.         lludivv7m.o& M. C) e/ b6 `4 r  k& t$ n! ]/ @
  56.         startup_stm32h750xx.o5 V5 O$ j9 [/ r- E! c3 B5 m6 l
  57. : o% q+ f: m' m$ T* j
  58.         rt_locale_intlibspace.o- c( ?+ ~1 a. B  X; z! s' U
  59.         lc_numeric_c.o/ S; ?* H; |9 F" l
  60.         lc_ctype_c.o. g9 O( J6 Y0 u" O
  61. - N$ T- o) P- K& t
  62.         main.o9 Q) s. @3 [6 a2 S9 g$ N5 W
  63.         sys.o
    ! z  v5 P3 N2 f9 ]$ S. H8 A
  64.         usart.o
    ; H- U7 \+ k; w: x
  65.         delay.o* J; ]7 e9 r2 W# C9 w  J; t( _. g
  66.         pwr.o: _) W" o- y( B  m% L
复制代码
+ Q" M. \( r& q& `3 C" j
   /* H7的QSPI接口不支持读时写,因此必须把以下3个文件放到内部FLASH,以保证可以
' m) \$ M- }, G. Q5 v对QSPI FLASH的写入 */3 c) E& s2 U8 ?/ z$ v
1 C7 t3 x1 D6 e( C% N4 ?" O
  1.         qspi.o
    8 ~2 S3 T- m# }" n5 z$ i  Y
  2.         norflash.o2 N. ]2 D8 g: A
  3.         norflash_ex.o5 S! }0 t+ P* V* }* A% O' N: ?, m3 c
  4. 2 G+ \1 e+ y; _9 g4 u$ K
  5.         /* 针对HAL库驱动添加到内部的文件 */- F  |. P4 v! ^/ L
  6.         system_stm32h7xx.o3 S% u# `8 g& F; h! k
  7.         stm32h7xx_hal.o
    3 N3 I/ v) |& p% F9 V3 c4 f
  8.         stm32h7xx_hal_cortex.o
    ) g$ ~( w( V5 P' [/ G2 {
  9.         stm32h7xx_hal_rcc.o: n. ^" y: O! J" j1 Y. ^5 ^
  10.         stm32h7xx_hal_rcc_ex.o* q4 O/ M" G$ b
  11.         stm32h7xx_hal_gpio.o
    % Q/ ^5 `3 p/ O/ l# A; ]7 {
  12.         stm32h7xx_hal_dma.o
    0 G$ @3 T8 W, E& s' o& Z$ q
  13.         stm32h7xx_hal_dma_ex.o
    ' h4 j% D+ |  D5 x  r) k
  14.         stm32h7xx_hal_qspi.o
    6 D0 @0 g/ F& ~. r" F  ?7 m6 [
  15.         stm32h7xx_hal_pwr.o
    - y% y$ H: n5 V  B& `
  16.         stm32h7xx_hal_pwr_ex.o
    ( Y1 J5 {; i4 K1 o8 }; c
  17. }
    . G0 N2 d' e) v4 i
  18. /* RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size. */
    * \6 D5 _  |. R2 j4 V
  19.     RW_m_stmsram m_stmsram_start m_stmsram_size {      
    5 h1 p% P; f2 {1 @2 |) \
  20.         .ANY (+RW + ZI)                           /* 将所有用到的RAM都放在这个区域 */8 A: m2 C( C% v0 Y9 J2 m; q
  21.     }& g9 T0 _0 I7 w6 V9 o1 n
  22. }  T) w" ]1 P3 Q: A  d
  23. : B$ x0 f: z# i2 S; f
  24. LR_m_qspiflash m_qspiflash_start m_qspiflash_size       /* LR_m_qspiflash加载域 */2 P7 Z8 ?: ?. e5 V" O: ?. K
  25. {
    3 a& p8 Q" E! P/ \9 K# [
  26. /* ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size */
    9 m% _9 ~4 y9 J, }8 n
  27.     ER_m_qspiflash m_qspiflash_start m_qspiflash_size { 3 ?5 F# Q$ g( B& I7 Z8 u
  28.         .ANY (+RO)     /* 将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的 */$ f0 a/ C! p  }2 ?* ^7 D5 m
  29.     }( \) \! \, {! S3 X7 w- A) `+ F
  30. }
复制代码
  j' W- K' z" _) D, ~2 M
相比于MDK自己生成的分散加载文件,我们自己编写的相对复杂一些,qspi_code.scf分散加载文件包含2个加载域,3个运行域,分别是:
/ m% b( D6 w0 p  mLR_m_stmflash加载域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。它包含二个运行域分别是:
* f/ s5 p0 F; Q6 dER_m_stmflash运行域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。1 z0 k: |! k6 l% @) S
RW_m_stmsram运行域,起始地址为:m_stmsram_start(宏定义,实际值:0X2400 0000)大小为:m_stmsram_size(宏定义,实际值:0X80000)。
7 P: C# r0 @% S( n) k) xLR_m_qspiflash加载域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。它包含一个运行域:7 b' v7 ~  C4 v; M3 r6 O
ER_m_qspiflash运行域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。( ^0 ~6 \+ \% N; M3 K; Y7 Y
具体的存储器分配情况为:
$ Q/ I# a; Z5 P$ X8 k; X, wER_m_stmflash运行域,包含:*.o (RESET, +First)开始到delay.o结束的相关代码,这些代码运行在内部FLASH,可以得到最佳的性能。需要注意的是:这些代码大部分都是必须放到内部FLASH,否则无法正常运行!!
) X* h) u) n6 i6 i' i) b5 ^ER_m_qspiflash运行域,所有没有在ER_m_stmflash运行域指定的代码,都被放在这个运行域,这些代码运行在外部SPI FLASH,速度比内部FLASH慢一些。1 k# z* I- L7 A* e/ x
RW_m_stmsram运行域,所有变量及堆栈(RW Data + ZI Data)都存放在这个运行域。; i8 R. o% Z8 `; L4 O
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct文件,特意将.ANY ROM区域放在外部SPI FLASH,这样大家在新增.c参与编译时,默认就是存放在外部SPI FLASH的,这样使用起来就更方便。
! J3 W$ r) [$ ^4 ]
/ m: {3 ]  }% F注意事项:6 ~# @+ H2 q) k) ?4 J# U
1、如果你新增的代码,对速度有要求,可以将其对应的.o添加到内部FLASH,即放在:ER_m_stmflash运行域。& x" @9 f- Q8 m( }
2、如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增的.o放到ER_m_stmflash运行域后(重新编译)再尝试。如果还不行,可以尝试将所有代码都放到ER_m_stmflash运行域后再尝试。6 K4 x% E% J9 M+ Z- E& r. }. z
至此,分散加载文件就给大家介绍完了。  O% e3 T. W/ d/ [2 X2 x% u
————————————————$ o2 R6 {0 ?" O0 T0 d% h
版权声明:正点原子
0 ?# O  `& A1 K4 C* o% R+ f9 c1 s( s5 _' i/ T( e0 J- \

; q- D$ b) n; v8 F  p9 }4 @: w3 G: E: I% N* y6 k8 M
收藏 评论0 发布时间:2022-10-5 21:06

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版