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

【经验分享】STM32H7启动过程详解

[复制链接]
STMCU小助手 发布时间:2021-12-21 22:00
13.1 初学者重要提示. ~, u) v& n8 C. [. o
1、  如果觉得学习本章节吃力的话,推荐看我们早期做的入门视频教程第8章,同样适用于STM32H7。6 \# I: t& t: \8 E. T

/ b6 Y% v5 Y; u% x2、  相比F1,F4的启动方式,H7的启动方式更灵活些,只需一个boot引脚即可。但是一个引脚只能区分出两个状态,为了解决这个问题,H7专门配套了两个option bytes选项字节来解决此问题。7 K- k5 M% a, C

& d2 x# Y% l5 g4 h' s2 b( ]13.2 各个版本的启动文件介绍/ N0 t* i+ l0 ?
这里各个版本的意思是指不同的编译器、不同的H7系列对应的启动文件。5 y" h) f, N1 m; w* c$ T* J! Z. U

) R2 c2 G4 y3 q, [# v13.2.1 不同编译器对应的启动文件
7 E* \  I3 `( v' _: |5 C: `打开我们为本教程提供的工程文件,路径如下:' {2 z' q0 g# O3 ]6 g& W- K" E) g

$ A7 x) l% [# j3 t! F- H0 g6 n/ G: ~3 t\Libraries\CMSIS\Device\ST\STM32H7xx\Source\Templates 在这个文件里面有ST官方为各个编译器提供的启动文件。% K0 z! G+ u; C% O- r

/ ?) B7 Q9 }7 }" Z) y
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

