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

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

[复制链接]
STMCU小助手 发布时间:2022-10-5 21:06
新建HAL版本MDK工程3 e" a. F5 i8 r) O% c0 q& D' @
在前面的章节我们介绍了STM32H7xx官方固件包的一些知识,本章我们将讲解新建HAL库版本的MDK工程的详细步骤。我们把本章新建好的工程放在光盘里,路径:4,程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,大家在学习新建工程过程中间遇到一些问题,可以直接打开这个工程,然后对比学习。, ~! b5 h/ w6 W. ^; F/ N

( D( M* R4 m# `- \8.1 新建HAL库版本MDK工程/ D) o  k- J, V$ p/ \6 o. j, X4 g
本节我们将教大家如何新建一个STM32H750的HAL库版本MDK5工程。为了方便大家参考,我们将本节最终新建好的工程模板存放在A盘:4、程序源码\2,标准例程-HAL库版本\实验0 基础入门实验\实验0-3,新建工程实验-HAL库版本,如遇新建工程问题,请打开该实验对比。  s$ x9 H6 P: }
整个新建过程比较复杂,我们将其拆分为5个步骤进行讲解,请准备大概2个小时时间,耐心细致的做完!对你后续的学习非常有帮助!
) T5 b4 ]! g7 d在新建工程之前,首先我们要做如下准备:" W; }8 ]1 n  J& ?5 u
1、 STM32Cube官方固件包:我们使用的固件包版本是STM32Cube_FW_H7_V1.6.0,固件包路径:A盘8,STM32参考资料1,STM32CubeH7固件包。/ B! O: v7 a2 ]$ s$ ?8 D
2、开发环境搭建:参考本书第三章相关内容。
$ p) R7 d- {( u8.1.1 新建工程文件夹
: V6 i7 ]+ f' w1 o5 b3 }' [+ l新建工程文件夹分为2个步骤:1,新建工程文件夹;2,拷贝工程相关文件。
0 C9 \/ G! F3 x. W1.新建工程文件夹
& l- h6 R1 T8 g# z首先我们在桌面新建一个工程根目录文件夹,后续的工程文件都将在这个文件夹里建立,我们把这个文件夹重命名为:实验0-3,新建工程实验-HAL库版本。如图8.1.1.1所示:
5 E. E$ ^% S3 I" p$ n c07d881ff02749af9842e8d3614198f4.png
, r& k9 i6 F$ e/ R* \* }. J
' F- n$ z# ^& d- y2 `, k图8.1.1.1 新建工程根目录文件夹
. }+ Y7 [! ^% {9 a为了让工程的文件目录结构更加清晰易懂,我们会在工程根目录文件夹下建立以下几个文件夹,每个文件夹名称及其作用如表8.1.1.1所示:, x6 z, K& h& w) |- @5 n. L

6 N* B$ g0 A. a7 {6 B# v) q* [ 09bd521756de4e4ca5b578aa5591d9b2.png
; K. n# U+ c# l, N) Q8 H! ^/ D
. p" i8 D! R9 r( i& u7 e0 t表8.1.1.1 工程根目录新建文件夹及其作用
6 }: E& E; I& y7 P新建完成以后,最后得到我们的工程根目录文件夹如图8.1.1.2所示。
% [* l; V# K4 o& P
1 |( n3 S+ g# R9 w! J) i% Y d83446698d7e4a6181e1d40778b8742c.png
" P3 T. L4 B/ W7 F$ T$ u! o+ }) v8 v9 C, G% s+ z0 ^
图8.1.1.2 工程根目录文件夹2 z1 A% h8 I1 `
另外我们的工程根文件目录下还有一个名为keilkill.bat的可执行文件,双击便可执行。其作用是删除编译器编译后的无关文件,减少工程占用的内存,方便打包。还有一个名为readme的记事本文件,其作用是介绍本实验的各种信息。1 r: {+ Q  d. W9 C5 G: M% a4 T0 K
工程根目录及其相关文件夹新建好以后,我们需要拷贝一些工程相关文件过来(主要是在Drivers文件夹里面),以便等下的新建工程需要。
7 I( g* z3 y. O, w2. 拷贝工程相关文件
: o# W! }7 h. G, }/ h% A  B+ M接下来,我们按图8.1.1.2的根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。
5 {9 g1 R. w) z2 {* l1 f$ ]4 }/ ~& jDrivers文件夹
1 F5 B! p& I9 C: w- h( v该文件夹用于存放与硬件相关的驱动层文件,一般包括如表8.1.1.2所示的三个文件夹:
: L3 V2 C) m2 t' q* m7 \1 ?3 k2 x. H) m1 O2 P# Z" n3 _
a28caaec3fcb44039e3f6b6858907daf.png
/ K/ e& z! `6 ]: \* B' @; I2 P+ C! ?
表8.1.1.2 Drivers包含文件夹
$ J1 t9 H% s' G1 j# Y4 y- PBSP文件夹,用于存放正点原子提供的板级支持包驱动代码,如:LED、蜂鸣器、按键等。本章我们暂时用不到该文件夹,不过可以先建好备用。1 L- p8 u2 C$ E+ ?8 a0 j+ H1 a
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文件夹里面拷贝过来。
4 E# n# V, x# j# f  kSYSTEM文件夹,用于存放正点原子提供的系统级核心驱动代码,如:sys.c、delay.c和usart.c等,方便大家快速搭建自己的工程。该文件同样可以从:A盘4,程序源码1,标准例程-HAL库版本 文件夹里面的任何一个实验的Drivers文件夹里面拷贝过来。  ]$ b0 f9 Y; ^" e
STM32H7xx_HAL_Driver文件夹,用于存放ST提供的H7xx HAL库驱动代码。该文件夹我们可以直接从STM32CubeH7固件包里面拷贝。直接拷贝“STM32CubeH7固件包Drivers”路径下的“STM32H7xx_HAL_Driver”文件夹到我们工程的Drivers下,只保留Inc和Src文件夹即可。; a, C" L; X' ]( ]6 V8 R
执行完以上操作后,Drivers文件夹最终结构如图8.1.1.3所示:# U& z7 j! ^* r" d& u  S7 j
9 ?; Z* e3 y" v) [: R! v2 C% j
89aca4a79e13457088ecf50d8d500e3d.png 4 i1 y0 K! s2 B" m+ z0 m/ _

/ y5 u# c: m7 P" F3 H图8.1.1.3 工程根目录下的Drivers文件夹# z5 i7 J! E/ ]( C
关于工程根目录下的Drivers文件操作到这里就完成了,可以说步骤是有点多。在此过程遇到问题的话,请大家多参考我们提供的实验0-3,新建工程实验-HAL库版本工程,一步步操作。1 _, F3 c# L# M0 [1 P
Middlewares文件夹& S: G4 C3 L6 r; v5 P/ A) }
该文件夹用于存放正点原子和其他第三方提供的中间层代码(组件/Lib等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各种OS、各种GUI等等。本章我们暂时用不到该文件夹,不过可以先建好备用,后面的实验将会陆续添加各种文件。$ w: D3 o7 W, G6 m" |/ x9 t( G0 z
Output文件夹, T) V% l' f' ^6 n# S: @* C7 G
该文件夹用于存放编译器编译工程输出的中间文件,比如:.hex、.bin、.o文件等等。这里不需要操作,后面只需要在MDK里面设置该文件夹为编译过程中间文件的存放文件夹就行。
  i; Q* v' g/ m; |' WProjects文件夹' X9 N0 Q6 r( a6 e
