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

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

[复制链接]
STMCU小助手 发布时间:2022-10-5 21:06
新建HAL版本MDK工程) u) [; _, n8 Q8 z
在前面的章节我们介绍了STM32H7xx官方固件包的一些知识,本章我们将讲解新建HAL库版本的MDK工程的详细步骤。我们把本章新建好的工程放在光盘里,路径:4,程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,大家在学习新建工程过程中间遇到一些问题,可以直接打开这个工程,然后对比学习。% {1 P6 M* B2 C7 m# ?2 m: [, K

" x3 Z- J% `+ q# a7 I8.1 新建HAL库版本MDK工程
) N6 J+ A3 v3 E1 g本节我们将教大家如何新建一个STM32H750的HAL库版本MDK5工程。为了方便大家参考,我们将本节最终新建好的工程模板存放在A盘:4、程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,如遇新建工程问题,请打开该实验对比。
+ m7 M' ]. G* o: z5 ?5 \8 I( r整个新建过程比较复杂,我们将其拆分为5个步骤进行讲解,请准备大概2个小时时间,耐心细致的做完!对你后续的学习非常有帮助!6 P2 p$ z, Y& ~& m1 Q' w6 L
在新建工程之前,首先我们要做如下准备:
1 y3 W: E* H3 D. |7 v  S1 X; R4 ?1、 STM32Cube官方固件包:我们使用的固件包版本是STM32Cube_FW_H7_V1.6.0,固件包路径:A盘8,STM32参考资料1,STM32CubeH7固件包。
$ S& J7 e, V$ v2 z3 B3 L2、开发环境搭建:参考本书第三章相关内容。
* Q$ J& t: E9 @  G8.1.1 新建工程文件夹
/ `" r" \/ e; g/ J4 L9 ^. I( c5 ]' l, }3 v新建工程文件夹分为2个步骤:1,新建工程文件夹;2,拷贝工程相关文件。
; z  T5 u7 p. x2 O  _1.新建工程文件夹% W  V# l5 D+ j. \0 O4 ]4 z
首先我们在桌面新建一个工程根目录文件夹,后续的工程文件都将在这个文件夹里建立,我们把这个文件夹重命名为:实验0-3,新建工程实验-HAL库版本。如图8.1.1.1所示:% c2 E3 }) p- W8 i. U
c07d881ff02749af9842e8d3614198f4.png
, A& o4 G" {! k
( H0 S" O; @2 C: N1 b图8.1.1.1 新建工程根目录文件夹, |, K1 c. A$ `! B) w) ^
为了让工程的文件目录结构更加清晰易懂,我们会在工程根目录文件夹下建立以下几个文件夹,每个文件夹名称及其作用如表8.1.1.1所示:
( Q! W, G$ q/ i4 K! a' y3 t2 S3 x9 g. T- ~" z, Z
09bd521756de4e4ca5b578aa5591d9b2.png
& ?5 b% ?' S/ b6 O* m) K) S7 F1 ?
. w! }8 a; Z! t$ t表8.1.1.1 工程根目录新建文件夹及其作用$ z7 |, {% ~; p  f) H2 n* y8 t7 Q
新建完成以后,最后得到我们的工程根目录文件夹如图8.1.1.2所示。9 B* `! O  p+ t5 s
" J2 F, ^) z/ V7 r) g$ g
d83446698d7e4a6181e1d40778b8742c.png 4 p; [+ [7 N) r6 W+ E
% o! m' {: G1 K" O
图8.1.1.2 工程根目录文件夹" a4 P( \( Q# w7 p' U& T7 I, R; t
另外我们的工程根文件目录下还有一个名为keilkill.bat的可执行文件,双击便可执行。其作用是删除编译器编译后的无关文件,减少工程占用的内存,方便打包。还有一个名为readme的记事本文件,其作用是介绍本实验的各种信息。  y! p- g: }; e/ h: Q* I
工程根目录及其相关文件夹新建好以后,我们需要拷贝一些工程相关文件过来(主要是在Drivers文件夹里面),以便等下的新建工程需要。% x! W! W8 D  w/ }
2. 拷贝工程相关文件
% g5 \7 E5 j3 A( x/ G* n. Y1 Z' F接下来,我们按图8.1.1.2的根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。0 K2 y6 v+ b+ c5 ~; H2 M
Drivers文件夹
. t  h% \2 E" o. Z该文件夹用于存放与硬件相关的驱动层文件,一般包括如表8.1.1.2所示的三个文件夹:( [4 Z1 j$ t+ m* J
# V  l, N+ i/ w0 X2 ~9 R( z9 t6 C
a28caaec3fcb44039e3f6b6858907daf.png
" Z/ O- u; T5 K& N' G. ^0 o8 n7 w( c( }0 Q0 G4 |5 f% M6 {" L
表8.1.1.2 Drivers包含文件夹
+ F5 z/ i  p; qBSP文件夹,用于存放正点原子提供的板级支持包驱动代码,如:LED、蜂鸣器、按键等。本章我们暂时用不到该文件夹,不过可以先建好备用。
1 A  I1 Y$ }" ^# X) ]6 d% CCMSIS文件夹,用于存放CMSIS底层代码(ARM和ST提供),如:启动文件(.s文件)、stm32h7xx.h等各种头文件。该文件夹我们可以直接从STM32CubeH7固件包(路径:A盘8,STM32参考资料1,STM32CubeH7固件包)里面拷贝,不过由于固件包里面的CMISIS兼容了太多芯片,导致非常大(300多MB),因此我们根据实际情况,对其进行了大幅精简,精简后的CMSIS文件夹大小为2.3MB左右。精简后的CMSIS文件夹大家可以在:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。# i9 X( D9 T  _4 t) d1 i8 ]: ?
SYSTEM文件夹,用于存放正点原子提供的系统级核心驱动代码,如:sys.c、delay.c和usart.c等,方便大家快速搭建自己的工程。该文件同样可以从:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。1 P) \! @) k/ J( ?9 c
STM32H7xx_HAL_Driver文件夹,用于存放ST提供的H7xx HAL库驱动代码。该文件夹我们可以直接从STM32CubeH7固件包里面拷贝。直接拷贝“STM32CubeH7固件包Drivers”路径下的“STM32H7xx_HAL_Driver”文件夹到我们工程的Drivers下,只保留Inc和Src文件夹即可。- _; ?! H6 _5 D
执行完以上操作后,Drivers文件夹最终结构如图8.1.1.3所示:
  ]# y9 g) @; V5 D- D7 y  A6 l8 p6 b0 r0 g8 ~
89aca4a79e13457088ecf50d8d500e3d.png
" y# ]+ j0 E; k  D3 F% B' l( G# ?3 `, [3 N! p
图8.1.1.3 工程根目录下的Drivers文件夹
7 Z  d% C5 @& g关于工程根目录下的Drivers文件操作到这里就完成了,可以说步骤是有点多。在此过程遇到问题的话,请大家多参考我们提供的实验0-3,新建工程实验-HAL库版本工程,一步步操作。! Q9 n9 {  {3 H  D
Middlewares文件夹
# ?: B; j1 C, V0 J  v该文件夹用于存放正点原子和其他第三方提供的中间层代码(组件/Lib等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各种OS、各种GUI等等。本章我们暂时用不到该文件夹,不过可以先建好备用,后面的实验将会陆续添加各种文件。
, c* o/ ~- X4 w2 n2 dOutput文件夹0 G+ V* V% M% D; Y' V2 s
该文件夹用于存放编译器编译工程输出的中间文件,比如:.hex、.bin、.o文件等等。这里不需要操作,后面只需要在MDK里面设置该文件夹为编译过程中间文件的存放文件夹就行。
6 d: }( W- {( M' RProjects文件夹' l1 j# A4 t# E) J9 t
该文件夹用于存放编译器(MDK、IAR等)工程文件,我们主要用MDK,为了方便区分,我们在该文件夹下新建:MDK-ARM文件夹,用于存放MDK的工程文件,如图8.1.1.4所示:9 ], `7 H0 q  r6 C
+ N7 ?: O% _" n. s
2278dc824fc0410f95513f3bb54003ca.png
' q1 C8 G. ^9 Y9 R+ r; U+ ~9 M3 j4 C2 g
; F0 T" ?7 g; L$ n1 w$ g5 M图8.1.1.4 在Projects文件夹下新建MDK-ARM文件夹
' C8 y0 K) ^  p2 Z, t: s- yUser文件夹
2 }( L1 K1 U! l5 eUser文件夹用于存放HAL库用户配置文件、main.c文件、中断处理文件,以及分散加载文件。% I+ o. ~' y* T, P" W4 Z( K/ b
我们首先从官方固件包里面直接拷贝官方的模板工程下的HAL库用户配置文件和中断处理文件到我们的User文件夹里。官方的模板工程路径:STM32Cube_FW_H7_V1.6.0\Projects\ STM32H750B-DK\Templates\Template_Project,打开Template_Project文件夹,如图8.1.1.8所示。
( j! s1 x5 o" I% G* s/ ?' Y0 e; S0 `
b80bce4e05ec4463bca87c2ea099755e.png . ?8 t, w0 ?( Z4 X- Z1 X- u4 S+ F8 |

# H8 g, {& j. B2 N5 u1 H: ]2 b图8.1.1.8 官方模板工程根目录
1 h0 P' D/ Z" C. i8 d2 |我们需要的文件就在Inc和Src文件夹里面,在这两个文件夹里面找到:stm32h7xx_it.c、stm32h7xx_it.h、stm32h7xx_hal_conf.h这三个文件,并且拷贝到我们的User文件夹下。6 j# C/ {+ H/ J. ^% O% d* d  @
最后在User文件夹下新建一个命名为SCRIPT的文件夹,用于存放分散加载文件。分散加载文件直接在我们的实验0-2,新建工程实验-HAL库版本工程对应位置拷贝过来,后面再给大家讲解。main.c文件我们也是放在User文件夹里面的,后面在MDK里面教大家新建.c文件并保存。
' J) t& i  L, b/ j8.1.2 新建一个工程框架, Y0 ^$ [* b% B
首先,打开MDK软件。然后点击ProjectNew uVision Project如图8.1.2.1所示:" }+ G4 Y# V, e" A, ]1 C0 L
- a+ c+ G6 d: h
d7b3678492b147a4b3afd58c9fc1a07f.png 0 c$ J& w& M8 r2 {# |7 a

9 R, m/ v& l  Y9 h; c& d图8.1.2.1 新建MDK工程6 X  x! h( u6 i4 g
然后弹出工程命名和保存的操作窗口,我们将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:桌面实验0-2,新建工程实验-寄存器版本ProjectsMDK-ARM,工程名字我们取:atk_h750,最后点击保存即可。具体操作窗口如图8.1.2.2所示:" |) l; v" v8 `5 ^9 X7 u6 U2 v

8 E. Y3 G; B0 o' T/ J9 `/ H 6b119a5b802c457692bf3e22588dd8bc.png
1 {( I4 v8 P0 Q5 [; U2 u: E' H; L9 ]" f8 d8 @7 O7 `6 s
图8.1.2.2 保存工程界面& a4 X( `$ Y) j, R
之后,弹出器件选择对话框,如图8.1.2.3所示。因为MiniPRO STM32H750开发板所使用的STM32型号为STM32H750VBT6,所以我们选择:STMicroelectronicsSTM32H7 SeriesSTM32H750STM32H750VBTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件pack才会显示这些内容哦!!如果没得选择,请关闭MDK,然后安装 A盘:6,软件资料\1,软件\MDK5\ Keil.STM32H7xx_DFP.2.5.0.pack这个安装包后重试)。! M% z& k6 p& a- J' `0 l4 N+ U0 G
! h* I+ G( H9 D+ l( o+ U
c25315eb813e47abacc2163ff53014bb.png
/ U" O* [2 s4 U  V$ U+ h: i* K$ c
                       图8.1.2.3 器件选择界面
' H& Y& W0 I3 G* A- s- G# i4 d2 s& v% ]
1 t1 h! K1 S2 U点击OK,MDK会弹出Manage Run-Time Environment对话框,如图8.1.2.4所示:
9 @6 ?1 @% Y2 Q9 g  G# k2 E# Y1 d) c( m/ P( V; P9 ?+ c, M+ m
840c8d7777c748d4ba3c8480058b0bc9.png : ^+ F7 x# {- W' N; I- N9 z
+ T* e5 u6 M/ s; ^. P6 E5 ^$ m6 R5 W
图8.2.1.4 Manage Run-Time Environment界面5 h8 n7 k1 B# d! p- z5 [
这是MDK5新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。所以在图8.1.2.4所示界面,我们直接点击Cancel,即可,得到如图8.1.2.5所示界面:3 P/ M! [1 F5 b5 p3 D/ \
0 b# G% K/ p1 C/ r. Z
01c6e443fd774a919f4afe3eff0d363e.png 2 x( e, a' E1 |" ?
* Y2 J" G2 C2 F1 c, f0 V
图8.1.2.5 工程初步建立
& c& k7 a  ~7 ^  o/ q) {0 @此时,我们打开MDK-ARM文件夹,会看到MDK在该文件夹下自动创建了3个文件夹(DebugConfig、Listings和Objects),如图8.1.2.6所示:
& p+ h& Z: ?# M5 P+ {1 L
) F$ o# G* Q, ]/ b! Y1 [% } cf22cd3c423d441398f1363d26b3d632.png ( g. G# z" J5 c, h) N( K  E, f' F  k

$ {: m0 g2 N3 z3 [6 @( l5 |图8.1.2.6 MDK新建工程时自动创建的文件夹1 a1 l0 u+ z/ C
这三个文件夹的作用如表8.1.2.1所示:
& ~" r# Y9 m% G6 r# ~# O, O7 I& F3 }$ x/ R1 @0 F3 e% _
5021687c5d93429da588e41d6ac541f5.png ! N6 l  P, `$ b; R7 B  x  N( H

* Q( |  C5 z7 D7 Y0 C$ @, @表8.1.2.1 三个文件夹及其作用
, f) [, M) W7 k4 o# R编译过程产生的链接列表、调试信息、预览、lib等文件,统称为中间文件。为了统一管理,方便使用,我们会把输出在Listings和Objects文件夹的内容,统一改为输出到Output文件夹(通过魔术棒设置),我们先把MDK自动生成的这两个文件夹(Listings和Objects)删除。. x7 x9 c3 a: T0 Q
至此,我们还只是建了一个框架,还有好几个步骤要做,比如添加文件、魔术棒设置、编写main.c等。
. d9 I) I2 x' C! x" p# _9 p1 d8.1.3 添加文件
6 t  y! K& Q+ ]0 M$ W本节将分5个步骤:1,设置工程名和分组名;2,添加启动文件;3,添加SYSTEM源码4,添加 User 源码;5,添加 STM32H7xx_HAL_Driver 源码。0 ^) u. @5 u2 p6 K
1.设置工程名和分组名3 B9 d1 ^) K4 Z2 X
在ProjectTarget上右键,选择Manage Project Items…(方法一)或在菜单栏点击品字形红绿白图标(方法二)进入工程管理界面,如图8.1.3.1所示:' X, D* n3 f( I1 @
5 ]) I+ S" ~7 K- W6 I2 |2 ~
43f3eccd0c0a49038e00b8140982a73a.png 7 o; e% ]# y% Y; ]& [9 t& D/ i

' D' {) T3 o- S6 @8 u) B  q图8.1.3.1 进入工程管理界面
3 T$ f  d8 [. Q( M. W# A/ Z在工程管理界面,我们可以执行设置工程名字(Project Targets)、分组名字(Groups)以及添加每个分组的文件(Files)等操作。我们设置工程名字为:Template,并设置四个分组:Startup(存放启动文件)、User(存放main.c等用户代码)、Drivers/SYSTEM(存放系统级驱动代码)、Readme(存放工程说明文件),如图8.1.3.2所示:2 _, M2 A% ^( n) q6 n

4 h2 i* Z: Q0 W1 L( h- r4 ]$ N fd29f722719047778a894ec11dc15ac3.png ) }- X# P5 T) O
( h2 t9 O0 p. ?
图8.1.3.2 设置工程名和分组名
" s* z+ W! _! Y* L- J设置好之后,我们点击OK,回到MDK主界面,可以看到我们设置的工程名和分组名如图8.1.3.3所示:# B' W  h3 N8 l" N8 K1 b  A

" b+ u1 s0 s) [" v- b& L2 f9 ~( } 9575821896784b26965cf3d769e26165.png
9 d+ D! C  S. S( V* a4 O2 P
4 H/ X& X8 Y  W: W+ a8 H) W" E) A5 L图8.1.3.3 设置成功
4 g; I: t$ ?4 w( y3 \这里我们只是新建了一个简单的工程,并没有添加BSP、Middlewares等分组,后面随着工程复杂程度的增加,我们需要一步步添加对应的分组。1 G/ k+ i  ]5 d; V; s0 A- R
注意:为了让工程结构清晰,我们会尽量让MDK的工程分组和我们前面新建的工程文件夹对应起来,由于MDK分组不支持多级目录,因此我们将路径也带入分组命名里面,以便区分。如:User分组对应User文件夹里面的源码,Drivers/SYSTEM分组,对应Drivers/SYSTEM文件夹里面的源码,Drivers/BSP分组对应Drivers/BSP文件夹里面的源码等。! m- e: x# u1 V  ?
2. 添加启动文件8 L$ q" N  `$ t
启动文件(.s文件)包含STM32的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用main函数等,是每个工程必不可少的一个文件,我们在本书第九章会有详细介绍。
9 q, ]9 a! y0 V( D! `0 I6 _; J# B启动文件由ST官方提供,存放在STM32CubeH7软件包的:DriversCMSISDevice STSTM32H7xxSourceTemplatesarm文件夹下。因为我们开发板使用的是STM32H750VBT6,对应的启动文件为:startup_stm32h750xx.s,为了节省空间,在精简版CMSIS文件夹里面我们把其他启动文件都删了。" t) ?# T, S+ I  |! y
关于启动文件的说明,我们就介绍这么多,接下来我们看如何添加启动文件到工程里面。我们有两种方法给MDK的分组添加文件:1,双击Project下的分组名添加。2,进入工程管理界面添加。7 b( v/ u" J& c" J) F
这了我们使用方法1添加(路径:实验0-3,新建工程实验-HAL库版本\Drivers\CMSIS* i; S1 V" Q$ V: _! C/ @
Device\ST\STM32H7xx\Source\Templates\arm),如图8.1.3.4所示:8 |6 Z* D. ]2 R* [
; {& F; G/ P. m0 Y
faa546005287411f83b7561ea1f6003a.png ! L3 d5 c2 d3 i4 K7 c! h# k

8 T" r( i1 ?) _+ K7 T) v: O+ m! u2 [图8.1.3.4 双击分组添加启动文件(startup_stm32h750xx.s)7 R& Z' U; D4 r$ U. `6 s( ]
上图中,我们也可以点击Add按钮进行文件添加。添加完后,点击Close,完成启动文件
1 c% p  Y$ M; ~  w) A! K& h' h/ S: S# _添加,得到工程分组如图8.1.3.5所示:+ [, Y, w& r0 F( X/ R3 |
% y5 a8 C8 n6 O( a  {
8647718b231747e6a03964b009c2efa6.png 8 T; G9 x+ M# {( [" P$ k9 O( a

- N: `+ j1 H( b; p+ R图8.1.3.5 启动文件添加成功
2 L  M5 q0 F8 D9 D' R1 R  a+ m1 L3. 添加SYSTEM源码# r$ F6 U7 i' a: D& @
这里我们在工程管理界面(方法2)进行SYSTEM源码添加。点击:按钮,进入工程管理界面,选中Drivers/SYSTEM分组,然后点击:Add Files,进入文件添加对话框,依次添加delay.c、sys.c和usart.c到该分组下,如图8.1.3.6所示:
0 ?6 W5 }% ?. i) w1 A
& S9 K) ^% o* g 884e3508a6444b5cb08f234ae2a0499b.png
0 W0 d$ v9 [$ _8 ^. i: s3 e2 d% u: P7 ~
图8.1.3.6 添加SYSTEM源码
' D- Z4 ~/ p& O/ _9 y3 E, h0 ^( p+ t1 X/ E注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.7所示:) t" V0 {; u5 ]5 e
; W$ l. i% b3 I
a9bc3490a642456e8e6c7062e85dceaf.png   E  d5 @: e$ I! X1 J3 T

' S$ M7 S) l; y: @图8.1.3.7 SYSTEM源码添加完成
, M6 |6 W  N& z1 {5 Z0 R5 `4. 添加User源码
  f: k) B4 h5 h# `6 }0 i/ c* k. M这里我们在工程管理界面(方法2)进行User源码添加。点击:按钮,进入工程管理界面,选中User分组,然后点击:Add Files,进入文件添加对话框,依次添加stm32h7xx_it.c和system_stm32h7xx.c到该分组下,如图8.1.3.8所示:
2 i. h; v: _. E; f8 X: q+ Y5 F* K8 J1 i: b9 H
83627a883d394387b9a69b3f97ca5d0d.png * w1 a) v' n6 ?# M
0 G, R. h" Q( Z( i1 ]
图8.1.3.8 添加User源码1 d# P# a( |3 e: g4 @! Y' y, y0 I' z
注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.9所示:
2 Z% Z' _. F" a1 D1 M, V( ^
& E( S9 }; G4 x4 \' C c7f798b6bbac4d329ef22a34f0da7a35.png
8 R' [  ?+ K  f$ {# u) l  q0 m8 e- \
图8.1.3.9 User源码添加完成
7 F& l% O1 {0 L2 P5. 添加STM32H7xx_HAL_Driver源码" W3 P* f. x4 s3 v7 c1 |2 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所示:
* b4 i8 W2 l( X5 t9 M3 Z6 `$ H# |- m1 [( F5 k! _
5f1b4fe0439b4dfea7d7dc9ed6ceb634.png
( L- S# X7 t& N) C. R! V; ]3 H6 h
: I+ u! K% S7 B7 K图8.1.3.10 添加STM32H7xx_HAL_Driver源码: N! L$ h% G2 Y1 J! G
添加完成后,如图8.1.3.11所示:
0 a+ i1 z, K! C5 v! H1 V7 L3 z
; Q* s) e7 N+ f) R! } bcb17b5b492a4a80a630f98abbad546e.png 1 {/ J9 w* _5 _  ~- Q' \

2 }" g8 A9 w" ^- a图8.1.3.11 STM32H7xx_HAL_Driver源码添加完成0 z& O. p  j  q, T$ e, g4 ~
可以看到分组中有些.c文件有个小钥匙的符号,这是因为官方的固件包的文件设置了只读权限,我们取消只读权限就好了,方法如图8.1.2.12所示。
4 v, G4 @. R' R: B* C5 F
% ?' r' W" V+ A3 y cd4fff494965426d861a066a1ab4e118.png 7 N1 v4 R( y- H' }4 ?  H
6 O% J- ^$ @" Y8 b. @
图8.1.2.12 取消工程文件夹的只读权限: w. X* ~6 h; Z0 y$ Q
8.1.4 魔术棒设置
7 M6 j# g+ A( b: d3 `为避免编写代码和编译报错,我们需要通过魔术棒对MDK工程进行相关设置。在MDK主界面,点击:(魔术棒图标,即Options for Target按钮),进入工程设置对话框,我们将进行如下几个选项卡的设置。
4 x) A6 W6 R/ Y* K1.设置Target选项卡
7 K5 `2 }) H) {/ Q( f  T在魔术棒Target选项卡里面,我们进行如图8.1.4.1所示设置:& d4 M* z5 H: ^. d7 w0 |" D: s
. d2 P" o) O% k3 G6 F+ i2 m, m) p
aa079a0**74261b7926c03ee69b908.png $ N2 \8 ]' ]0 Q- b% N

- b1 C4 H& ?) g) u2 c* g图8.1.4.1 Target选项卡设置7 q4 [* @. X. l2 t9 c  q
上图中,我们设置芯片所使用的外部晶振频率为8Mhz,选择ARM Compiler版本为:Use default compiler version 5(即AC5编译器)。% r' l* M4 }$ j
这里我们说明一下AC5和AC6编译的差异,如表8.1.4.2所示:
' W- a# N1 e) H& s, W( r# j+ f. X
# X7 w! c0 e& B4 @ ec16655ac2564428a4614ce6ade99f64.png
$ {, b5 W  ]  m* }0 G
9 F7 y% l1 w. f表8.1.4.1 AC5&AC6简单对比7 f$ U% j' U) S0 a- q
由于AC5对中文支持比较好,且兼容性相对好一点,为了避免不必要的麻烦,我们推荐大家使用AC5编译器。为了让大家自由选择,我们正点原子的源码,也是支持AC6编译器的,不过在选项卡设置上稍有差异,具体差异如表8.1.4.2所示:
, G0 _/ o9 ^6 c, P1 G1 _  i" Q( W1 m6 F+ _) q4 s+ E* P* z) p4 \# u
ac1ed12294bf4652b48a2b8b2cfdd5ba.png
- G2 w' S; A) p1 P$ E8 M  C8 ^0 |- x7 P
表8.1.4.2 AC5&AC6设置差异
. T1 ]' F7 k  M2. 设置Output选项卡# x5 s4 W. B: B
在魔术棒Output选项卡里面,进行如图8.1.4.2所示设置:8 P  J" `5 G2 I* Y7 q) `0 W& I4 I: \
0 D& b; N: W. a/ W% O! I0 n
eb9687db7b964a33bad72c5485588208.png
8 c/ M' v" s$ W: w5 A
8 i* c' C9 r1 i, H图8.1.4.2 设置Output选项卡, g4 d) H* I5 V( `3 n
注意,我们勾选:Browse Information,用于输出浏览信息,这样就可以使用go to definition查看函数/变量的定义,对我们后续调试代码比较有帮助,如果不需要调试代码,则可以去掉这个勾选,以提高编译速度。
0 A/ J9 y2 O2 x2 w( i3. 设置Listing选项卡1 f' D! c4 z# }; Z
在魔术棒Listing选项卡里面,进行如图8.1.4.3所示设置:
% [, b6 U4 a- b6 n4 h1 G8 U! {9 Z! h1 P2 p# E9 {$ `+ f
c997f30d6c3d4504a4ec90f2c42edb29.png
% Z# V% [. D7 G* N% N& E
+ Q  K, d7 ~* [! C图8.1.4.3 设置Listing选项卡2 n5 B" Q+ [+ A. P
经过Output和Listing这两步设置,原来存储在Objects和Listings文件夹的内容(中间文件)就都改为输出到Output文件夹了。( |( U$ }4 N4 j1 B5 G7 g
4. 设置C/C++选项卡
4 j+ g+ L, ^3 d( v/ _) r在魔术棒C/C++选项卡里面,进行如图8.1.4.4所示设置:$ m( A) b+ Y6 a9 h/ R
, O  `/ f! ^, D# E$ E9 G+ x
ff4c06ba4f6742998cfa29c00c5ae22f.png 2 t. s8 y9 N: W3 @! r/ N( ?. b  }

, V& p( q4 W; _' Y* A$ O% I# O) A8 P  U图8.1.4.4 设置C/C++选项卡
/ M. D- A* l0 ^- w/ k( W在②处设置了全局宏定义:STM32H750xx,用于定义所用STM32型号,在stm32h7xx.h里面会用到该宏定义。
7 R; T4 G6 f) D' u# t% `在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6编译器的时候,这里推荐默认使用-O1优化。
# O6 G5 B5 ^/ B; \" K4 o/ b在④处勾选C99模式,即使用C99 C语言标准。: }) l7 ^2 W6 c
在⑤处,我们可以进行头文件包含路径设置,点击此按钮,进行如图8.1.4.5所示设置:
/ o' B! Q; q3 O/ i6 n/ r
6 j$ T& p$ ~& e# Z5 o: r 042d59760099402eb057ab905f45cf9e.png 8 \* d+ h) t* k8 p% F2 j: ~
! |) @  S& y  O
图8.1.4.5 设置头文件包含路径0 X! ~+ R! z1 A' j( j
上图中我们设置了4个头文件包含路径,其中3个在Drivers文件夹下,一个在User文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的include全部使用相对路径,也就是我们只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码,如果全部是使用相对路径,则无需再设置头文件包含路径了,直接在include里面就指明了头文件所在。
! g8 h2 U/ ~: d- G: B关于相对路径,这里大家记住3点:
3 J5 L  x1 c+ h1,默认路径就是指MDK工程所在的路径,即.uvprojx文件所在路径(文件夹)
  W3 d( [+ f- N; D$ R* L  U8 x# e* c2,“./”表示当前目录(相对当前路径,也可以写做“.\”)
7 d9 l1 l2 a2 k0 m6 y2 K* K/ K, E% R. _3,“…/”表示当前目录的上一层目录(也可以写做“…\”)! l6 J; D, E3 `* X- M5 _( @6 _
举例来说,上图中:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,前面两个“…\”,表示Drivers文件夹在当前MDK工程所在文件夹(MDK-ARM)的上2级目录下,具体解释如图8.1.4.6所示:
9 @5 v: J( X, E9 r& J, _0 j* G$ `- t! v" M! r1 O
9958cf50da964d39ac15e517a4bdc8dc.png
. D% Z4 d: d: a- q, z
  D# a- P0 l9 J, G图8.1.4.6 …\Drivers\CMSIS\Device\ST\STM3H7xx\Include的解释5 |6 _7 [# d' W5 o7 ^. H
上图表示根据头文件包含路径:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,编译器可以找到⑥处所包含的这些头文件,即代码里面可以直接include这些头文件使用。0 N, C- p7 A) K/ y+ E. u$ T+ I0 s
再举个例子,在完成如图6.1.4.5所示的头文件包含路径设置以后,我们在代码里面编写:1 p  l- n( U# E: Q0 V0 ^
#include “./SYSTEM/sys/sys.h”# J2 u8 T: [$ w$ @( @# j3 m; @" K
即表示当前头文件包含路径所指示的4个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h的路径,实际上就是在Drivers文件夹下面,两者结合起来就相当于:
. r' U7 w# B, H# U#include “…/…/Drivers/SYSTEM/sys/sys.h”
0 _  `" _) N  V# A这就是相对路径。它既可以减少头文件包含路径设置(即减少MDK配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在那个文件夹,因此我们推荐在编写代码的时候使用相对路径。8 u2 w' q( ~) O4 S6 A2 H
关于相对路径,我们就介绍这么多,大家搞不明白的可以在网上搜索相关资料学习,也可以在后面的学习,分析我们其他源码,慢慢体会,总之不难,但是好用。- r0 E0 X' G, a$ D& Y4 u) h
最后,我们如果使用AC6编译器,则在图6.1.4.4的Misc Controls处需要设置:-Wno-invalid-source-encoding,避免中文编码报错,如果使用AC5编译器,则不需要该设置!!. i3 {, n' x) k' ~: K) `4 S$ j
5. 设置Debug选项卡
. @( N0 J$ r# P4 h9 O( Y2 [  a在魔术棒Debug选项卡里面,进行如图8.1.4.7所示设置:7 o% o, z2 T2 Z" A, ~7 `. t

: h5 i5 ~, J! x$ ^  a. o 663ae5fcb1cb4a64abe8ee0f5360fdeb.png " U- |8 V& B) v1 E* j- o/ V+ \

8 Z- m/ U" _6 l  I1 a. {- D" E- u图8.1.4.7 Debug选项卡设置4 ~4 h0 i$ R, ]( k& T
图中,我们选择使用:CMSIS-DAP仿真器,使用SW模式,并设置最大时钟频率为10Mhz,以得到最高下载速度。当我们将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在SW Device窗口显示芯片的IDCODE、Device Name等信息(图中⑤处),当无法找到时,请检查供电和仿真器连接状况。/ Q+ |: Q/ c' L3 j/ H" m5 Y( E
6. 设置Utilities选项卡
7 k- k# }" C; L在魔术棒Debug选项卡里面,进行如图8.1.4.8所示设置:9 n1 B1 z; Q5 P4 q* r4 D/ I
* H( d2 m+ K) ^9 v, N
6cb0e659dc6846e5998d10fb61975fc7.png
1 X, o% I+ y' t# a) M1 }1 k: c2 _+ d& T0 R
图8.1.4.8 Utilities选项卡设置
7 {- }. j; O: m$ q6 D图中⑥处下载算法STM32H750,是MDK默认添加的,针对STM32H750系列产品。除此之外,我们还要添加算法,点击⑦处按钮添加即可。添加好算法后,设置算法使用的 RAM 地址和大小,这里设置的起始地址为:0X2000 0000(DTCM),大小为:0X3000。必须按这个大小设置,否则下载会出错(无法加载算法)。
- a$ h9 f! Y5 I' F1 N7. 添加分散加载文件
$ s2 l, ?6 ]! c4 M& s* A# p由于STM32H750VBT6芯片内部的FLASH的空间比较少(只有128KB)。对于大的工程,这个FLASH空间是不够用的,为了解决这个问题,同时方便后续工程的新建,我们统一使用分散加载的方式来决定FLASH内存的分配,而不用MDK默认的设置。关于分散加载是什么?我们后面8.3小节会讲解,请大家先跟着我们把新建工程完成。' q; m5 b/ V& z& F  a6 S
分散加载的文件已经为大家准备好了,可以在实验0-3,新建工程实验-HAL库版本\User\SCRIPT,或者在(A盘)/程序源码/STM32启动文件/分散加载_HAL库版本/SCRIPT中拷贝qspi_code.scf文件到我们的工程User\SCRIPT路径下,如图8.1.4.9所示。5 P3 d! k7 y' [& I+ h1 ?" [% ?5 U
( e( }8 k- t% F& M- ^6 q/ U* x
0362a607c804410ea055be3879718082.png
* r+ q. I: b5 D, U/ d( r
2 Z" N" W: @' ~0 b图8.1.4.9 拷贝分散加载文件到工程8 v) D( Z9 F# l0 i+ O
注意:这里的分散加载文件寄存器跟HAL库是不一样的,我们建立HAL库工程,所以必需用HAL库版本的分散加载文件。
1 v$ Q' f9 I8 }1 f, V: L' I4 ^接下来我们需要对MDK进行配置,相当于把分散加载文件关联到工程里。方法:点击魔术棒, Linker选项卡取消勾选:Use Memory Layout from Target DialogScatter File路径选择SCRIPT文件夹 选择qspi_code.scf文件,然后,在disable Warnings一栏,添加:6314,6329,屏蔽6314和6329这两个警告。如不屏蔽,当分散加载里面有某些段(section)没用到,则会报警告,所以我们需要屏蔽这两个警告。如图8.1.4.10所示。
# H4 O" \( M6 v% j( A! G% `: J1 w1 Q! y9 s9 _) r6 d
241659f1cccf4ec799607dbe4bcc40ed.png
8 z. E8 L2 b& k0 E/ W0 O/ T! E' R9 @& c$ J# T( Y
图8.1.4.10 添加分散加载文件  p, t2 i& {7 V. M$ P3 c. A2 v
至此,添加分散加载文件的相应操作就完成了。* W8 i: Z( O8 g0 J
8.1.5 添加main.c,并编写代码* B! ]5 H4 U# |
在MDK主界面,点击:,新建一个main.c文件,并保存在User文件夹下。然后双击User分组,弹出添加文件的对话框,将User文件夹下的main.c文件添加到User分组下。得到如图8.1.5.1所示的界面:
* M6 e+ X3 y+ P5 S; c. A6 y8 |, M( G! B, n0 Z3 X% K
b1dd0efba9964663a212af58f8129af6.png % L3 P3 {) C7 i

7 ~: r# @/ ], N- ~# h+ e图8.1.5.1 在User分组下加入main.c文件2 T8 k6 e$ {# x! v! e  t& Y# x+ `( m: K
至此,我们就可以开始编写我们自己的代码了。我们在main.c文件里面输入如下代码:) F$ k1 m& ?- F' p# J+ B

. @3 S$ p, c8 M9 p1 ?* M
  1. #include "./SYSTEM/sys/sys.h"
    6 A$ F& e- q+ J) U2 y
  2. #include "./SYSTEM/usart/usart.h"- f1 V6 y7 ]4 M3 X: t
  3. #include "./SYSTEM/delay/delay.h"/ P! v: _) A3 M+ A

  4. / h+ E0 y" G+ r3 b5 w- m
  5. void led_init(void);                               /* LED初始化函数声明 */
    / G( q9 n5 i1 b& T8 z- i
  6.   U2 E" E( }0 X: ~$ v
  7. int main(void)* j- m; U6 M( Y; V% S0 V7 v" U
  8. {
    % P4 I0 _& k6 i& }" F, x
  9.     sys_cache_enable();                            /* 打开L1-Cache */. c) [) a- o5 o$ R4 Q6 [1 T
  10.     HAL_Init();                                             /* 初始化HAL库 */' S! n- @. B8 @% |" H2 h
  11.     sys_stm32_clock_init(240, 2, 2, 4);                /* 设置时钟, 480Mhz */
    3 ~% r9 X" N  F* t# B
  12.     delay_init(480);                                       /* 延时初始化 */
    ! I$ @  h; [* ^5 a0 D
  13.     led_init();                                             /* LED初始化 */( r: H2 \$ R! e: O  h
  14.     while(1)- ~8 L# h5 y: n  @8 O6 d8 d8 Q% Z
  15.     { * u$ B, b/ k8 {4 `. g
  16.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);            /* PB4置1 */ 3 Z  p: m4 r$ J; e( q
  17.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);         /* PE5置0 */
    1 W0 U4 f' ~4 f
  18.         delay_ms(500);6 h- ]+ R* w1 A* g9 ~9 }: S1 R
  19.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);            /* PE5置1 */
    & [7 n* O) x) e; Q( ?
  20.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_RESET);         /* PE6置0 */: J1 P5 X) @; P, x$ @
  21.         delay_ms(500);
    ! T/ b0 `8 \2 p/ g
  22.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_SET);            /* PE6置1 */0 j9 G' @/ r  n  C; N7 c
  23.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);         /* PB4置0 */
    6 g9 V% {3 d. V! [" `- S7 _1 l
  24.         delay_ms(500);/ H: z' F' x( M: X) p
  25.     }
    3 C0 \4 k' D; ^/ n" ~
  26. }- s% F& O6 ]+ r/ h, E2 C3 K* I- E
  27. , M* I, m# _; W) F1 ?
  28. /**; m, m1 Q* c; Z4 F" g2 R9 G8 x' i
  29. * @brief       初始化LED相关IO口, 并使能时钟
    / L" G$ {  \5 e
  30. * @param       无4 f6 c( Z! ~, C
  31. * @retval      无8 M) C# f! H6 D# V0 \- o: n
  32. *// ^9 ?/ V( d# ^- s
  33. void led_init(void)
    * L  {( W! u9 B) P" X7 r/ u% D% `
  34. {6 Y& Q- g3 P! _, r6 X  a
  35.     GPIO_InitTypeDef gpio_init_struct;
    1 Y: V8 X* y0 J6 I8 l6 V  D; A
  36.     __HAL_RCC_GPIOB_CLK_ENABLE(); /* PB4时钟使能 */
    " d8 o3 z- E0 z5 O2 q% }* v/ b
  37.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE6时钟使能 */
    ) v8 Q6 \. [0 U
  38.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE5时钟使能 */
    ) P: P' E5 a* _- c* V* G7 I
  39. * r0 l- Y- g5 y) M
  40.     gpio_init_struct.Pin = GPIO_PIN_4;                                /* LED0引脚 */
    9 c  u. t; u: M9 }7 \; [  z% n* c/ O
  41.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */
    ; r- R( }! S4 P
  42.     gpio_init_struct.Pull = GPIO_PULLUP;                              /* 上拉 */" R0 o+ O1 z' G0 _
  43.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;            /* 高速 */
    $ }- I9 Z$ A1 [6 K* o% `4 o
  44.     HAL_GPIO_Init(GPIOB, &gpio_init_struct);                         /* 初始化LED0引脚 */
    * \& ]3 K2 f2 O# w, B( [

  45.   m. [$ N2 _0 |
  46.     gpio_init_struct.Pin = GPIO_PIN_6;                                /* LED1引脚 */
    ! |% G  ?* P' @* ?& M
  47.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED1引脚 */* n7 D4 q  ~( e- z# E0 N
  48. 6 M; k5 F' l+ M1 v. X( D0 a9 u, ~
  49.     gpio_init_struct.Pin = GPIO_PIN_5;                                /* LED2引脚 */1 M9 C. b) g5 n( J& h" P6 p
  50.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED2引脚 */2 F4 y, y& @( c6 y5 v
  51. }
复制代码
& B* v, a: S3 r7 E. O2 k
此部分代码,在A盘4,程序源码1,标准例程-HAL库版本 实验0 基础入门实验实验0-3,新建最工程实验-HAL库版本Usermain.c里面有,大家可以自己输入,也可以直接拷贝。强烈建议自己输入,以加深对程序的理解和印象!!  g8 A* \+ X9 |( b' z
注意,这里的include就是使用的相对路径,关于相对路径,请参考前面C/C++选项卡设置章节进行学习。: f2 S& H3 m( B) g& N# i; h
编写完main.c,我们点击:(Rebuild)按钮,编译整个工程,编译结果如下图所示:
4 c4 b" x( F- l
  _# `+ Q& h  o- k3 c& }图8.1.5.2 编译结果2 L0 b7 S" K3 V# y8 a
编译结果可以看到1个错误,0个警告。这个错误说找不到main.h,因为我们也不需要用到main.h,双击这个错误会弹出下面的stm32h7xx_it.c文件对应包含main.h的语句。我们只需要把它删除,然后重新编译,如图8.1.5.3所示。
3 _8 {. K7 S3 r. q4 H. j% h, D5 F- ~. Y1 A) {$ B
68deb7ad5549455cad3065d60e1f0432.png
* u* w( O4 C( a6 |3 \4 y4 n' \, n! T% D5 u
图8.1.5.3 删除包含main.h的语句% o* W7 H- Q2 D, ^& X6 C0 o
编译后发现又有一个警告,警告HAL_IncTick函数没有声明,如图8.1.5.4所示。
3 B" x( K2 A! {& y/ c9 d% J  ~, x2 ?2 j, w( a4 Y9 H- m
84622d45998148969e1d60df31ad2d5a.png
6 m. N& ~. v; `/ h: e+ T5 o( s
1 B: Z1 y+ R7 I3 G7 n- G$ y图8.1.5.4 编译报警告
  X/ F7 d, `1 {2 T: w因为这个函数是在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所示。) Z4 \9 X5 {* F! w
; W" l9 ~6 I+ \. E$ z
844b5d27125c4d708677dc0b95b9a39d.png 1 b; y* }5 M; W
7 i7 ^" Q1 {" b, v$ d5 Z6 x* g& N
图8.1.5.5 包含stm32h7xx_hal.h头文件到工程
8 P' G6 P  I' P* h% Q3 T, v# h再进行编译就会发现0错误0警告,结果如图8.1.5.6所示。1 q4 z4 S8 ?& ]+ E- n& t9 A; l9 r
' K6 ]  J* M7 Y& ]9 Z
0f3099f2b6e64aa286997c8756220211.png 5 g! [& v1 K0 S$ U$ e5 v  u0 }4 K

* n- v0 d4 ?; }图8.1.5.6 编译结果! _( X0 G! d$ {. D* J5 J+ C" r- b. _/ x
编译结果提示:代码总大小(Porgram Size)为:FLASH占用13520字节(Code + RO + RW),SRAM占用2008字节(RW + ZI);并成功创建了Hex文件(可执行文件,放在Output目录下)。
! }* d8 g+ [  b3 D& I总结:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到0错误0警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。
0 s+ w' m' I& }: I! j* \( F0 w另外,我们在Readme分组下还没有添加任何文件,由于只是添加一个说明性质的文件(.txt),并不是工程必备文件,因此这里我们就不添加了,开发板光盘的源码我们是有添加的,大家可以去参考一下。8 T2 H' V: g; j2 j! a( }( V6 U
至此,新建HAL库版本MDK工程完成。
* J  ?$ Z3 Q* g1 Q( B8.2 下载验证
6 v) [  T0 k$ V这里我们继续使用DAP仿真器下载,在MDK主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,如图8.2.1所示:
& V6 ?+ C! p  Q5 ?' k
( P8 S3 l0 q, _0 U- d c780acf342a14739aaffe4cac5f5d48f.png
# ?9 V1 w  e! m( `; B, f: f/ a3 b5 d/ H
图8.2.1 下载成功2 u  ?" L5 q5 ?3 [/ a" |' W
上图提示:Application running…,则表示代码下载成功,且开始运行。此时,看到RGB的红、蓝、绿三种灯轮流亮,类跑马灯。如果有朋友没能下载成功,请看第四章寻找问题,或者直接对照我们提供的实验0-3,新建工程实验-HAL库版本工程设置。& W. Y, A2 p8 z3 [; ]
2 w6 S4 W! p6 o5 ^# L9 k* {
8.3 分散加载文件简介! c" ]% v, J7 ]3 ^
ARM芯片用于在链接时指定存储器分配方案的文件,称之为分散加载文件(.sct),它可以将不同的代码(.o文件)放在不同的存储空间。关于分散加载的详细介绍,请参考:A盘8,STM32参考资料4,分散加载分散加载文件浅释.pdf,这是周立功公司的一份文档资料,详细介绍了.sct文件的基础概念、语法及应用实例说明,对学习分散加载非常有帮助,请大家务必先学习一下这个文档。
5 R5 v7 e( n' b; h" v9 |4 M5 d本节,我们仅对.sct文件进行一个简介,方便大家学习。. m8 |& V* ?  T; t6 B. q# c3 c
首先明确一个概念:MDK编译任何STM32工程,都会需要用到分散加载文件。分散加载文件的来源有两种方式:
5 b1 ]+ ]2 S- s) K: e1,通过MDK自己生成;
1 h4 p( }( D9 T' K2,通过用户指定(用户自己编写);8 F1 @' P/ V1 T* c
首先,我们来看MDK自己生成的.sct文件。
4 K. [- M3 T- |( q6 C选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.1所示的设置:
- }. v7 |, |/ @7 o! T8 E& W0 c$ Q% c
1b2f6b2ec058411b9335ca44636b00c8.png
" r# [' X  [0 E; `9 R5 G" D* L- Z( R) q6 {
图8.3.1 勾选Use Memory Layout from Target Dialog选项
( k0 P3 a: a+ v- {6 l) w* z# b勾选Use Memory Layout from Target Dialog选项后,MDK就会根据Target选项卡里面的相关设置来决定存储器分配,如图8.3.2所示:
2 T6 q/ G+ i! i" R! m0 |/ J- Y2 L4 g+ X4 s0 b- R. B3 E
07e46a153a124ba6bec9f183314042fd.png & w$ |$ b3 D) v1 D6 ^$ o  B
9 W8 M2 g/ K$ {9 L+ B
图8.2.2 MDK默认的存储器分配
7 A0 t6 D& X; e: L3 S标号①,是MDK的只读存储区域(ROM)和可读写存储区域(RAM)的配置区域。; f) P$ I- j  m- l2 {4 P
标号②处,说明默认将所有的代码(Code + RO Data + RW Data)都存放到IROM1指定的地址范围上。其起始地址为:0X0800 0000,大小为:0X20000。
" U$ h% [% r. J$ o标号③处,说明默认将所有的变量及堆栈(RW Data + ZI Data)都存放在IRAM1和IRAM2指定的地址范围上。IRAM1的起始地址为:0X2000 0000,大小为:0X20000;IRAM2的起始地址为:0X2400 0000,大小为:0X80000;变量的具体位置由编译器自动分配。" ]! L6 w% c0 _& n
在完成以上两步操作以后,对MDK进行一次编译,在编译成功后,MDK会自动生成一个以工程名命名的.sct文件,存放在Output文件夹里面,如图8.2.3所示:
- N3 W2 v) Y* M9 o6 B! `6 ?- F3 Y* w8 U4 i2 G/ a
ef00c14f4d1b4f88a3910a68e4d8deb1.png
4 Q# @# z6 }. u5 Z) M
0 B" p& F) Y, J1 b$ i$ ^" t图8.2.3 MDK自动生成的分散加载文件
! W8 S' C# g3 i7 @5 v打开上图中的test.sct文件,其内容如下:% z- f! P% K2 w" i; T' k/ b

& u% R/ O, H  S: v' d
  1. ; *************************************************************' p5 R" o, D5 A0 F* x: {
  2. ; *** Scatter-Loading Description File generated by uVision ***
    0 W/ a# |$ v" d' n1 P. s
  3. ; *************************************************************
    9 c, n8 u/ Y9 d8 z- t7 d

  4. 3 f! I* g% g' [( n0 K
  5. LR_IROM1 0x08000000 0x00020000  {    ; load region size_region% `1 }2 N' H2 j8 R2 R& j  R0 H6 X
  6.   ER_IROM1 0x08000000 0x00020000  {  ; load address = execution address& A+ ^5 S# ?  ^+ c) D
  7.    *.o (RESET, +First)
    5 I. O' _4 c1 |8 f5 `0 G% g
  8.    *(InRoot$Sections)2 Z* G. a$ K/ C- F: l8 [( T
  9.    .ANY (+RO)
    & O; M* O  w- \1 j  B8 e
  10.    .ANY (+XO)
    * \) e0 X3 m/ G4 f
  11.   }
    0 L/ Q5 i! Y7 r! N& C7 K! u4 }
  12.   RW_IRAM1 0x20000000 0x00020000  {  ; RW data
    & L8 S) e* B( f9 L# _
  13.    .ANY (+RW +ZI)
    8 X( B" Z3 B! h" ]2 [
  14.   }
    ' W9 J5 Y5 ]7 _4 j
  15.   RW_IRAM2 0x24000000 0x00080000  {
    ' D  _# w* |  [, C5 T( U- z- H# K
  16.    .ANY (+RW +ZI)% ?$ Z/ I% {5 s9 f, y
  17.   }9 h# H' {& K$ v- A1 a6 l6 ]
  18. }, P9 B, P$ U: e$ M0 C* z2 Y8 y
  19.         LR_IROM1是一个加载域,起始地址为:0X0800 0000,大小为:0X0002 0000。它包含三个运行域分别是:; ^* b* v" Y: G- Z4 \9 E
  20.         ER_IROM1运行域,起始地址为:0X0800 0000,大小为:0X0002 0000。
    ! Q0 {! [1 b" U3 s3 v4 Y
  21.         RW_IRAM1运行域,起始地址为:0X2000 0000,大小为:0X0002 0000。6 S. U4 l7 t" c, j
  22.         RW_IRAM2运行域,起始地址为:0X2400 0000,大小为:0X0008 0000。
复制代码

' }  A1 v2 T' ?8 T3 o, @其中:
( w/ m0 A# E/ [( g4 q( d9 J9 n
/ B: @$ ?* Z7 o5 I% x# V# sER_IROM1为ROM区域,存放:Code + RO Data + RW Data等只读数据,由:.ANY (+RO)指定,即所有只读数据,都存放在这个区域。8 ^4 E+ q& j+ X# e% }
RW_IRAM1和RW_IRAM2为RAM区域,存放:RW Data + ZI Data等可读写数据,由:.ANY (+RW +ZI)指定,即所有的可读写数据,都存放在这两个区域,具体存放位置由MDK编译器自动分配。5 s3 l, k. Q4 e9 q' R. u( T( L
*.o (RESET, +First):表示优先(+FIRST)将RESET(即中断向量表)段放这个域的起始位置,实际上就是把中断向量表拷贝到最开始的位置。
9 U' o$ @, X- H; ^, g
2 v8 l! g5 w( r: V/ I; x8 ?(InRoot$$Sections):表示将所有用到的库段放到root区,如:__main.o、__scatter*.o和__dc*.o等。
4 K% Z) J4 t, W5 D% |以上,就是MDK自动生成的.sct文件简介。
' m- d! I1 D, y, p0 `3 L接下来,我们看用户指定.sct文件的实现方式。选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.4所示的设置:
0 V5 v; e3 k# N" d' V# y
& g5 {- `( N1 a3 \4 Q( l 54ff6c39cc27474493884470322305e5.png & D# E; p5 U! m5 R

' x9 S% S. X) r& W' @图8.2.4 取消Use Memory Layout from Target Dialog选项9 Y% V( a2 M9 z% }  d
标号①,取消Use Memory Layout from Target Dialog选项,使用用户自定义分散加载文件。3 o% ]9 b2 r9 \/ f2 y* S4 f1 v& ?  T; \0 o# B
标号②,新建工程时,已选择SCRIPT\qspi_code_scf.scf分散加载文件。
: f: M6 O5 E9 o8 X( ^( F标号③,点击Edit按钮,即可在MDK里面打开qspi_code_scf.scf分散加载文件。# m# ]( t% E! |* w$ _
qspi_code.scf的内容如下:+ _; v; I; p/ P" b

! b1 R* A9 O. D# n: ?) I
  1. #! armcc -E
    & G5 c3 Q! l6 Y  S- i. V6 D
  2. ;#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc
    5 b! Y7 v7 e$ n2 D1 G$ _/ [! _4 C
  3. /* 使用说明 % i& z, v. g" \9 r! ?
  4. ! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, 用于AC6编译报错(L6709E错误)时,请使用此设置" ?' r1 l: c0 c- [$ m0 G- }( M
  5. !armcc -E, 用于AC5编译报错(L6709E错误)时,请使用此设置& r: |) d3 }/ l1 J
  6. 注意,设置必须放本文件第一行!否则还是报错!请注意调整顺序!
    ) i  |' o9 U- Z
  7. */, ~0 B8 u; [5 Y8 J! r8 X

  8. . p: F& ^' Y5 o' O5 S8 |4 d
  9. /**
    , d! y4 l1 f' b) ^3 `
  10. **********************************************************************************************9 r! K0 o" e# e1 N/ w/ g
  11. , R; i6 l4 e$ g& r
  12. **********************************************************************************************( [* d, a8 G, \( w1 ~
  13. * @attention! r! Z3 f: @' s: i! S) _
  14. *
    7 ?) x" ~* R* x& M5 w2 Q
  15. *8 \, n: L, c/ r. [
  16. **********************************************************************************************
    5 K4 i( W' k7 W! Z# h+ T. V( `8 J- r
  17. */! A) Y* ^2 {5 |2 R  A: h# v3 \

  18. * u5 R5 \* S( j0 v6 Y6 P; A
  19. #define m_stmflash_start    0X08000000  /* m_stmflash(STM32内部FLASH)域起始地址 */+ j  r; {$ m5 U: Z" ?8 ^) H- I, T. M
  20. #define m_stmflash_size    0X20000     /* m_stmflash(STM32内部FLASH)大小,H750是128KB */
    6 p1 B5 T9 ?( F
  21. # r; ]$ G' v& @, Y$ ~9 }8 j
  22. #define m_qspiflash_start   0X90000000  /* m_qspiflash(外扩QSPI FLASH)域起始地址 */
    6 f  T, H5 M+ }" X6 H* b
  23. #define m_qspiflash_size   0X800000    /* m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB */  y( u, d$ d- x5 G8 W/ G6 _6 q* p1 x
  24. 1 }2 |! p3 H! p9 g7 ]
  25. #define m_stmsram_start   0X24000000  /* m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM */
    : l7 h6 F# p' U) g
  26. #define m_stmsram_size   0X80000     /* m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB */
    , w7 n$ B3 G% _* b: D6 i6 R. p

  27. , c$ {4 I, b% b8 E7 J3 v
  28. LR_m_stmflash m_stmflash_start m_stmflash_size          /* LR_m_stmflash加载域  */
    , C7 P; f. O! ~6 W) C
  29. {
    ) y5 W" v8 }! `2 r6 h- C. k
  30. /* ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size  */
    4 u7 d4 t9 G5 F% a" c
  31. ER_m_stmflash m_stmflash_start m_stmflash_size {    . E5 N- v5 [& Z8 t, I1 o5 s
  32. /* 优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start */5 d8 {* ~, C( c
  33.       /* RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的. */& a! o2 D: X- q
  34. *.o (RESET, +First)                           
    4 C1 [, c3 E% C# [; y4 S1 |
  35.         /* 将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等 */
    + N$ S" e0 ]8 c$ A/ q8 _1 Z
  36.         * (InRoot$Sections)                            ( D1 k0 o1 z& _) J) l! V
  37.         * (Veneer$Code)5 X2 D+ B! `+ _! ]) F5 j8 A
  38.         libinit.o
    : S, M5 X0 W, ]6 i4 v: ]
  39.         libinit2.o4 k* a; H0 L1 Z2 k
  40.         libshutdown.o
    0 C- }  V+ Y# z, d0 R  L
  41.         libshutdown2.o! m/ O) A6 u" m0 p
  42.         __rtentry.o
    # h; u: z- c7 r3 ?
  43.         __rtentry2.o
    9 q* V9 t$ \" G5 t! N
  44.         __rtentry4.o: H5 E2 S# F' k- H, K
  45.         rtexit.o4 Z9 {2 _  F' ^1 x4 b' I+ G5 D
  46.         rtexit2.o8 A! T1 V* a; K

  47. ) ]  z- }& F" x: t
  48.         use_no_semi_2.o/ Y, y7 o/ ^' G8 ^, I5 t
  49.         heapauxi.o9 X- Z$ l3 L5 z/ l0 j
  50.         use_no_semi.o
    ) s1 J# B4 V; s! g; h. ~+ c4 P
  51.         sys_stackheap_outer.o
    2 L, ~8 u& N" r2 W% w
  52.         exit.o
    % ?, k% S  x; y( ?' O8 b/ L) F
  53.         libspace.o
    6 T, @0 d5 l0 \- W/ l1 X2 [
  54.         fpinit.o
    1 G2 p$ {- ?! n5 ?! W* w
  55.         lludivv7m.o6 E8 g# Y2 ~$ v* F/ L! a
  56.         startup_stm32h750xx.o
    ; x8 ~6 U- p- b2 t
  57. 6 M+ Q- S6 S8 T4 h3 w( |
  58.         rt_locale_intlibspace.o
    ' A% ^- L7 n* s2 }( B6 T
  59.         lc_numeric_c.o
    6 G" C. z# b; @' d: o" }1 H) y7 M1 ^
  60.         lc_ctype_c.o6 L3 }' u4 A  X1 M/ ~& n

  61. ) F5 S( N- U8 v' m* u
  62.         main.o
    2 W2 q* p$ M! ~- [4 E" u
  63.         sys.o
    % u6 y5 f0 u  Z! l& _8 p0 m
  64.         usart.o' R! D) K; i; \2 s! A
  65.         delay.o, ?* L- ]' i4 E, \& d
  66.         pwr.o
    . F' s* S4 o2 ~0 b- G5 e
复制代码

$ g- S+ v, k- ?' x3 }' I& a. o   /* H7的QSPI接口不支持读时写,因此必须把以下3个文件放到内部FLASH,以保证可以
9 q! f: R- B4 A5 j对QSPI FLASH的写入 */
# N8 U7 u, `; H2 V3 p
- S- t+ O3 V0 _4 b, z
  1.         qspi.o
    . e' T: k; G$ w  {6 D
  2.         norflash.o
    8 j% L: M% f6 k: d
  3.         norflash_ex.o* @# a6 p; ], L2 w) \
  4. % G; z1 y( e, g  r3 m) u
  5.         /* 针对HAL库驱动添加到内部的文件 */# x, r' y0 W* x( H- u9 [! P6 V1 V
  6.         system_stm32h7xx.o
    8 V: \4 J% _2 l
  7.         stm32h7xx_hal.o
    ' F1 k' Q4 h. g- O0 U9 ^1 Q
  8.         stm32h7xx_hal_cortex.o8 C3 f8 @, G: H$ O, ^$ w
  9.         stm32h7xx_hal_rcc.o
    ( s$ o+ j+ B, @6 z2 b, w1 ^
  10.         stm32h7xx_hal_rcc_ex.o
    # k7 E3 K7 M8 A- B  p
  11.         stm32h7xx_hal_gpio.o: g+ W+ b" k/ R* R5 \( t: }
  12.         stm32h7xx_hal_dma.o
    . k2 H0 o" w3 A2 B+ g
  13.         stm32h7xx_hal_dma_ex.o
    3 U( w* i3 G  S/ R
  14.         stm32h7xx_hal_qspi.o$ I' b5 ]& M; F/ B
  15.         stm32h7xx_hal_pwr.o! S. q/ M$ V* L* N+ B
  16.         stm32h7xx_hal_pwr_ex.o
    0 q* t: r% ~$ V
  17. }
    + `2 v. C. b, O8 h+ `6 ^$ x; I
  18. /* RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size. */
    4 Q( Y  o* M" l  L8 {( m2 a
  19.     RW_m_stmsram m_stmsram_start m_stmsram_size {      
    + ]8 v9 g1 \# Z0 {+ H2 V) f
  20.         .ANY (+RW + ZI)                           /* 将所有用到的RAM都放在这个区域 */+ f9 T$ [# Q, Q' U! g' r5 v; |+ U
  21.     }4 L; i4 Y- z$ V- n
  22. }
    7 O# T. x2 {4 o! L$ ^9 s
  23. # _$ w' @5 y; O# [! x' ]
  24. LR_m_qspiflash m_qspiflash_start m_qspiflash_size       /* LR_m_qspiflash加载域 */5 C1 Q2 c6 h6 @* w7 i6 M* T
  25. {
    % `/ L) |5 _6 T5 k
  26. /* ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size */# V+ M( y6 |, m8 E9 _" M
  27.     ER_m_qspiflash m_qspiflash_start m_qspiflash_size {
    ( k% X. U4 D6 T+ t' I1 d
  28.         .ANY (+RO)     /* 将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的 */
    & t' y8 C% q5 m' s6 [+ v
  29.     }
    : `0 u3 l" A4 H" r5 Z% s% I
  30. }
复制代码
( B4 v4 M& G+ R! D3 s0 B) a
相比于MDK自己生成的分散加载文件,我们自己编写的相对复杂一些,qspi_code.scf分散加载文件包含2个加载域,3个运行域,分别是:- r3 @* U' ^+ |) A. W' v7 E2 Q
LR_m_stmflash加载域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。它包含二个运行域分别是:' s& [9 i% W- ~! h; u
ER_m_stmflash运行域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。
! N* R; |1 r) p) c0 wRW_m_stmsram运行域,起始地址为:m_stmsram_start(宏定义,实际值:0X2400 0000)大小为:m_stmsram_size(宏定义,实际值:0X80000)。
& P' Y# V. b* `7 uLR_m_qspiflash加载域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。它包含一个运行域:$ |. D- }/ P. I2 A
ER_m_qspiflash运行域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。
5 t7 a7 y  V9 E8 A, P具体的存储器分配情况为:
" i2 q- I8 ?; ~5 E: pER_m_stmflash运行域,包含:*.o (RESET, +First)开始到delay.o结束的相关代码,这些代码运行在内部FLASH,可以得到最佳的性能。需要注意的是:这些代码大部分都是必须放到内部FLASH,否则无法正常运行!!( }0 }9 Z2 x5 f. e3 ?0 s
ER_m_qspiflash运行域,所有没有在ER_m_stmflash运行域指定的代码,都被放在这个运行域,这些代码运行在外部SPI FLASH,速度比内部FLASH慢一些。, p0 t! Y. M9 p; I5 u" G
RW_m_stmsram运行域,所有变量及堆栈(RW Data + ZI Data)都存放在这个运行域。" n; u7 a! e; j' j5 N$ I+ Z8 W6 K
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct文件,特意将.ANY ROM区域放在外部SPI FLASH,这样大家在新增.c参与编译时,默认就是存放在外部SPI FLASH的,这样使用起来就更方便。
, F8 _0 h; e8 z; Q/ m# _3 j1 u7 k) k5 J8 r9 l1 O
注意事项:8 J- ^* I6 Q8 Z0 ~0 K- L
1、如果你新增的代码,对速度有要求,可以将其对应的.o添加到内部FLASH,即放在:ER_m_stmflash运行域。
6 [; r( e8 H- ?, q6 Q3 Y2、如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增的.o放到ER_m_stmflash运行域后(重新编译)再尝试。如果还不行,可以尝试将所有代码都放到ER_m_stmflash运行域后再尝试。
, @1 Y  V" Z0 I1 U/ [1 Z至此,分散加载文件就给大家介绍完了。% n. n# Q5 C; V; ^/ M
————————————————* D: k5 f! D+ `+ }) M( s
版权声明:正点原子9 b7 e0 L/ `2 g8 K0 [3 [+ J

5 k/ s9 R8 E$ b9 ?+ _, ]
2 `) v. F. |; f  X; J# n( K, e) E: A+ B' g" o9 R6 S: J  n; M
收藏 评论0 发布时间:2022-10-5 21:06

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版