" i* o& j) y& E" x( q( H4 _% D( l% o/ z' i
看了上面的截图,大家会问怎么没有KEIL MDK呢?其实已经被放在了文件夹arm里面,KEIL公司已经在2005年被ARM公司收购了。开发板大部分例程都是配套了MDK和IAR两个版本,这里重点给大家分析一下MDK的启动文件分析,IAR和MDK的大同小异。; r$ W/ i% ~/ ~
& j7 y, e3 `! g- N
13.2.2 不同H7系列对应的启动文件
/ ^! b" B( t/ l( t/ m: N$ p先来看一下ARM文件夹里面的文件(2018-07-03,当前只有如下两个系列,后期ST会增加新的型号,相应的启动文件也会添加进来):. @- _6 L/ C, l4 i( W- w

; |& H% |% G' U: s6 a
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

% }/ ^. _8 U1 K1 m" u) B/ \
' o2 ?5 ^! R) F如果是H743系列,就使用startup_stm32h743xx.s文件,如果是H753系列,就使用startup_stm32h753xx文件。当前H743和753系列对应的型号如下:
* R9 f4 j2 Q6 H4 ^1 J! E* d+ C, J5 g5 y4 r; ~
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
$ J6 W; Q$ `3 g6 F
) i# X9 p1 H" U) W2 W& h
我们再来打开IAR文件夹里面的文件:. b% O# I3 I3 U3 P# J' R
( P9 d4 \* M4 {8 i: |/ F
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

5 C0 L. H* Q, [% U$ b( T% m
) p- H- n; `4 W: m4 [: t多了一个linker文件夹,用于IAR配置的ICF文件:
# o& I' ]5 f. ?" y5 c3 z7 W
5 d# k2 g% k; `
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
% i) X/ |4 `* j  d6 U
" e: P+ Y$ q. @/ C" p
而启动文件跟MDK里面的一样,一个是用H743系列,另一个是用于H753系列。' t( V" @$ i& p0 ]% y& \9 S

0 J2 u" }9 C5 ?8 m3 Y) b' N! z13.3 启动文件分析
  q% ^. C( y4 r: h- O鉴于V7开发板使用的是STM32H743XI,下面我们详细的分析一下启动文件startup_stm32h743xx.s。分析前,先掌握一个小技能,遇到不认识的指令或者关键词可以检索。
$ i% }, O. k/ ~+ s
6 |/ F; ?" ^5 [0 _2 n; {, N 启动 MDK软件,在Help菜单点击 uVision Help+ n- b1 ~* ?7 ]2 c

+ h1 [! W! K4 e  E: Z! u
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
& [9 P; _: M  X3 d0 `0 |
, u! o( w# v8 W% c, C+ {
  点击后弹出如下文件" I1 {/ m' ^2 n. \* n& q& `
  O/ j7 z/ A; P7 G# P
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

$ z* s$ ?6 O* l8 S( @* x2 ]& g
. R& [/ K( k) L4 L在搜索栏输入你需要查询的单词进行查询,然后点击“列出主题”按钮,会将相关的知识点都罗列出来。此功能非常实用,建议熟练掌握。
4 A1 N0 D1 Z3 q: N. C8 r' W. \% u* k: O
下面先来看启动文件前面的介绍 (固件库版本:V1.2.0): ^+ [/ }+ v; ^% E7 @$ R
! U& E9 M/ [: q4 f( ^  J' L$ J
  1. ;******************** (C) COPYRIGHT 2017 STMicroelectronics ********************9 o: Q* m7 o8 t9 G+ B/ f
  2. ;* File Name          : startup_stm32h743xx.s/ E- r) e5 o% C5 e8 {7 [, ?+ Z
  3. ;* @author  MCD Application Team
    5 W6 M3 K0 W5 k  L) s) @. i
  4. ;* version            : V1.2.00 H  \3 R6 ^* b8 M1 c
  5. ;* Date               : 29-December-20175 s( M7 E0 _4 i! C
  6. ;* Description        : STM32H7xx devices vector table for MDK-ARM toolchain.
    4 G$ }* q4 n6 {
  7. ;*                      This module performs:
      [; e; o6 w0 h( i7 f& C
  8. ;*                      - Set the initial SP! |" F  _% w# G0 P9 |6 ?
  9. ;*                      - Set the initial PC == Reset_Handler
    ( j3 C, z+ G5 w9 ^
  10. ;*                      - Set the vector table entries with the exceptions ISR address6 h' M% ], E# K
  11. ;*                      - Branches to __main in the C library (which eventually( W. U+ s5 U  F$ u3 i( l8 ]. d$ T) l4 f
  12. ;*                        calls main()).
    , s) {7 i% x1 t% _2 x. Z
  13. ;*                      After Reset the Cortex-M processor is in Thread mode,
    5 a; Z% k/ O' }
  14. ;*                      priority is Privileged, and the Stack is set to Main.
    " K: z' |$ t* d# ^5 I- X
  15. ;* <<< Use Configuration Wizard in Context Menu >>>   
    . n. \7 e2 ~: m7 N: X1 v
  16. ;*******************************************************************************
    6 W6 t' {" g2 ^6 s& ^; O* J
  17. ; 5 Q0 D$ ~2 I) x0 I/ N( q
  18. ; Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
    6 e' R8 ?% B5 t% P
  19. ; You may not use this file except in compliance with the License.
    ; G9 {# M9 A- u3 I7 t9 [
  20. ; You may obtain a copy of the License at:
    6 E" z- M: a$ _; Z" V
  21. ;
    - |; J; N$ z) S& B2 Y
  22. ;        <a href="http://www.st.com/software_license_agreement_liberty_v2" target="_blank">http://www.st.com/software_license_agreement_liberty_v2</a>+ v% T0 Y" Y6 D
  23. ;
    5 ?  R' p* {# n( R/ G3 Z- w8 Z
  24. ; Unless required by applicable law or agreed to in writing, software
    % i9 ?0 ?! C% w5 u8 t/ d3 v
  25. ; distributed under the License is distributed on an "AS IS" BASIS,
    : c* F" M% E% v" F) C( N! j
  26. ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    3 |* t  f1 }- Q6 i* o
  27. ; See the License for the specific language governing permissions and9 O4 p0 W- G9 M6 _/ t9 u% A
  28. ; limitations under the License.
    & P- I  S$ }& U) l$ P# H9 H8 H, h' z
  29. ;
    : f& G" a9 z  E. d3 q
  30. ;*******************************************************************************
复制代码
) q- K6 ?. g% }" E$ c4 p
启动文件是后缀为.s的汇编语言文本文件,每行前面的分号表示此行是注释行。
% Z, F5 F! z/ v+ x7 A+ z
2 b3 T$ F) l/ K) l, k: i( w启动文件主要完成如下工作,即程序执行过程:
( Y; P" Q0 y! d: R; C
+ G* U* p; h9 `, {7 U-      设置堆栈指针SP = __initial_sp。! @; Z5 o) \" O( E' q
7 u1 O! m$ v% v' _6 ]7 W
-      设置PC指针 = Reset_Handler。! K2 o. i2 ]( B5 }! M' z: k' \' s8 i& O

: v. Y3 s9 o' y! m! C( Q-      设置中断向量表。
( P/ I( Q3 g" }6 s. L* N& `+ ^0 [4 d6 \8 T5 c& M, G; f! I4 a
-      配置系统时钟。& z% i8 Z. ]: V  [! A
' B5 W8 Y. n) H( w
-      配置外部SRAM/SDRAM用于程序变量等数据存储(这是可选的)。* w1 u9 h" C0 S' q8 f4 n

( q- v, b" c; {0 C2 y7 v-      跳转到C库中的 __main ,最终会调用用户程序的main()函数。
* ^8 {8 i5 \7 W
4 k9 Q9 k: }+ u6 r/ eCortex-M内核处理器复位后,处于线程模式,指令权限是特权级别(最高级别),堆栈设置为使用主堆栈MSP。
9 x" L% n" r3 x/ |4 \+ d& a6 u) N8 _
13.3.1 复位序列
3 s6 V7 K2 o# l! h. P8 u硬件复位之后,CPU 内的时序逻辑电路首先完成如下两个工作(程序代码下载到内部flash为例,flash首地址0x0800 0000)
" w4 ~0 T- A+ z7 `& Y
4 r# t: X( a4 |. z3 ?$ T" P  将0x08000000位置存放的堆栈栈顶地址存放到SP中(MSP)。
- f$ T0 n1 x/ p$ J9 B  将0x08000004 位置存放的向量地址装入 PC 程序计数器。' g$ I+ y* o1 T, T
CPU 从 PC 寄存器指向的物理地址取出第 1 条指令开始执行程序,也就是开始执行复位中断服务程序 Reset_Handler。
9 i% i( ~$ Y+ R, O* \% l6 L4 H! Y0 A9 L5 X/ b# P) J5 a$ b4 T: N. A
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

- h* b/ a; W) _$ K$ S' G6 W4 l; ?6 d8 B
复位中断服务程序会调用SystemInit()函数来配置系统时钟、配置FMC总线上的外部SRAM/SDRAM,然后跳转到C 库中__main 函数。由C库中的__main 函数完成用户程序的初始化工作(比如:变量赋初值等),最后由__main 函数调用用户写的 main()函数开始执行 C 程序。+ s/ u5 q# \( e4 O! j) q6 ~
4 {) L7 g7 N& g5 S
13.3.2 代码分析$ ?9 l! `% |$ j
  第1部分代码分析
+ r, y4 h  D8 S; j/ ?下面的代码实现开辟栈(stack)空间,用于局部变量、函数调用、函数的参数等。6 ~2 `9 w) M+ K0 Z' ^! l
5 M0 v# G9 ?) W( b( Q, C
1.        ; Amount of memory (in bytes) allocated for Stack
* v7 n/ N, b$ C" p0 u2.        ; Tailor this value to your application needs! B6 b% J% C" X" x; D8 s6 E7 T
3.        ; <h> Stack Configuration
* T# z: j6 Q4 \/ v) H; C* }4.        ;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>0 _' k$ R7 D8 |; |4 s+ v% ?
5.        ; </h>  i( c  E, d, Q+ U# E
6.        . p. X. Y; h% ~+ i
7.        Stack_Size      EQU     0x00000400$ E$ g. ^5 Q2 A: ]
8.        
" j- \4 V0 ]0 `" u2 z) B; V& w9.                        AREA    STACK, NOINIT, READWRITE, ALIGN=3
6 H% D/ g' m4 x% B$ @( o5 f10.        Stack_Mem       SPACE   Stack_Size
" D  `6 X$ r- T11.        __initial_sp* G" p6 N7 S2 U  k; f3 S6 o
第7行:EQU 是表示宏定义的伪指令,类似于 C 语言中的#define。伪指令的意思是指这个“指令”并不会生成二进制程序代码,也不会引起变量空间分配。
) z4 w4 N! l5 v0 E# z' ]
( k( X" `% x4 [" k- k. T0x00000400 表示栈大小,注意这里是以字节为单位。
. _/ E5 x' x. L) f3 w6 ^1 U. O
5 S  Z! N! W' O- n" \$ G1 d+ T6 Q5 r2 t% f

% R; `/ Y- _; v3 g) ^8 j" d9 r' U第9行:开辟一段数据空间可读可写,段名 STACK,按照 8 字节对齐。ARER 伪指令表示下面将开始定义一个代码段或者数据段。此处是定义数据段。ARER 后面的关键字表示这个段的属性。
! X# E: T% q; j: p  v
$ U% u0 [+ b( @6 `- Q2 YSTACK :表示这个段的名字,可以任意命名。  N1 R) q8 U  e. T4 I/ k

( [3 V- @+ @( o' e# U: G: e# wNOINIT:表示此数据段不需要填入初始数据。
5 i5 g5 c4 h# n: F* s5 m/ J3 V8 V$ i; K, [2 e1 c4 b
READWRITE:表示此段可读可写。4 N- b2 o5 N- V8 ^7 i" a$ e
# E$ D6 c9 ^: H& O9 S7 |, x2 a
ALIGN=3 :表示首地址按照 2 的 3 次方对齐,也就是按照 8 字节对齐(地址对8求余数等于0)。
  R7 s3 h( P" g" A% Z. H
4 z( i& X+ t3 b) S( {; Q- Y7 T- y. n) j/ g: G, w" y8 O: ^% _
第10行:SPACE 这行指令告诉汇编器给 STACK 段分配 0x00000400 字节的连续内存空间。9 H. m8 A: `8 F
( |3 ^# R8 q0 {) F7 i0 J
- O+ h, V8 f- t6 q# O
第11行: __initial_sp 紧接着 SPACE 语句放置,表示了栈顶地址。__initial_sp 只是一个标号,标号主要用于表示一片内存空间的某个位置,等价于 C 语言中的“地址”概念。地址仅仅表示存储空间的一个位置,从 C 语言的角度来看,变量的地址,数组的地址或是函数的入口地址在本质上并无区别。
, H0 R. f- i/ q+ z" c6 k9 ~
) |1 c, Q0 }% ]7 W5 K: @
$ _- @+ N7 y4 ?% L- f% K$ {8 ?5 h3 H
& u# H% u, n0 `6 U第2部分代码分析6 }. y+ \& R; f. W8 D: L
下面的代码实现开辟堆(heap)空间,主要用于动态内存分配,也就是说用 malloc,calloc, realloc等函数分配的变量空间是在堆上。2 ~. w( c2 N+ z; x5 o

  `# [7 d3 F4 W) c1 X
  1. 1.        ; <h> Heap Configuration
    4 I6 M  t4 b& {1 |) v9 k" |
  2. 2.        ;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>: }/ v1 W7 Q+ s0 T/ B& h! D$ x
  3. 3.        ; </h>
    8 P% G$ \# U% ~" D3 d: q: z: G4 a
  4. 4.        
    $ m- v% H$ k  u% \3 w
  5. 5.        Heap_Size       EQU     0x00000200+ Z+ [1 J4 y/ q% f
  6. 6.        
    . O! h/ h& O' X0 C& ~0 `
  7. 7.                        AREA    HEAP, NOINIT, READWRITE, ALIGN=3
    & }2 X9 m4 ]' U" c
  8. 8.        __heap_base
    * @* O0 K1 X9 e1 o% {! K" c
  9. 9.        Heap_Mem        SPACE   Heap_Size/ Z2 R1 O5 T$ N# u1 y7 w
  10. 10.        __heap_limit
复制代码
& c6 \. j6 b5 c
这几行语句和上面第1部分代码类似。分配一片连续的内存空间给名字叫 HEAP 的段,也就是分配堆空间。堆的大小为 0x00000200。
/ Z$ v' @- g. @! U7 \) k2 V1 A* F3 [% y, O6 g0 t5 U
__heap_base 表示堆的开始地址。4 I: `8 i; o( n- p# G

* }- B+ h0 U4 `/ E) x' a. ^__heap_limit 表示堆的结束地址。0 C) f& G! Q7 r' l* @- z7 R
2 q0 k5 V, n! l1 C: G7 ~
  第3部分代码分析
$ Q; e5 s2 @4 I
  1. 1.                        PRESERVE8% u) y$ I2 w* M' Y1 h( L
  2. 2.                        THUMB$ o$ Q6 e) @$ {+ r, D6 t0 @" R6 I, ~
  3. 3.        5 o- H( Q  F1 E- c
  4. 4.        ( z9 X# Q! w5 O. H3 c$ `
  5. 5.        ; Vector Table Mapped to Address 0 at Reset
      _% m/ V: y1 H8 x0 H2 `5 e, l! }
  6. 6.                        AREA    RESET, DATA, READONLY
    ' i/ ~/ B: P- ]! W
  7. 7.                        EXPORT  __Vectors* L5 Y! H. e9 r. D4 {# P/ ^. `3 J
  8. 8.                        EXPORT  __Vectors_End
    3 q7 S) ~# j" C0 c" F  A- b
  9. 9.                        EXPORT  __Vectors_Size
复制代码
$ L$ m8 e8 U" C8 A
第1行:PRESERVE8 指定当前文件保持堆栈八字节对齐。4 i( P# D; F0 [0 K8 q8 Q! @. i

/ p' d# E$ l( X4 |# b3 q6 l第2行:THUMB表示后面的指令是THUMB指令集 ,CM7采用的是THUMB - 2指令集。
* A! }) k0 H' V, |' l" N$ W/ H. R. q4 E
第6行:AREA定义一块代码段,只读,段名字是 RESET。READONLY 表示只读,缺省就表示代码段了。
) W- [/ D, b& \9 W( q8 |& Y: e: B, b) P4 H
第7-9行:3 行EXPORT语句将 3 个标号申明为可被外部引用, 主要提供给链接器用于连接库文件或其他文件。
& I" p/ |  |4 L2 K( h9 q  ~8 m9 R) ^' \$ e9 j& g

6 h" b# A+ R9 c& r; ?  第4部分代码分析. N1 O. \4 w5 ?- b7 X! s
  1. 1.    __Vectors       DCD     __initial_sp                      ; Top of Stack# k1 ]$ s; {% |- V; E4 i5 O# l) u- y
  2. 2.                    DCD     Reset_Handler                     ; Reset Handler* J) t" k! z& ^- w+ {0 g
  3. 3.                    DCD     NMI_Handler                       ; NMI Handler
    " d) ^3 `( G. U- w8 O
  4. 4.                    DCD     HardFault_Handler                 ; Hard Fault Handler. ?7 U) l5 o0 C, f0 s
  5. 5.                    
    8 L- s7 T( Q) _* {; J
  6. 6.                    中间部分省略未写& S0 p% V5 D7 a! i% W1 C
  7. 7.   
    3 |5 F1 C" W9 ^8 L4 r: g: ^
  8. 8.                    DCD     0                                 ; Reserved                                    
    0 w# |( Z; w( Q$ \' c6 p
  9. 9.                    DCD     WAKEUP_PIN_IRQHandler             ; Interrupt for all 6 wake-up pins ) B( y  A3 ?* L( e$ S
  10. 10.                    ' Z  `  b- h3 J& G1 Y3 F) L% H
  11. 11.   
    8 j7 g% D2 J5 a. ]
  12. 12.    __Vectors_End. ~1 B: W: r. \" ?
  13. 13.   
    # Y: L* t' }% a  [
  14. 14.    __Vectors_Size  EQU  __Vectors_End - __Vectors
复制代码

4 ]5 [  O# ?/ T; ~5 \上面的这段代码是建立中断向量表,中断向量表定位在代码段的最前面。具体的物理地址由链接器的配置参数(IROM1 的地址)决定。如果程序在 Flash 运行,则中断向量表的起始地址是 0x08000000。6 S7 u5 W2 c1 r3 E2 w
; U2 F' [, j( D0 n
以MDK为例,就是如下配置选项:+ I& Z) R" J# ]1 m% K- O, N
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

4 q. }* R- [8 O7 U! X# o9 `4 D$ C3 W, V+ W2 g. ?: x
DCD 表示分配 1 个 4 字节的空间。每行 DCD 都会生成一个 4 字节的二进制代码。中断向量表存放的实际上是中断服务程序的入口地址。当异常(也即是中断事件)发生时,CPU 的中断系统会将相应的入口地址赋值给 PC 程序计数器,之后就开始执行中断服务程序。
3 i. @1 {& ?' l0 A! `3 \
, H. X* Q6 q7 J: E/ {- P8 S  _1 c  第5部分代码分析
- y- d4 j) I# ^; Z. u9 S
  1. 1.                    AREA    |.text|, CODE, READONLY" c/ S5 M6 P% Z8 K
  2. 2.    $ Z' O7 H$ }! ?; ~! c
  3. 3.    ; Reset handler0 D# H& e% ~. {- Q& j$ W
  4. 4.    Reset_Handler    PROC
    5 t; i, N( R1 S7 Y- J+ c
  5. 5.                     EXPORT  Reset_Handler                    [WEAK]
    ) |  R* X1 ^6 h4 C5 G; ]2 Q
  6. 6.            IMPORT  SystemInit
      \9 o: q$ f9 U$ d3 d* [# i
  7. 7.            IMPORT  __main
      A  X6 V9 g7 r6 H/ G
  8. 8.    2 C4 Y; Q& b4 d1 ~& u  E& }
  9. 9.                     LDR     R0, =SystemInit
    4 J" Q: T) Q$ m5 D" M1 c. d( i
  10. 10.                     BLX     R0  t8 ^' ^7 U7 z0 J  y6 |: ~
  11. 11.                     LDR     R0, =__main
    / p6 y5 }& O- `" m) K" A8 X- \
  12. 12.                     BX      R0
    & F* o3 B( }* l7 l
  13. 13.                     ENDP
复制代码
( G8 b7 {( p3 [5 U7 U  Y+ ]
第1行:AREA 定义一块代码段,只读,段名字是 .text 。READONLY 表示只读。
# i& v' j3 P0 A+ e2 w! j: I0 |
, l- D0 a& @' f! P4 l! d/ h9 ]2 F第4行:利用 PROC、ENDP 这一对伪指令把程序段分为若干个过程,使程序的结构加清晰。
: m7 b+ E  ]4 ^  Z- Y/ _
- e8 f! x8 {9 q% L1 u8 [第5行:WEAK 声明其他的同名标号优先于该标号被引用,就是说如果外面声明了的话会调用外面的。 这个声明很重要,它让我们可以在C文件中任意地方放置中断服务程序,只要保证C函数的名字和向量表中的名字一致即可。
2 W7 M) }0 w+ ?% {) ^3 B5 x+ @+ g# z: K' p! q
第6行:IMPORT:伪指令用于通知编译器要使用的标号在其他的源文件中定义。但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。1 |9 o) ~5 t: a" L  r9 p
* Z) @$ V  S6 Z
第9行:SystemInit 函数在文件system_stm32h7xx.c 里面,主要实现RCC相关寄存器复位和中断向量表位置设置。3 M7 Q2 V; N  S

' p3 U, H5 U8 m7 M. Q+ N第11行:__main 标号表示C/C++标准实时库函数里的一个初始化子程序__main 的入口地址。该程序的一个主要作用是初始化堆栈(跳转__user_initial_stackheap 标号进行初始化堆栈的,下面会讲到这个标号),并初始化映像文件,最后跳转到 C 程序中的 main函数。这就解释了为何所有的 C 程序必须有一个 main 函数作为程序的起点。因为这是由 C/C++标准实时库所规,并且不能更改。
. J+ P, [6 M7 M+ f
, k! [/ E/ M  ~! ?* y" N: V  第6部分代码分析' Z! V6 V' B" F3 f, [6 `7 B
代码如下:4 y, H4 ~7 x9 ?$ U

3 X* B% P0 r$ e$ k
  1. 1.        ; Dummy Exception Handlers (infinite loops which can be modified)
    0 e* H+ N- J1 I& h# Z
  2. 2.        # _( O8 I9 J' Q& `* K
  3. 3.        NMI_Handler     PROC
    # d4 F# I# @: r- j+ z  z, S
  4. 4.                        EXPORT  NMI_Handler                      [WEAK]7 }7 j3 K9 m. ]9 D# v, s# U2 x
  5. 5.                        B       .  
    $ n. m& [0 r/ z) A' m
  6. 6.                        ENDP  ?9 |9 `! W2 Q( M4 T
  7. 7.        HardFault_Handler\7 {0 A3 M" I& Z0 {
  8. 8.                        PROC- S" Y6 J, U, \" M$ j
  9. 9.                        EXPORT  HardFault_Handler                [WEAK]2 n7 i6 b* H+ K
  10. 10.                        B       .% T/ A- d( s$ r( w, W) a5 @4 ?
  11. 11.                        ENDP
    6 ^) G. B4 C% i" s0 x
  12. 12.        7 w, X" w. {! n& Y' N# P/ C
  13. 13.                        中间部分省略未写
    3 A/ H2 t+ a, V( ?
  14. 14.        Default_Handler PROC                                      / h( A2 e# C! n- ]9 u- _
  15. 15.        
    & v6 G& G. G8 ^2 m! \2 a# a1 c
  16. 16.                        EXPORT  WWDG_IRQHandler                   [WEAK]                                       2 n( J* g; `2 I7 k$ U/ j4 H& m
  17. 17.                        EXPORT  PVD_AVD_IRQHandler                [WEAK]                         ( t1 g9 X/ ~" }, I
  18. 18.                        EXPORT  TAMP_STAMP_IRQHandler             [WEAK]
    . b" R  a4 {' {3 D
  19. 19.                        中间部分省略未写
    / ]! M- u4 v& G
  20. 20.        SAI4_IRQHandler      
    + B" [/ T! d' I$ K. W
  21. 21.        WAKEUP_PIN_IRQHandler
    # ?) ^0 O. H- @8 {6 w
  22. 22.        
    2 m5 j- |1 `, R3 j% R) ^6 a
  23. 23.                        B       .5 ~0 }  V5 U' d  y9 z6 _
  24. 24.        
    - r# f0 j* J+ p
  25. 25.                        ENDP
    0 t: |: x0 r/ P
  26. 26.        , X4 _1 Q# z& C6 u. T) v3 c
  27. 27.                        ALIGN
复制代码

. w2 [3 Z( Z( c, t: e9 o, E/ ~9 `第5行:死循环,用户可以在此实现自己的中断服务程序。不过很少在这里实现中断服务程序,一般多是在其它的C文件里面重新写一个同样名字的中断服务程序,因为这里是WEEK弱定义的。如果没有在其它文件中写中断服务器程序,且使能了此中断,进入到这里后,会让程序卡在这个地方。
+ Q5 Q+ I7 n  F1 b5 I5 X+ b
; T& ~( ^6 T' r$ h9 z, G0 z第14行:缺省中断服务程序(开始)
7 q& U8 m% E) J3 ?/ o3 i5 V) x8 g- q7 ]' p  D
第23行:死循环,如果用户使能中断服务程序,而没有在C文件里面写中断服务程序的话,都会进入到这里。比如在程序里面使能了串口1中断,而没有写中断服务程序USART1_IRQHandle,那么串口中断来了,会进入到这个死循环。) V  f( V: w$ A$ m- O

* w& `- G  {& a; ]第25行:缺省中断服务程序(结束)。/ ~, @, J% Z% w8 Z

$ m& w6 R+ D/ r+ w  第7部分代码分析
% y4 B! W/ n. I$ }( s启动代码的最后一部分:
7 A: G3 [$ K8 T( I; b& q
  1. 1. ;******************************************************************************* 9 q3 l* A, @5 s8 k5 y
  2. 2. ; User Stack and Heap initialization
    3 z" ~* X+ j: b/ L7 l# d+ Y
  3. 3. ;*******************************************************************************+ z6 Q$ k: v: ?+ C
  4. 4. IF :DEF:__MICROLIB
    # Q) g0 |- W! r" a+ s' n
  5. 5.
    . m& D, _8 u5 e8 o0 U% R6 t* {) p
  6. 6. EXPORT __initial_sp 0 P$ V# A  ]/ u: o; I* H$ V
  7. 7. EXPORT __heap_base / ^1 i* h; B6 m6 u3 R
  8. 8. EXPORT __heap_limit
    / Q1 R# U# s$ C- V- [4 m/ a0 ~
  9. 9.
    2 a/ O7 U: z4 g" m  q
  10. 10. ELSE # p6 H" \- D6 w$ s
  11. 11. : I# \1 u/ g- v& F3 i' Z. Q% a
  12. 12. IMPORT __use_two_region_memory # ]9 \* r8 `' Q* k" F0 s; a
  13. 13. EXPORT __user_initial_stackheap
    ) g( f& ^$ R! K8 R7 s( o; Y
  14. 14.
    & S" [5 B" @1 ?
  15. 15. __user_initial_stackheap
    * N0 K8 j+ C5 y$ v. f: V# ?
  16. 16.
    , `1 t$ v- u, P% }; U
  17. 17. LDR R0, = Heap_Mem 8 Y, K: @3 R) e' Y2 }3 I" r/ U5 S
  18. 18. LDR R1, =(Stack_Mem + Stack_Size) 19. LDR R2, = (Heap_Mem + Heap_Size)
    ) G/ D( x5 H. c4 S# g9 K
  19. 20. LDR R3, = Stack_Mem ! F' d) I2 }; c
  20. 21. BX LR
    $ B( a0 J7 [0 n8 ~
  21. 22. ( u  H& A3 T) [2 O) F1 r4 s
  22. 23. ALIGN $ C' x& n( @) A" x0 ]5 `9 W
  23. 24. 7 N! B& ]' j: P: u+ L0 x+ \4 p
  24. 25. ENDIF
    " N0 r. _) C( U- n4 K: T
  25. 26.
    $ C& O/ }( e8 a3 B0 w: S
  26. 27. END
复制代码

' a  K  ^* Q' D* V第4行:简单的汇编语言实现IF…….ELSE…………语句。如果定义了MICROLIB,那么程序是不会执行ELSE分支的代码。__MICROLIB可能大家并不陌生,就在MDK的Target Option里面设置。
. e9 A: ~+ Z( A9 S2 S
8 r2 ~) @, J) k$ T/ J+ R$ Y7 T5 x
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png