该文件夹用于存放编译器(MDK、IAR等)工程文件,我们主要用MDK,为了方便区分,我们在该文件夹下新建:MDK-ARM文件夹,用于存放MDK的工程文件,如图8.1.1.4所示:
( y: [9 G0 o1 q# T& ]; u8 s" u3 L) ]+ ~6 _5 c
2278dc824fc0410f95513f3bb54003ca.png
* \+ u5 m  k- @+ U* i( F" f1 R% B1 H% }
图8.1.1.4 在Projects文件夹下新建MDK-ARM文件夹) A( X3 |( N" W% Z! C% ]# `
User文件夹
) c; j- _- R3 B. m2 JUser文件夹用于存放HAL库用户配置文件、main.c文件、中断处理文件,以及分散加载文件。
# u! V' n" N- t我们首先从官方固件包里面直接拷贝官方的模板工程下的HAL库用户配置文件和中断处理文件到我们的User文件夹里。官方的模板工程路径:STM32Cube_FW_H7_V1.6.0\Projects\ STM32H750B-DK\Templates\Template_Project,打开Template_Project文件夹,如图8.1.1.8所示。  ?( E1 C9 z2 J

( Q4 P* |4 b: f" |3 \. X6 e b80bce4e05ec4463bca87c2ea099755e.png 0 B# j, E. c0 \+ W& _/ X6 g8 A
$ ~; [) m4 M* E, M
图8.1.1.8 官方模板工程根目录
8 H3 [* D% U7 y我们需要的文件就在Inc和Src文件夹里面,在这两个文件夹里面找到:stm32h7xx_it.c、stm32h7xx_it.h、stm32h7xx_hal_conf.h这三个文件,并且拷贝到我们的User文件夹下。0 }  F/ R! W% s! o# j' h
最后在User文件夹下新建一个命名为SCRIPT的文件夹,用于存放分散加载文件。分散加载文件直接在我们的实验0-2,新建工程实验-HAL库版本工程对应位置拷贝过来,后面再给大家讲解。main.c文件我们也是放在User文件夹里面的,后面在MDK里面教大家新建.c文件并保存。
( X+ H( @3 z+ v: o$ _  _3 [0 [8.1.2 新建一个工程框架
8 p$ o! }& t1 G$ B7 V. o首先,打开MDK软件。然后点击ProjectNew uVision Project如图8.1.2.1所示:& d( X$ T$ b% e  l" ^  g' R
1 e% u+ j, _4 A- {" }0 D
d7b3678492b147a4b3afd58c9fc1a07f.png
& ], o& D. t/ q: n! q
# D' F, X, x: @: B0 c% S' k图8.1.2.1 新建MDK工程6 o2 D. P# s% \( u2 C( a
然后弹出工程命名和保存的操作窗口,我们将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:桌面实验0-2,新建工程实验-寄存器版本ProjectsMDK-ARM,工程名字我们取:atk_h750,最后点击保存即可。具体操作窗口如图8.1.2.2所示:
: s6 [) P: H5 i9 X( V  ^
: V4 V4 N) M" I; C3 D 6b119a5b802c457692bf3e22588dd8bc.png
4 Z8 b6 g$ Y4 W5 ~! Q
. }9 E% I# L1 q6 g- s图8.1.2.2 保存工程界面1 k* _5 q8 n$ `: L) W% O
之后,弹出器件选择对话框,如图8.1.2.3所示。因为MiniPRO STM32H750开发板所使用的STM32型号为STM32H750VBT6,所以我们选择:STMicroelectronicsSTM32H7 SeriesSTM32H750STM32H750VBTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件pack才会显示这些内容哦!!如果没得选择,请关闭MDK,然后安装 A盘:6,软件资料\1,软件\MDK5\ Keil.STM32H7xx_DFP.2.5.0.pack这个安装包后重试)。
9 R, s2 t! Z" W1 T- ~; d% Z5 g: q' z9 }: r) j3 g, o
c25315eb813e47abacc2163ff53014bb.png & n1 K7 G; k5 D) Y( i

6 a* f% @7 ]( P" o                       图8.1.2.3 器件选择界面
; [+ ^  o. Z2 v6 M0 a* ^8 [
1 J# J+ Y) d, m3 ]5 }/ x0 H7 E点击OK,MDK会弹出Manage Run-Time Environment对话框,如图8.1.2.4所示:! f; n  b1 p% i

5 J% _8 |" w# w; e! l- K+ _ 840c8d7777c748d4ba3c8480058b0bc9.png
* a4 D! W( p- c  e4 B
: l' C% T* c8 X+ T1 y) k图8.2.1.4 Manage Run-Time Environment界面
8 `' B2 e. z( G  |! j这是MDK5新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建开发环境,不过这里我们不做介绍。所以在图8.1.2.4所示界面,我们直接点击Cancel,即可,得到如图8.1.2.5所示界面:
  S6 W' h+ s3 R3 d) Y! e" w" G: ^) F
01c6e443fd774a919f4afe3eff0d363e.png
& }) E' {; g0 y* G- Y5 x
4 i7 ^$ B2 H  ~; f0 q  J$ Z图8.1.2.5 工程初步建立
6 y! r& W2 A' P' _! A( [8 D此时,我们打开MDK-ARM文件夹,会看到MDK在该文件夹下自动创建了3个文件夹(DebugConfig、Listings和Objects),如图8.1.2.6所示:8 X; h* `9 D" ^/ x, I0 e# p9 x) q* F

# a& m* M' m. B( S$ z cf22cd3c423d441398f1363d26b3d632.png $ h9 j$ ?9 F3 W( P0 c4 V
' a  \" h- b% c0 I% Z
图8.1.2.6 MDK新建工程时自动创建的文件夹/ R. ^( L7 N2 r+ p5 @! D3 U  u
这三个文件夹的作用如表8.1.2.1所示:9 A$ k3 w3 Z$ z4 l+ K- i
9 {( M7 o8 A  y" m
5021687c5d93429da588e41d6ac541f5.png
- S( O/ E+ v' H6 h7 a, u2 n& m. n! d" a! g- b0 F
表8.1.2.1 三个文件夹及其作用! E- ~1 z1 w4 k+ j/ L: t; P* o
编译过程产生的链接列表、调试信息、预览、lib等文件,统称为中间文件。为了统一管理,方便使用,我们会把输出在Listings和Objects文件夹的内容,统一改为输出到Output文件夹(通过魔术棒设置),我们先把MDK自动生成的这两个文件夹(Listings和Objects)删除。
2 y+ |) k" A# m% f至此,我们还只是建了一个框架,还有好几个步骤要做,比如添加文件、魔术棒设置、编写main.c等。& Q' H6 ~( n& s+ |+ _, B; Z
8.1.3 添加文件& k+ S0 c# K* r4 V
本节将分5个步骤:1,设置工程名和分组名;2,添加启动文件;3,添加SYSTEM源码4,添加 User 源码;5,添加 STM32H7xx_HAL_Driver 源码。
( |# y5 Q* l% p3 X) p  W2 v1.设置工程名和分组名" e9 j% L3 ~1 T3 I/ T! _
在ProjectTarget上右键,选择Manage Project Items…(方法一)或在菜单栏点击品字形红绿白图标(方法二)进入工程管理界面,如图8.1.3.1所示:+ P# R! t; e4 Z

' j! j! k# S6 o2 ^3 m& x$ [/ [ 43f3eccd0c0a49038e00b8140982a73a.png , O4 l/ h8 R1 K& |1 f$ \+ _( v( d
+ S2 D* Q# [) f9 B1 y. f9 H: N2 |
图8.1.3.1 进入工程管理界面
" V1 K8 h# P2 i$ }! y在工程管理界面,我们可以执行设置工程名字(Project Targets)、分组名字(Groups)以及添加每个分组的文件(Files)等操作。我们设置工程名字为:Template,并设置四个分组:Startup(存放启动文件)、User(存放main.c等用户代码)、Drivers/SYSTEM(存放系统级驱动代码)、Readme(存放工程说明文件),如图8.1.3.2所示:
$ {* {7 ]. @2 }: ~5 ~1 B" r/ ~8 N2 ~# M( b
fd29f722719047778a894ec11dc15ac3.png
/ ]3 N- g$ J% v3 O
5 a  C6 q$ o+ w. g6 e图8.1.3.2 设置工程名和分组名
+ d6 b* L( F, m! z5 g设置好之后,我们点击OK,回到MDK主界面,可以看到我们设置的工程名和分组名如图8.1.3.3所示:
! L, a9 ^5 Q7 p+ U3 |. k2 p' g
% M0 k3 c; U( c0 ] 9575821896784b26965cf3d769e26165.png : H# G: y+ `4 c' r+ W5 M

' o/ q4 I  I, D( P- e/ b6 H- N0 b图8.1.3.3 设置成功" z4 ?6 q$ c( \) h
这里我们只是新建了一个简单的工程,并没有添加BSP、Middlewares等分组,后面随着工程复杂程度的增加,我们需要一步步添加对应的分组。
* X' A1 t7 n6 D% N注意:为了让工程结构清晰,我们会尽量让MDK的工程分组和我们前面新建的工程文件夹对应起来,由于MDK分组不支持多级目录,因此我们将路径也带入分组命名里面,以便区分。如:User分组对应User文件夹里面的源码,Drivers/SYSTEM分组,对应Drivers/SYSTEM文件夹里面的源码,Drivers/BSP分组对应Drivers/BSP文件夹里面的源码等。) w( E( b% Q) s; ^- H
2. 添加启动文件
8 _8 V0 t2 ^; d' d: E* ~7 g启动文件(.s文件)包含STM32的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用main函数等,是每个工程必不可少的一个文件,我们在本书第九章会有详细介绍。
! F9 o$ Q6 v  m2 V; p/ C" R启动文件由ST官方提供,存放在STM32CubeH7软件包的:DriversCMSISDevice STSTM32H7xxSourceTemplatesarm文件夹下。因为我们开发板使用的是STM32H750VBT6,对应的启动文件为:startup_stm32h750xx.s,为了节省空间,在精简版CMSIS文件夹里面我们把其他启动文件都删了。% D) ]& z9 i; X4 @
关于启动文件的说明,我们就介绍这么多,接下来我们看如何添加启动文件到工程里面。我们有两种方法给MDK的分组添加文件:1,双击Project下的分组名添加。2,进入工程管理界面添加。$ _0 A0 o# p" W8 ]3 K
这了我们使用方法1添加(路径:实验0-3,新建工程实验-HAL库版本\Drivers\CMSIS6 x' c. K7 z" B6 ?0 ]
Device\ST\STM32H7xx\Source\Templates\arm),如图8.1.3.4所示:
* k! D* c& `7 `5 B9 J2 }$ d3 O( `: q  d1 f5 H6 _
faa546005287411f83b7561ea1f6003a.png ; ]$ ], t' Y- V9 b

2 t3 _; ^  n3 Q" [' [# x图8.1.3.4 双击分组添加启动文件(startup_stm32h750xx.s)/ ?( U6 R$ t) ^. w/ M
上图中,我们也可以点击Add按钮进行文件添加。添加完后,点击Close,完成启动文件
4 C+ W/ G7 `/ k+ d" O添加,得到工程分组如图8.1.3.5所示:/ q2 }$ o0 o  u6 f5 u
; Y9 c7 `& X0 {+ Y0 _0 f
8647718b231747e6a03964b009c2efa6.png : l$ p1 R) Y8 D+ r- x7 E3 Y9 ?

" q& Q+ H7 Q* v图8.1.3.5 启动文件添加成功. @2 D0 }" @& Q9 i0 L7 W
3. 添加SYSTEM源码  j4 b' U; `, r5 l0 W+ u: z
这里我们在工程管理界面(方法2)进行SYSTEM源码添加。点击:按钮,进入工程管理界面,选中Drivers/SYSTEM分组,然后点击:Add Files,进入文件添加对话框,依次添加delay.c、sys.c和usart.c到该分组下,如图8.1.3.6所示:
9 }9 C' z8 Z# T$ [
2 Q1 K* |$ a/ b) y$ ]0 c 884e3508a6444b5cb08f234ae2a0499b.png ( l& A2 z# n+ `- J- k( I* }
6 k' w& w: K  ]' O! x
图8.1.3.6 添加SYSTEM源码
6 h, P" i+ p- G0 `+ H注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.7所示:" u$ a+ A% k1 B- F/ n

" m' R( Q' _8 c* v a9bc3490a642456e8e6c7062e85dceaf.png
0 A2 N9 v3 o1 Y. E% j& N; n
, N/ [2 r$ u: D( }9 p- g0 X- v图8.1.3.7 SYSTEM源码添加完成% M6 T8 F3 O$ Q2 B  i. w" Y
4. 添加User源码: z- |7 U! @/ ?* K  J# ]3 J
这里我们在工程管理界面(方法2)进行User源码添加。点击:按钮,进入工程管理界面,选中User分组,然后点击:Add Files,进入文件添加对话框,依次添加stm32h7xx_it.c和system_stm32h7xx.c到该分组下,如图8.1.3.8所示:
  f  k* x2 R) y1 y: e- H7 z1 w4 K& Q: m
- z, P# q3 Z) x! ~ 83627a883d394387b9a69b3f97ca5d0d.png / D( L$ O( }0 e' p5 v4 G

5 {- Y0 I( Q# v. ]: u图8.1.3.8 添加User源码5 {4 L2 d4 m, z, k6 T
注意:这些源码都是在第8.1.1小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图8.1.3.9所示:
/ i  J$ Z$ r% Y+ g$ S, A& \0 U+ ]; U) j5 n) M3 c. c
c7f798b6bbac4d329ef22a34f0da7a35.png
7 \' x2 T* A9 e  m0 j
  u% U$ W/ s; j& E1 J$ g: z$ M1 S图8.1.3.9 User源码添加完成- g* u0 {) N- s/ ^. [% z8 i
5. 添加STM32H7xx_HAL_Driver源码
( j( Z" }. C# \$ J5 u9 ~2 D& w+ a接下来我们往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所示:
. @  q# b- k* B# \- O% U2 ~9 N/ Q0 n% y
5f1b4fe0439b4dfea7d7dc9ed6ceb634.png
- s9 W  q4 q1 P0 Q
/ ?  B( b1 \- P, |图8.1.3.10 添加STM32H7xx_HAL_Driver源码$ x7 r( Y# \" y8 B( T4 n
添加完成后,如图8.1.3.11所示:4 y7 d  r- I4 u+ E% E2 E6 `$ V

6 g0 }: x! r  O, r6 U1 r5 n& D bcb17b5b492a4a80a630f98abbad546e.png
. M+ u' ?' `' G% O4 D# L+ c9 }1 Z& e) N
图8.1.3.11 STM32H7xx_HAL_Driver源码添加完成6 N: I" l% m& o3 r2 ~
可以看到分组中有些.c文件有个小钥匙的符号,这是因为官方的固件包的文件设置了只读权限,我们取消只读权限就好了,方法如图8.1.2.12所示。
5 ~" K' U' F6 b! z) q' P* f$ ~3 V; Z" T( j4 T* d% o/ {4 s
cd4fff494965426d861a066a1ab4e118.png 9 w( n3 L; W3 k5 t7 G7 g- Z3 \
' X7 m) ~4 |) _' u+ R: L# o4 F! W
图8.1.2.12 取消工程文件夹的只读权限
! E- m5 l& W: E; L  i! x9 B; S7 y% ?8.1.4 魔术棒设置
, v' V  X6 [; v$ X# V' q4 ~为避免编写代码和编译报错,我们需要通过魔术棒对MDK工程进行相关设置。在MDK主界面,点击:(魔术棒图标,即Options for Target按钮),进入工程设置对话框,我们将进行如下几个选项卡的设置。& `5 b4 u' K; a) M/ Q; C
1.设置Target选项卡+ P* Y6 v3 `, e0 ?) `
在魔术棒Target选项卡里面,我们进行如图8.1.4.1所示设置:% [* J0 x$ M- b4 O
# s  d" t2 a4 \; A  `% R  Z' X, _
aa079a0**74261b7926c03ee69b908.png 9 B! G2 @( O6 d0 h. x
2 l* U  d' C% Y
图8.1.4.1 Target选项卡设置: `- y9 R0 u' X2 i. H7 G: M/ w
上图中,我们设置芯片所使用的外部晶振频率为8Mhz,选择ARM Compiler版本为:Use default compiler version 5(即AC5编译器)。1 n; l& N. V- i- W/ ^
这里我们说明一下AC5和AC6编译的差异,如表8.1.4.2所示:
7 v0 {2 p6 P5 i1 h- j6 ~% H2 H
" I  g# L" K4 N7 _  X- C) {$ C. e ec16655ac2564428a4614ce6ade99f64.png
5 P  v/ Y+ ]/ A! z( ]3 i5 h  B1 |1 z% p" R& M: s) I
表8.1.4.1 AC5&AC6简单对比4 r$ o2 f( {4 ~& L& G, j5 |" E- T3 T4 q
由于AC5对中文支持比较好,且兼容性相对好一点,为了避免不必要的麻烦,我们推荐大家使用AC5编译器。为了让大家自由选择,我们正点原子的源码,也是支持AC6编译器的,不过在选项卡设置上稍有差异,具体差异如表8.1.4.2所示:  [' G' G9 k5 @. ], l8 |8 r
& T% G6 b6 m6 Y9 p- [2 g
ac1ed12294bf4652b48a2b8b2cfdd5ba.png & N: m5 K0 k! m

8 U7 j& K) Z( r/ Q: T& v: E9 U表8.1.4.2 AC5&AC6设置差异
3 [" J$ ?: F' U/ F. A; R, B2. 设置Output选项卡
5 d) d' h, e  _0 y+ n# y在魔术棒Output选项卡里面,进行如图8.1.4.2所示设置:5 L- u  s7 j: k0 i
9 Z. a+ i! C1 p6 C! }* L
eb9687db7b964a33bad72c5485588208.png & a) B! N) t7 s/ M9 B# R& ^6 k
6 U7 ~4 z! ]4 @
图8.1.4.2 设置Output选项卡: l( [6 s3 w& @
注意,我们勾选:Browse Information,用于输出浏览信息,这样就可以使用go to definition查看函数/变量的定义,对我们后续调试代码比较有帮助,如果不需要调试代码,则可以去掉这个勾选,以提高编译速度。" [1 s: {  [/ ?. j0 Y9 f$ h$ b
3. 设置Listing选项卡2 b: F: \3 Z; j! s
在魔术棒Listing选项卡里面,进行如图8.1.4.3所示设置:4 X' k: e4 E9 I( F$ N5 T* I

! n2 a! S3 k8 e7 { c997f30d6c3d4504a4ec90f2c42edb29.png 4 K4 @7 B& m3 E+ V* }

( r! d: D) `8 n. l; c: p4 e图8.1.4.3 设置Listing选项卡8 Q8 I4 f9 P" Z
经过Output和Listing这两步设置,原来存储在Objects和Listings文件夹的内容(中间文件)就都改为输出到Output文件夹了。/ f4 y: c( S" P$ W
4. 设置C/C++选项卡+ V4 k% C$ `" H5 j* |
在魔术棒C/C++选项卡里面,进行如图8.1.4.4所示设置:6 P# A/ B" t0 }0 ^0 @7 N
- `4 n+ F' y. w8 m% c
ff4c06ba4f6742998cfa29c00c5ae22f.png   |0 w  j7 f. T) A4 k
; _% J, M! Y: E" c- ]
图8.1.4.4 设置C/C++选项卡
3 W/ X/ }# ]$ p" @1 U. K, Z在②处设置了全局宏定义:STM32H750xx,用于定义所用STM32型号,在stm32h7xx.h里面会用到该宏定义。7 y& k3 i. [& s# d+ }$ b4 T8 [
在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6编译器的时候,这里推荐默认使用-O1优化。
; L+ R7 d, d% G0 U$ B! ]: d: _在④处勾选C99模式,即使用C99 C语言标准。
6 l1 h5 k5 I% o在⑤处,我们可以进行头文件包含路径设置,点击此按钮,进行如图8.1.4.5所示设置:
/ k3 }) k# L3 k0 K9 R. {) L' {0 j7 }3 o1 q' O
042d59760099402eb057ab905f45cf9e.png 5 k1 I: I. H) m# S, u3 t. I$ i

: l8 K* ]& |' z4 C5 k图8.1.4.5 设置头文件包含路径
9 |: [& s: t6 Y6 s3 I) E上图中我们设置了4个头文件包含路径,其中3个在Drivers文件夹下,一个在User文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的include全部使用相对路径,也就是我们只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码,如果全部是使用相对路径,则无需再设置头文件包含路径了,直接在include里面就指明了头文件所在。1 Q- y3 |9 s- i7 `6 f  d
关于相对路径,这里大家记住3点:
% u6 y4 Z* e9 @% c8 q; a+ p( m, I: q0 `1,默认路径就是指MDK工程所在的路径,即.uvprojx文件所在路径(文件夹)! Q/ A! [  e  L
2,“./”表示当前目录(相对当前路径,也可以写做“.\”)# s" \3 m& p% A0 N9 p
3,“…/”表示当前目录的上一层目录(也可以写做“…\”). J# p) \8 [0 d3 Y9 [
举例来说,上图中:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,前面两个“…\”,表示Drivers文件夹在当前MDK工程所在文件夹(MDK-ARM)的上2级目录下,具体解释如图8.1.4.6所示:- G% A, {! A2 [( W# V4 t7 S

; O9 H8 d5 Y$ ? 9958cf50da964d39ac15e517a4bdc8dc.png ! O0 ^! S8 {' v

* z$ x  p" T; f图8.1.4.6 …\Drivers\CMSIS\Device\ST\STM3H7xx\Include的解释' m$ E, u* u$ _2 R% w% x, E
上图表示根据头文件包含路径:…\Drivers\CMSIS\Device\ST\STM32H7xx\Include,编译器可以找到⑥处所包含的这些头文件,即代码里面可以直接include这些头文件使用。
. n' k3 `  M$ x5 h再举个例子,在完成如图6.1.4.5所示的头文件包含路径设置以后,我们在代码里面编写:
, }5 N- o' ?0 l& d* e# C5 ?#include “./SYSTEM/sys/sys.h”- G- @5 ?* n; R" E: B+ m+ \
即表示当前头文件包含路径所指示的4个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h的路径,实际上就是在Drivers文件夹下面,两者结合起来就相当于:, g" e( w9 R* w2 t( E+ ]
#include “…/…/Drivers/SYSTEM/sys/sys.h”  f- V. M. N# E3 h& Q' V
这就是相对路径。它既可以减少头文件包含路径设置(即减少MDK配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在那个文件夹,因此我们推荐在编写代码的时候使用相对路径。
$ a  X- V+ N* H+ w, i6 W关于相对路径,我们就介绍这么多,大家搞不明白的可以在网上搜索相关资料学习,也可以在后面的学习,分析我们其他源码,慢慢体会,总之不难,但是好用。
0 N9 h; ]) {0 Z& ~最后,我们如果使用AC6编译器,则在图6.1.4.4的Misc Controls处需要设置:-Wno-invalid-source-encoding,避免中文编码报错,如果使用AC5编译器,则不需要该设置!!# M3 W, y3 W8 l" P6 n
5. 设置Debug选项卡
: C, t: m# ]3 h+ |在魔术棒Debug选项卡里面,进行如图8.1.4.7所示设置:
1 o1 B5 Q. Z% Z9 A& h
4 j$ T6 ~! Y+ Z5 J/ W' Q4 y 663ae5fcb1cb4a64abe8ee0f5360fdeb.png 1 X6 u+ l  f0 E  t% H
8 D& @+ s, S+ g. L! M
图8.1.4.7 Debug选项卡设置
9 A8 H% X  Q2 R+ I& C图中,我们选择使用:CMSIS-DAP仿真器,使用SW模式,并设置最大时钟频率为10Mhz,以得到最高下载速度。当我们将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在SW Device窗口显示芯片的IDCODE、Device Name等信息(图中⑤处),当无法找到时,请检查供电和仿真器连接状况。; l, P& m$ Y  n7 A2 ^- d
6. 设置Utilities选项卡
+ Y; R5 u& D. _7 K( z+ g在魔术棒Debug选项卡里面,进行如图8.1.4.8所示设置:
+ h# Z6 _  u5 I
, g& |9 \  N3 K1 x( Q8 [% D+ }7 i5 k 6cb0e659dc6846e5998d10fb61975fc7.png
! M# B0 R+ {: v2 N4 L: l+ t5 s. C
图8.1.4.8 Utilities选项卡设置
* q5 ^+ ]& `% z2 O5 ?2 N6 _图中⑥处下载算法STM32H750,是MDK默认添加的,针对STM32H750系列产品。除此之外,我们还要添加算法,点击⑦处按钮添加即可。添加好算法后,设置算法使用的 RAM 地址和大小,这里设置的起始地址为:0X2000 0000(DTCM),大小为:0X3000。必须按这个大小设置,否则下载会出错(无法加载算法)。
" ]7 s2 f4 V! \4 c+ _3 G7. 添加分散加载文件
' R# N+ w' L- t: f$ d5 R. T9 B由于STM32H750VBT6芯片内部的FLASH的空间比较少(只有128KB)。对于大的工程,这个FLASH空间是不够用的,为了解决这个问题,同时方便后续工程的新建,我们统一使用分散加载的方式来决定FLASH内存的分配,而不用MDK默认的设置。关于分散加载是什么?我们后面8.3小节会讲解,请大家先跟着我们把新建工程完成。
2 Q/ B- L6 M- ]$ i, ~. ]1 f分散加载的文件已经为大家准备好了,可以在实验0-3,新建工程实验-HAL库版本\User\SCRIPT,或者在(A盘)/程序源码/STM32启动文件/分散加载_HAL库版本/SCRIPT中拷贝qspi_code.scf文件到我们的工程User\SCRIPT路径下,如图8.1.4.9所示。
$ X3 ~1 M; p% U  L: b1 ^" C. F7 D0 [+ X, o
0362a607c804410ea055be3879718082.png   W7 I, @+ u' b1 X7 A: _& c3 G1 j# l2 a

8 V+ m  R3 Z2 J; I9 {) Z6 f图8.1.4.9 拷贝分散加载文件到工程+ s: \- J0 J* j
注意:这里的分散加载文件寄存器跟HAL库是不一样的,我们建立HAL库工程,所以必需用HAL库版本的分散加载文件。- T; x; W: w& K6 h
接下来我们需要对MDK进行配置,相当于把分散加载文件关联到工程里。方法:点击魔术棒, Linker选项卡取消勾选:Use Memory Layout from Target DialogScatter File路径选择SCRIPT文件夹 选择qspi_code.scf文件,然后,在disable Warnings一栏,添加:6314,6329,屏蔽6314和6329这两个警告。如不屏蔽,当分散加载里面有某些段(section)没用到,则会报警告,所以我们需要屏蔽这两个警告。如图8.1.4.10所示。/ p* G$ }& Y" E

, T) f8 k9 J3 I% Y 241659f1cccf4ec799607dbe4bcc40ed.png
. g5 Y! @6 r, J6 [- ?& l
, g3 h# ]$ V. Y8 N7 }5 V图8.1.4.10 添加分散加载文件, M  f& Q! w) ~+ \
至此,添加分散加载文件的相应操作就完成了。( J' v5 l: h: E  g- X& i; x) h
8.1.5 添加main.c,并编写代码/ g- ^8 ?5 K' _$ w& b7 D( {& C
在MDK主界面,点击:,新建一个main.c文件,并保存在User文件夹下。然后双击User分组,弹出添加文件的对话框,将User文件夹下的main.c文件添加到User分组下。得到如图8.1.5.1所示的界面:
$ g# X; T- o# J9 @
( ?) w4 _6 U2 h' O b1dd0efba9964663a212af58f8129af6.png 1 H, L' _& P: Q2 `# z# ^

0 d2 Y5 f) f1 w& p, ?, |3 [图8.1.5.1 在User分组下加入main.c文件
8 z! {! B% g6 u& P至此,我们就可以开始编写我们自己的代码了。我们在main.c文件里面输入如下代码:
  D! u( i" w. p; Y# ?: d* r
/ K" Q5 s! }9 R5 r
  1. #include "./SYSTEM/sys/sys.h"8 I1 ^; `3 l& j( H; ]$ i3 ^
  2. #include "./SYSTEM/usart/usart.h"% T" G: k$ s6 e) Z) V3 d: C; `
  3. #include "./SYSTEM/delay/delay.h"
    & C1 Q- {8 u# u2 a
  4. 5 l4 z$ z: x' P
  5. void led_init(void);                               /* LED初始化函数声明 */
    , f& r' T  |3 z8 @+ ]# J3 p( h4 Y

  6. % |0 F9 ~# X& _, v7 {% M: }
  7. int main(void)$ @- M! S: J  q. F% D9 a
  8. {
    # R9 e$ E2 w. J/ l- x; |
  9.     sys_cache_enable();                            /* 打开L1-Cache */
    , _8 `- U! x5 k3 C& }8 c
  10.     HAL_Init();                                             /* 初始化HAL库 */# ]8 V, f& ^; h
  11.     sys_stm32_clock_init(240, 2, 2, 4);                /* 设置时钟, 480Mhz */
    ) k$ o; o% x" C1 D% y
  12.     delay_init(480);                                       /* 延时初始化 */8 n2 |0 N' D: g9 _$ S, X
  13.     led_init();                                             /* LED初始化 */
    - s% p9 I; D7 P- Y& {
  14.     while(1)9 n7 O5 Q/ p' C7 t+ L9 ?
  15.     {
    , ]9 z5 @1 _6 ~% Q8 ?) G3 f$ B
  16.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);            /* PB4置1 */ * {: A5 s7 C9 n4 U0 J6 Z
  17.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);         /* PE5置0 */ . I- T( a, Q& V* k% h
  18.         delay_ms(500);
    9 B( F% [0 G% V( b% i$ Z, A
  19.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);            /* PE5置1 */
    - o. O2 T1 T; ~" _  t
  20.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_RESET);         /* PE6置0 */
    5 c% H1 x4 G4 q/ ]: m/ O
  21.         delay_ms(500);
    . i- k+ k# W4 q* e4 X! [5 l0 ]5 T
  22.         HAL_GPIO_WritePin(GPIOE,GPIO_PIN_6,GPIO_PIN_SET);            /* PE6置1 */7 W: i: t8 R/ C3 s
  23.         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);         /* PB4置0 */
    7 M0 i9 P* L& \+ \9 e; n& ]! {
  24.         delay_ms(500);
    $ p; _5 D9 o0 ^% h  U
  25.     }" g3 G. b$ Z3 a& o8 Q$ E* }
  26. }
    4 y- G. l8 J9 L

  27. 8 b9 h, s9 _, v: E3 i# p' P; I$ x( k% Q% k
  28. /**
    # I& @  l" c3 ]: E: d4 x& m9 Z6 |" R% O. C
  29. * @brief       初始化LED相关IO口, 并使能时钟; ^4 x) [; t# K0 a  a5 |7 h
  30. * @param       无: L1 s0 m0 H# p/ P7 v0 e
  31. * @retval      无( T, l' B' L, f9 Q! H
  32. */
    - ~4 ^5 e( u0 \- Y# Z9 K
  33. void led_init(void)# _1 A* C5 x/ h: K) R
  34. {
    - F! q3 _! U+ W" a& G
  35.     GPIO_InitTypeDef gpio_init_struct;
    3 U/ ?- L, Q& K$ S* H
  36.     __HAL_RCC_GPIOB_CLK_ENABLE(); /* PB4时钟使能 */
    ( y0 b0 P$ @/ K0 w
  37.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE6时钟使能 */
    4 K8 j  I) b6 B. K
  38.     __HAL_RCC_GPIOE_CLK_ENABLE(); /* PE5时钟使能 */
    , U3 h3 O' W/ x2 d7 a/ q# L6 ?
  39. 7 N0 o) P$ V  h9 Y( T
  40.     gpio_init_struct.Pin = GPIO_PIN_4;                                /* LED0引脚 */
    + i# q; \$ @2 Z$ F$ v
  41.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */
    # S* k4 z! Z6 N$ L; k
  42.     gpio_init_struct.Pull = GPIO_PULLUP;                              /* 上拉 */
    ) y8 m% S7 G0 E
  43.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;            /* 高速 */# E% c( r9 z8 f
  44.     HAL_GPIO_Init(GPIOB, &gpio_init_struct);                         /* 初始化LED0引脚 */
    " z0 a1 i% ^7 ^  s4 K0 F! z
  45. 0 X: r/ n; S% u6 _% M1 G2 N: K: l1 P
  46.     gpio_init_struct.Pin = GPIO_PIN_6;                                /* LED1引脚 */
    - N: O! l9 X2 J
  47.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED1引脚 */) p3 k4 s+ S" R- u' M

  48. ; t6 t8 v4 @( b
  49.     gpio_init_struct.Pin = GPIO_PIN_5;                                /* LED2引脚 */
    : D4 W, s" I% ]1 @" Y/ Z" q4 E. N
  50.     HAL_GPIO_Init(GPIOE, &gpio_init_struct);                        /* 初始化LED2引脚 */
    8 B6 q- c7 p# F' T; s
  51. }
复制代码
4 K9 u$ }* N  s0 @* Y
此部分代码,在A盘4,程序源码1,标准例程-HAL库版本 实验0 基础入门实验实验0-3,新建最工程实验-HAL库版本Usermain.c里面有,大家可以自己输入,也可以直接拷贝。强烈建议自己输入,以加深对程序的理解和印象!!1 y! r1 W. t0 d( |
注意,这里的include就是使用的相对路径,关于相对路径,请参考前面C/C++选项卡设置章节进行学习。: u5 F1 t. c. U  w; E: |
编写完main.c,我们点击:(Rebuild)按钮,编译整个工程,编译结果如下图所示:
2 |5 g4 T5 g; l8 W+ K  R9 K, s- a* I1 X( a' F
图8.1.5.2 编译结果
  |7 U$ ^8 d: Q: v8 e9 L- T  h编译结果可以看到1个错误,0个警告。这个错误说找不到main.h,因为我们也不需要用到main.h,双击这个错误会弹出下面的stm32h7xx_it.c文件对应包含main.h的语句。我们只需要把它删除,然后重新编译,如图8.1.5.3所示。
) M, Y4 _2 ?2 y+ q0 M* F4 t* c8 C$ k
" D+ V! N5 ]* c+ F1 [3 `. Y' P 68deb7ad5549455cad3065d60e1f0432.png ' n  t5 f& x  h. G, ~
+ ^0 E2 g5 N5 a
图8.1.5.3 删除包含main.h的语句* p8 Y2 }% g$ X+ e+ m
编译后发现又有一个警告,警告HAL_IncTick函数没有声明,如图8.1.5.4所示。' e7 n! \0 V+ D) t/ h8 K8 _
( Y  M2 n3 K+ G# ]* s, g* O/ |
84622d45998148969e1d60df31ad2d5a.png ; r  [0 a! w' x0 y2 H+ b! w
( v' H4 G! @  c
图8.1.5.4 编译报警告
4 F! D5 |( l2 @因为这个函数是在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所示。2 ]6 r; @8 X! |' ?0 k

/ i; @+ Z7 i/ {$ j: |; w 844b5d27125c4d708677dc0b95b9a39d.png + j: E  @" ?6 y1 G
; G$ x' q0 H1 g# w/ @
图8.1.5.5 包含stm32h7xx_hal.h头文件到工程
3 ~" j. F) Q9 S2 O% ~  R8 b- ~0 a& c8 W) g再进行编译就会发现0错误0警告,结果如图8.1.5.6所示。
: E) `- |) D% G- x, e5 }+ m* F. m* D: e  ?" u6 l
0f3099f2b6e64aa286997c8756220211.png , E) t- n4 D: u1 w2 s) u5 I: h  Z

* k5 l; b0 f4 k9 o1 _+ }9 c图8.1.5.6 编译结果
1 o) G# w4 Z! _" i& y* M! s* F/ y5 e编译结果提示:代码总大小(Porgram Size)为:FLASH占用13520字节(Code + RO + RW),SRAM占用2008字节(RW + ZI);并成功创建了Hex文件(可执行文件,放在Output目录下)。
! `9 L/ G# s( g% Q9 B! b0 g" R/ ]8 `总结:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到0错误0警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。
) m* T/ K8 i1 h5 a* A- m# m* X另外,我们在Readme分组下还没有添加任何文件,由于只是添加一个说明性质的文件(.txt),并不是工程必备文件,因此这里我们就不添加了,开发板光盘的源码我们是有添加的,大家可以去参考一下。
" l, r2 \" V' h! D! x至此,新建HAL库版本MDK工程完成。
) J, Z- P; @! C$ }: H8.2 下载验证6 |# f& }& X* y
这里我们继续使用DAP仿真器下载,在MDK主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,如图8.2.1所示:0 W0 R4 ]* m2 x/ Q
% m5 u; @% O4 ]3 m8 M
c780acf342a14739aaffe4cac5f5d48f.png / E0 y7 @" k) G

+ ?; V: |/ l7 m$ A图8.2.1 下载成功4 }9 |- y8 B/ W6 S' e% N+ Y
上图提示:Application running…,则表示代码下载成功,且开始运行。此时,看到RGB的红、蓝、绿三种灯轮流亮,类跑马灯。如果有朋友没能下载成功,请看第四章寻找问题,或者直接对照我们提供的实验0-3,新建工程实验-HAL库版本工程设置。% H. K& A5 ?/ O: d: P( B

" u0 O1 c: e5 h7 z- P8.3 分散加载文件简介
, o$ }! {' F, C5 I3 }; b+ [0 mARM芯片用于在链接时指定存储器分配方案的文件,称之为分散加载文件(.sct),它可以将不同的代码(.o文件)放在不同的存储空间。关于分散加载的详细介绍,请参考:A盘8,STM32参考资料4,分散加载分散加载文件浅释.pdf,这是周立功公司的一份文档资料,详细介绍了.sct文件的基础概念、语法及应用实例说明,对学习分散加载非常有帮助,请大家务必先学习一下这个文档。# D" s: t8 P* W& z8 b
本节,我们仅对.sct文件进行一个简介,方便大家学习。
. Q1 Y" V6 V5 U# L  U# A首先明确一个概念:MDK编译任何STM32工程,都会需要用到分散加载文件。分散加载文件的来源有两种方式:' \9 p5 h1 W6 B1 i- n
1,通过MDK自己生成;
3 [  T) f1 o3 P+ u# ^2,通过用户指定(用户自己编写);$ i% n( Q' R" s3 X6 y
首先,我们来看MDK自己生成的.sct文件。
6 x# b; ?1 H3 K+ p1 w# [选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.1所示的设置:
  y  P' O4 s3 N2 ~+ |$ o( x) a. k' v+ K9 W) @' M. p
1b2f6b2ec058411b9335ca44636b00c8.png . Z" C' a$ n; P3 @

: v, K) B. t# f# K9 A! V- D# D& O图8.3.1 勾选Use Memory Layout from Target Dialog选项  Q6 ]6 l& N/ m; F+ A. Z
勾选Use Memory Layout from Target Dialog选项后,MDK就会根据Target选项卡里面的相关设置来决定存储器分配,如图8.3.2所示:, h9 ^6 N+ V& L9 M/ F6 T
0 [4 L" ^% j$ [. t* J5 C+ b& J1 t9 ^
07e46a153a124ba6bec9f183314042fd.png 2 o9 e! ?5 E) i  r# ^* G" \

! @7 v+ y- e$ d% Z$ N2 l图8.2.2 MDK默认的存储器分配
( k& m& @/ y/ \6 f& q& A标号①,是MDK的只读存储区域(ROM)和可读写存储区域(RAM)的配置区域。0 E$ |" c8 F+ v0 i) d# {! c' g
标号②处,说明默认将所有的代码(Code + RO Data + RW Data)都存放到IROM1指定的地址范围上。其起始地址为:0X0800 0000,大小为:0X20000。  V% ]. N" m  x3 Z
标号③处,说明默认将所有的变量及堆栈(RW Data + ZI Data)都存放在IRAM1和IRAM2指定的地址范围上。IRAM1的起始地址为:0X2000 0000,大小为:0X20000;IRAM2的起始地址为:0X2400 0000,大小为:0X80000;变量的具体位置由编译器自动分配。
2 o7 p6 `  K' J. D在完成以上两步操作以后,对MDK进行一次编译,在编译成功后,MDK会自动生成一个以工程名命名的.sct文件,存放在Output文件夹里面,如图8.2.3所示:
  G5 q" L4 r+ O# T5 t! D+ }$ W
9 _3 b; o2 B4 A) U ef00c14f4d1b4f88a3910a68e4d8deb1.png
( g) M: F4 S" Z; D9 F' R
; D) v8 d* j8 y! N& V/ f# x图8.2.3 MDK自动生成的分散加载文件
  `; c5 H4 Y3 G3 ^& l打开上图中的test.sct文件,其内容如下:
# U! z% B! y* V: e) R& Q' V3 m* @+ x$ Y/ ~4 R% d, ^! m
  1. ; *************************************************************
    8 x+ g+ Y& q0 z
  2. ; *** Scatter-Loading Description File generated by uVision ***
    $ B) e; j+ e: ~0 M
  3. ; *************************************************************3 j; G7 f6 b+ j* B  [9 u

  4. 1 p7 N7 i7 i! g4 X/ x
  5. LR_IROM1 0x08000000 0x00020000  {    ; load region size_region' N& k+ j3 O* [2 @' @
  6.   ER_IROM1 0x08000000 0x00020000  {  ; load address = execution address
    9 v& V6 y% j- J
  7.    *.o (RESET, +First)
    ) I1 o6 Z/ ^; @% N1 T
  8.    *(InRoot$Sections)$ O8 w- c# d$ m* H3 |/ p* s  @$ N9 S
  9.    .ANY (+RO)6 _% Q: J8 i: Z  O
  10.    .ANY (+XO)0 V2 b$ s0 C0 X4 h$ N3 I8 w
  11.   }4 |: H% D8 Z6 B- a' {
  12.   RW_IRAM1 0x20000000 0x00020000  {  ; RW data
    - Z: M4 s# {3 f$ l
  13.    .ANY (+RW +ZI)* A. F9 g: W8 p6 o8 F$ U7 i
  14.   }
    - U7 O; [2 s' e" o* ^: \- e. J
  15.   RW_IRAM2 0x24000000 0x00080000  {9 E8 S8 t1 T, F+ V
  16.    .ANY (+RW +ZI)
    * ]) g" A% |2 D- M
  17.   }5 E* p: B- F* G4 n& y6 P+ q
  18. }
    : h' D0 x& R+ @# @, d
  19.         LR_IROM1是一个加载域,起始地址为:0X0800 0000,大小为:0X0002 0000。它包含三个运行域分别是:
    , }4 \3 J5 f+ L* z2 O/ F6 Q
  20.         ER_IROM1运行域,起始地址为:0X0800 0000,大小为:0X0002 0000。
    ' E& U# a+ d- ^0 p4 N
  21.         RW_IRAM1运行域,起始地址为:0X2000 0000,大小为:0X0002 0000。
    6 d8 @+ \/ @# U) s8 l
  22.         RW_IRAM2运行域,起始地址为:0X2400 0000,大小为:0X0008 0000。
复制代码
; m# @0 r* u7 {( I7 F. S$ A" M  Z
其中:: v2 ?" T. Z5 m3 ]9 o. b3 Y
4 l" E' I8 A9 H) D7 t8 R& a
ER_IROM1为ROM区域,存放:Code + RO Data + RW Data等只读数据,由:.ANY (+RO)指定,即所有只读数据,都存放在这个区域。
+ p7 A0 U/ q6 T; ARW_IRAM1和RW_IRAM2为RAM区域,存放:RW Data + ZI Data等可读写数据,由:.ANY (+RW +ZI)指定,即所有的可读写数据,都存放在这两个区域,具体存放位置由MDK编译器自动分配。
1 l, A  r1 n% |/ m; w! K& P*.o (RESET, +First):表示优先(+FIRST)将RESET(即中断向量表)段放这个域的起始位置,实际上就是把中断向量表拷贝到最开始的位置。) n' y  A% g2 Z8 R: U

" \" _! C+ w" A7 l(InRoot$$Sections):表示将所有用到的库段放到root区,如:__main.o、__scatter*.o和__dc*.o等。
7 [8 j7 T5 C+ l/ k9 n以上,就是MDK自动生成的.sct文件简介。
5 p, _# }, u$ `* s5 r接下来,我们看用户指定.sct文件的实现方式。选择本章新建工程MDK的魔术棒Linker选项卡里面,进行如图8.3.4所示的设置:0 l& W" |; {7 ~

* [4 q6 b- {/ l 54ff6c39cc27474493884470322305e5.png
' x* L. g/ o% M1 m- _5 l6 y( w; N' B* p4 H4 `
图8.2.4 取消Use Memory Layout from Target Dialog选项
# s3 Q2 R; c, b' z3 X标号①,取消Use Memory Layout from Target Dialog选项,使用用户自定义分散加载文件。
* J8 S6 I; t4 y' a1 F- B2 X标号②,新建工程时,已选择SCRIPT\qspi_code_scf.scf分散加载文件。9 l5 I- _7 H  R
标号③,点击Edit按钮,即可在MDK里面打开qspi_code_scf.scf分散加载文件。( {: U0 U1 v/ p# a2 N
qspi_code.scf的内容如下:  R. C. B$ W3 Y7 C; I: t9 V
* C! q1 s" z# K9 l2 V1 Q
  1. #! armcc -E
    6 Q) }7 z. N" z! x1 I
  2. ;#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc4 V  {! D: T8 J
  3. /* 使用说明
    6 t( t; b6 N! |1 _- y+ {9 k
  4. ! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m7 -xc, 用于AC6编译报错(L6709E错误)时,请使用此设置
    1 u7 I! M0 N, T4 h0 _/ R% h+ C. ^
  5. !armcc -E, 用于AC5编译报错(L6709E错误)时,请使用此设置9 V* t5 y3 y7 L1 B- G" N6 F
  6. 注意,设置必须放本文件第一行!否则还是报错!请注意调整顺序!
    * e, W. s1 r  i
  7. */) [/ x. z8 s6 K9 a, z- O: ?; k7 [3 L

  8. 9 @$ T, j9 U7 S9 F3 N
  9. /**& u3 F! a9 Q6 B$ ~$ A, C( e2 s% s
  10. **********************************************************************************************8 K6 ]$ |7 l$ M. |

  11. ' w- T5 y( j, ]& [
  12. **********************************************************************************************  C* T0 w* }+ c6 d( v
  13. * @attention% P) ^& f* z: w8 I2 Z) I9 K
  14. *
    - X2 ]& X/ K6 L5 P' A+ i2 w
  15. *
    2 i. V/ {3 f$ X0 U  I
  16. **********************************************************************************************6 f  y, `+ v7 P4 Z& G0 y! \( ~' G
  17. */
      |# b7 d7 \( l
  18. $ ?% m: {' P" |( [) W% \; b
  19. #define m_stmflash_start    0X08000000  /* m_stmflash(STM32内部FLASH)域起始地址 */- _- T6 j7 c: |, Z1 E1 Y. i/ Y7 i: d
  20. #define m_stmflash_size    0X20000     /* m_stmflash(STM32内部FLASH)大小,H750是128KB */
    5 B  Z/ \! M' l& V* t
  21. ' g+ `% R6 a+ D# V2 ?0 y' `3 ?
  22. #define m_qspiflash_start   0X90000000  /* m_qspiflash(外扩QSPI FLASH)域起始地址 */* A1 j" Z; _0 h0 }, w/ q
  23. #define m_qspiflash_size   0X800000    /* m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB */0 b8 g# b. o* ]. N+ a4 h5 H

  24. % U2 `: P* @& Q7 F  h# n
  25. #define m_stmsram_start   0X24000000  /* m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM */* x' g: ^5 R- @  G* r
  26. #define m_stmsram_size   0X80000     /* m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB */4 ]: `2 b" n( y. \

  27. + E8 K0 O9 y, z) D
  28. LR_m_stmflash m_stmflash_start m_stmflash_size          /* LR_m_stmflash加载域  */' s" u* S( s$ A9 f+ M4 H8 I
  29. {: r% E( V2 |8 [; d; c: G
  30. /* ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size  */
    / G5 d3 s5 q( [# x# [
  31. ER_m_stmflash m_stmflash_start m_stmflash_size {    ) A3 G6 T( N, r
  32. /* 优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start */2 E8 c3 ~4 ?2 G& Y; C& \1 V
  33.       /* RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的. */0 a4 |0 L: e! [5 c8 D
  34. *.o (RESET, +First)                            - _5 y0 q) \( ^, w* b2 j
  35.         /* 将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等 */9 Z4 B& T2 l; U$ X- U' @' t
  36.         * (InRoot$Sections)                           
    8 y2 h0 \3 ~7 V$ }
  37.         * (Veneer$Code)
    0 Z$ E; l: A4 l0 Q0 ~
  38.         libinit.o7 c* D. d$ d; u0 g
  39.         libinit2.o
    . A3 x2 i4 R2 j8 {# u/ v
  40.         libshutdown.o2 _: [& I, m+ D, R1 n
  41.         libshutdown2.o
    5 w# t; z# i' \% x
  42.         __rtentry.o
    ) g0 b# p' m% L1 ~  b! {  |
  43.         __rtentry2.o* t( g" u# t8 X8 H( y6 e5 m9 q6 J
  44.         __rtentry4.o
    + \. M( r' e8 o$ M* D/ L2 x8 y
  45.         rtexit.o. r+ ^) w2 V& n# t+ P
  46.         rtexit2.o
    % C- l( `6 s" R7 H  ?
  47. $ o$ i: d: h  x$ E! L
  48.         use_no_semi_2.o9 @) L$ o  t% m0 @8 [  A
  49.         heapauxi.o
    + m) u9 y" r  k$ J! K
  50.         use_no_semi.o2 c) ~. E! A" P
  51.         sys_stackheap_outer.o7 c4 a% \/ h) u
  52.         exit.o
    1 J( u5 ^7 m! T8 J; d4 V8 g
  53.         libspace.o
    * l6 \( U2 {! o
  54.         fpinit.o
    ) e: _& e3 U% E0 V2 l" N
  55.         lludivv7m.o
    8 I% ?& m/ B& ^7 c9 n! h  N
  56.         startup_stm32h750xx.o8 T/ b$ m3 t: [  |' t1 N
  57. - L" [5 e, {' x! q9 \4 O" B
  58.         rt_locale_intlibspace.o% l" T) Q4 d. g  O
  59.         lc_numeric_c.o
    + H% Z5 _8 j6 q
  60.         lc_ctype_c.o
    * _# H& \& q: O1 U4 A  H

  61. " ]  g) o5 R8 w! E/ T* f  F( p9 k
  62.         main.o5 E) T. h, g' v+ ]1 B0 t) y
  63.         sys.o  m' M9 N) s6 A8 [
  64.         usart.o
    5 ]5 e/ N  d. k$ E
  65.         delay.o/ K" @0 l& P6 p. ]5 R
  66.         pwr.o& G2 f% r3 T6 f8 }- w% b* P/ @2 B
复制代码
% H4 G. r. v9 H% S5 m  S) y/ n& @
   /* H7的QSPI接口不支持读时写,因此必须把以下3个文件放到内部FLASH,以保证可以9 n. R5 A6 b7 L9 z8 b
对QSPI FLASH的写入 */9 M) X- K4 h* P# ?* r# J+ {. W3 e

% P1 N  `0 F/ r8 g% C
  1.         qspi.o
    - }$ b4 \) Q6 W3 w3 s- W
  2.         norflash.o
    - _5 F- g: A2 U1 t$ k0 f
  3.         norflash_ex.o
    " j+ c9 u$ ~4 D0 h9 {- Z; l
  4. , {; d( m8 g' g. }0 |
  5.         /* 针对HAL库驱动添加到内部的文件 */
    ! O# @, b" Q8 n7 h' u" Y
  6.         system_stm32h7xx.o/ H, x$ q! Q7 _$ H
  7.         stm32h7xx_hal.o
    3 Y! m* j% K# N
  8.         stm32h7xx_hal_cortex.o, w. c, Z! C( _# Y0 x5 D
  9.         stm32h7xx_hal_rcc.o3 I: b2 @5 ^* Q& X
  10.         stm32h7xx_hal_rcc_ex.o& C6 h. Y& J& Q' I6 P7 g
  11.         stm32h7xx_hal_gpio.o
    6 c9 c+ ^, ]9 u  ~
  12.         stm32h7xx_hal_dma.o
    + l- m- ^* M  p9 G3 `. Q2 ~& ?3 H
  13.         stm32h7xx_hal_dma_ex.o
    ! z! `7 M3 }! X4 y! j1 M
  14.         stm32h7xx_hal_qspi.o
    $ [; x6 c- _# h3 A! M
  15.         stm32h7xx_hal_pwr.o+ A% D. ?( \6 A2 r
  16.         stm32h7xx_hal_pwr_ex.o
    7 W; Y4 T4 U. d/ @4 W5 P
  17. }
    4 I; O7 a2 w" ]1 \2 ?. l
  18. /* RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size. */' R4 l, R  K4 x  M4 @
  19.     RW_m_stmsram m_stmsram_start m_stmsram_size {       ( N3 D, e6 C3 Y0 {; o, y
  20.         .ANY (+RW + ZI)                           /* 将所有用到的RAM都放在这个区域 *// H* b7 O* y6 N! ^$ x  D3 {
  21.     }
    % B6 V' ^7 u( }5 _& c  y: ~
  22. }- W; F* y% @6 z& l" z

  23. - _; e2 J+ C8 B& D
  24. LR_m_qspiflash m_qspiflash_start m_qspiflash_size       /* LR_m_qspiflash加载域 */8 G4 H! x9 r7 Y- N
  25. {* R/ O' y( e6 A" [  ?
  26. /* ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size */
    - w: i! C+ L3 t  K
  27.     ER_m_qspiflash m_qspiflash_start m_qspiflash_size {
    8 f! A) U5 m' r% N/ R
  28.         .ANY (+RO)     /* 将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的 */
    + U2 \5 X$ o  M* D) X
  29.     }8 R! ~# q, `" Z. ^# C1 I* r6 s8 X
  30. }
复制代码

  j+ ?* y; d1 M+ N8 r相比于MDK自己生成的分散加载文件,我们自己编写的相对复杂一些,qspi_code.scf分散加载文件包含2个加载域,3个运行域,分别是:
4 p" e- z8 K, ^5 N0 U) QLR_m_stmflash加载域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。它包含二个运行域分别是:
. {6 k/ Y+ G7 `# e5 i5 ]" PER_m_stmflash运行域,起始地址为:m_stmflash_start(宏定义,实际值:0X0800 0000),大小为:m_stmflash_size(宏定义,实际值:0X20000)。
  l% c! g0 f+ e" |RW_m_stmsram运行域,起始地址为:m_stmsram_start(宏定义,实际值:0X2400 0000)大小为:m_stmsram_size(宏定义,实际值:0X80000)。
! W# s* ^8 P2 qLR_m_qspiflash加载域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。它包含一个运行域:" P2 [. D" w: L- p& g
ER_m_qspiflash运行域,起始地址为:m_qspiflash_start(宏定义,实际值:0X9000 0000)大小为:m_qspiflash_size(宏定义,实际值:0X80000)。) K0 x; h9 W& n4 \- F, g) U# H; ]5 t
具体的存储器分配情况为:/ _0 _* A7 f) `) n/ p( D# H
ER_m_stmflash运行域,包含:*.o (RESET, +First)开始到delay.o结束的相关代码,这些代码运行在内部FLASH,可以得到最佳的性能。需要注意的是:这些代码大部分都是必须放到内部FLASH,否则无法正常运行!!
: h* j$ h2 n# XER_m_qspiflash运行域,所有没有在ER_m_stmflash运行域指定的代码,都被放在这个运行域,这些代码运行在外部SPI FLASH,速度比内部FLASH慢一些。+ L3 j3 [; l; @9 f: \
RW_m_stmsram运行域,所有变量及堆栈(RW Data + ZI Data)都存放在这个运行域。' D( v7 V& W/ |4 ^' t9 n5 F) y
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct文件,特意将.ANY ROM区域放在外部SPI FLASH,这样大家在新增.c参与编译时,默认就是存放在外部SPI FLASH的,这样使用起来就更方便。. R" d* Q6 N$ b5 q7 j4 s7 D

& \8 j2 @& o& w6 z; d; L注意事项:
+ x* K3 M7 u; Z6 b- t! ~4 v; [1、如果你新增的代码,对速度有要求,可以将其对应的.o添加到内部FLASH,即放在:ER_m_stmflash运行域。
% @, q; k  A/ l8 q2、如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增的.o放到ER_m_stmflash运行域后(重新编译)再尝试。如果还不行,可以尝试将所有代码都放到ER_m_stmflash运行域后再尝试。0 U0 {, }- f' j) c# H( D
至此,分散加载文件就给大家介绍完了。
4 Q8 N# m6 b1 ~3 @————————————————+ x/ u4 E" N4 A& ?; h2 K4 t  o: t
版权声明:正点原子
# y* ?5 K0 A8 w4 `( x, x; u" x2 a! T6 E
3 d$ r1 ^3 A' n+ {; I8 h
+ @/ q. k' }* ?. z7 I+ _1 l) j  H
收藏 评论0 发布时间:2022-10-5 21:06

举报

0个回答

所属标签

相似分享

官网相关资源

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