# }" s, r6 F* c. U# K7 u+ [& G' M
第5行:__user_initial_stackheap将由__main函数进行调用。. \! K: f9 m9 [) N2 `( T9 }

9 ~- P4 o3 |* P3 a* |7 u! x) X  I+ a/ z MicroLib
; }( U( `; q/ P1 C0 G+ E, DMicroLib是MDK里面带的微库,针对嵌入式应用,MicroLIB做了深度优化,比使用C标准库所需的RAM和FLASH空间都大大减小比如调用:
& o# E1 O. [7 \! `+ n! q5 I2 C' E7 j
0 B- w8 J5 q4 ]" E$ \<mat h.h>,<std lib.h>,<stdi o.h>,<stri ng.h>
- G- E! z$ H* G! E6 V. W! x- h3 K& M8 I7 {- A; d) d
另外注意microlib只有库,没有源文件。下图是标准库和微库生成代码的比较。% b0 U8 r# E8 F0 n0 n0 [" U
+ m" Y  t+ C4 V0 u8 C
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
, R0 d3 d5 o8 f6 V6 S+ H5 Y$ l
  z/ e2 X1 L2 l9 K( x% D
13.4 BOOT启动模式& s- b; E5 ^/ Z9 z5 N7 f' x
相比F1,F4的启动方式,H7的启动方式更灵活些,只需一个boot引脚即可。但是一个引脚只能区分出两个状态,为了解决这个问题,H7专门配套了两个option bytes选项字节配置,如此以来就可以方便设置各种存储器地址了。' t' g# @2 ]- ?! h/ P+ ?

  t8 W5 Y8 A. {
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
* L: T( C2 [. n; G9 I+ G; K

2 L2 q" U  I4 Z3 o- |5 NBOOT_ADD0和BOOT_ADD1对应32位地址到高16位,这点要特别注意。通过这两个选项字节,所有0x0000 0000到0x3FFF 0000的存储器地址都可以设置,包括:
2 G* G+ r5 p% n) R% m6 B7 |  j
& F+ w; L/ O/ x8 w' y3 S' M9 f  所有Flash地址空间。
# L9 [3 O) z. b- a6 P- l$ c4 w/ N  所有RAM地址空间,ITCM,DTCM和SRAM。& \$ }3 i+ E8 S1 T) r
设置了选项字节后,掉电不会丢失,下次上电或者复位后,会根据BOOT引脚状态从BOOT_ADD0,或BOOT_ADD1所设置的地址进行启动。
7 C1 g$ s9 I$ z# X3 b% M) r/ R
使用BOOT功能,注意以下几个问题:7 K3 Y# i% s, S

7 R  _+ j* m2 v- B  如果用户不慎,设置的地址范围不在有效的存储器地址,那么BOOT = 0时,会从Flash首地址0x0800 0000启动,BOOT = 1时,会从ITCM首地址0x0000 0000启动。. A* I4 L" ?  ?1 ~+ B4 v
  如果用户使能了Flash Level 2保护,那么只能从Flash地址空间进行启动。
5 K6 Y. v4 A9 {  A, D" O: R/ ^- O, O6 |; g" Z2 y
& J3 a* l" M  I* U
  F1,F4的启动方式
! j! E1 b+ D: r2 k. Z& e作为对比,这里补充F1,F4的启动方式,由BOOT0和BOOT1引脚共同决定。" v8 t7 a1 d' [- ^! m& z

& F1 h2 w0 k0 S: Y
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDE5MDQvMTM3OTEwNy0yMDE5.png
" T# [" V. C7 K3 n

4 L% g+ t9 [4 g4 Z13.5 总结5 b% ~# B, |* N( X. j
本章节讲解的启动过程分析还是比较重要的,忘初学者务必掌握。, i! E; c4 M! a  M6 {3 x( M
" k9 t% f0 A! z. ]3 y; W

% |1 s4 }  I% _% _0 j7 C
( v7 L# n4 H8 G6 O
收藏 评论0 发布时间:2021-12-21 22:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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