在刚开始学习单片机的时候,一直以为程序启动后就直接进入到了main函数,但是随着学习的深入才发现,程序在进入main函数前其实还要干好多事情。现在就来分析一下,STM32系列单片机程序在进入main函数前都在干些什么?1 p- m4 i. V% l8 n/ K. m2 ^
3 d6 c1 C" i5 K2 b6 e5 t
7 J5 y8 i6 T! n$ F. H0 q1 l
, B2 p3 N% S0 z( g; I5 x
单片机上电后,程序首先跳转到地址0处,此时主堆栈指针MSP的初值也为0。然后单片机产生了复位信号,主堆栈指针加1,由于单片机内核为32位,所以地址增加一位,实际上是增加了32位,也就是增加了4个字节。此时MSP指针就指向了复位向量。而Cortex-M内核处理器的向量表可以重新定位,所以此时程序就会跳转到复位向量重新映射的地址处。
; D$ Z' L7 H* C N8 o- c: ~1 ~6 J: y9 O3 w, o$ O
9 h. r+ Y" N; Q% O, m
; c/ z4 P; k; s
: }+ D: ~6 [& i% z
通过上面的两个图可以看到,通过复位向量的重映射后,MSP指针就会跳转到复位向量处,在STM32系列单片机中,复位向量的函数通常在启动文件startup_stm32f10x_xx.s中实现。
9 b# R$ ?4 f5 D+ g: l
& m- L' d1 ~7 E& [$ S3 D下来接着看一下复位函数都实现了哪些功能。
, D0 P& l8 ]; a4 D
: T# Z: ]# ~& E8 N& J& a1 D7 q5 v
3 o& z0 n6 M; i. ~$ Z: e$ e& H
/ y5 S$ k6 C9 l7 v1 S" e 可以看到复位函数首先获取到了main()函数和系统初始化函数SystemInit()的地址,然后跳转到系统初始化函数中SystemInit()中,接着就会跳转到main函数中。7 n* Z. r; E' ?- d
" s$ Q, z1 C' H2 r( S2 f! P- void SystemInit ( void )
# b _% r9 u: H - {9 |' d. X: a/ e) M+ Q& o$ |) y
- /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
( [$ d! x* R) e7 K; h0 I) C3 R0 S - /* Set HSION bit */# N* e3 w# K- ~$ X* v K) y! y
- RCC->CR |= ( uint32_t )0x00000001;
# ~0 I! u$ k! @9 H
5 x: e' `+ X0 p8 \! h2 v- /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
( ?5 }# D" }) a8 e! ]- A: p - #ifndef STM32F10X_CL6 \$ N5 l, q8 k- g; G
- RCC->CFGR &= ( uint32_t )0xF8FF0000;& G- G* m% F2 u0 G4 K" g
- #else G, i4 h# e- l3 T- i% `- H
- RCC->CFGR &= ( uint32_t )0xF0FF0000;
1 f1 M, v( z. Y: l - #endif /* STM32F10X_CL */- u" Y+ x1 l5 t
- ! ~0 y) R5 O# y+ Y4 j; X
- /* Reset HSEON, CSSON and PLLON bits */ K2 d6 C3 t* c m# t3 h
- RCC->CR &= ( uint32_t )0xFEF6FFFF;. W. R/ S8 h- y4 |$ U6 p) V
) K' C1 _7 Y* {" ]3 C) ~- /* Reset HSEBYP bit */
/ i$ i* h/ O4 y4 z, p" U2 c/ S$ ? - RCC->CR &= ( uint32_t )0xFFFBFFFF;9 v3 k2 H1 Z1 Q7 D$ a
- ! }! I* t$ n! m! Q; x( ^( b& o* \+ [
- /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */+ b0 ]* s$ E, e2 V% {3 G
- RCC->CFGR &= ( uint32_t )0xFF80FFFF;+ u& ]+ O; z+ W0 u! h C4 X
- 0 Q" A1 t3 j1 M' I0 w
- #ifdef STM32F10X_CL
+ s$ _! M. r- s9 ]3 E. F - /* Reset PLL2ON and PLL3ON bits */
- a5 P7 S) i8 A$ a6 h! I - RCC->CR &= ( uint32_t )0xEBFFFFFF;! F( n: o& @3 k1 t5 p1 w
0 I; [1 b6 ]9 C! f) _- /* Disable all interrupts and clear pending bits */
6 ?0 f0 v, I& N) D% n- z' P9 { - RCC->CIR = 0x00FF0000;1 L3 \3 X" @+ g e8 e
- + A$ _7 z) Z9 B* } w
- /* Reset CFGR2 register */5 A, E" b# u' c+ j6 z" X
- RCC->CFGR2 = 0x00000000;
4 V- L1 l# ~8 N$ z- A$ U - #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)( H+ h/ }# b y$ v
- /* Disable all interrupts and clear pending bits */
* G$ Y# N3 z8 \6 i - RCC->CIR = 0x009F0000;6 E5 G$ O: F/ c7 ]1 F4 `/ T
% E0 F4 `; p. a: v- /* Reset CFGR2 register */4 A) E6 c2 I- U: `4 m, x/ Y
- RCC->CFGR2 = 0x00000000;
; v& H8 w* M$ V6 W# n4 Y" Z3 L - #else
, j$ Q0 Y4 r5 A! i! T - /* Disable all interrupts and clear pending bits *// [* |7 l6 O' F$ P% |( A
- RCC->CIR = 0x009F0000;/ P" S: b' c& i+ g) z2 Q8 q
- #endif /* STM32F10X_CL */
R! q$ I9 a7 b$ \ - 6 ]; e( [" u9 f b" r
- #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)2 T0 m! S( t: Y h8 C; F
- #ifdef DATA_IN_ExtSRAM
# A& H6 D. R5 S; Q - SystemInit_ExtMemCtl();
# K0 s& u3 o- @4 l, t; x - #endif /* DATA_IN_ExtSRAM */
A- _% M# i9 _! F8 G8 @ - #endif, w# s& H/ F$ ]0 ^$ ~6 `* I
3 h: z ] j2 b* T- /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */* G' K9 \ _7 y" O0 }9 Y9 k
- /* Configure the Flash Latency cycles and enable prefetch buffer */2 C; @/ v' g) U6 p) d5 K2 K% k, R
- SetSysClock();
3 b! m2 C c3 i3 `3 U6 n" H
& l6 e! Z- i! o: q7 N( G$ U8 a- #ifdef VECT_TAB_SRAM
& ]' [- h ?/ H - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
5 n" s0 X: Y1 g - #else
8 m! o w& f7 P - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
8 K a9 p" ]& ^+ j) S - #endif9 ^$ x2 N2 n6 U3 w' \. o& }* ?4 {+ w
- }
复制代码 " P% b4 j& |( M7 v D) B
在系统复位函数SystemInit()中,主要是复位各个寄存器,将寄存器值设置位默认值,最后调用时钟设置函数 SetSysClock()对系统时钟进行设置。
# G+ C9 [+ ~4 a5 _) r, p7 ]& q4 i% Q7 t
- static void SetSysClock( void )
' d& T" C' {* n7 K a* u - {0 l6 L- c7 ?' L) Y1 }/ M. {" ~# d1 N
- #ifdef SYSCLK_FREQ_HSE1 R# w; K' D9 S0 Z8 l. p! _' k
- SetSysClockToHSE();
" j: R$ P" I1 h& Z0 k+ U( Q, K - #elif defined SYSCLK_FREQ_24MHz6 u6 a) d7 H. e) j# G
- SetSysClockTo24(); A) w' g6 i1 D7 b
- #elif defined SYSCLK_FREQ_36MHz
5 O' f8 y- |0 l/ I - SetSysClockTo36();
( U" C. V3 O5 z* ?1 I7 p0 d - #elif defined SYSCLK_FREQ_48MHz6 ~9 _/ I; }; P8 t. s: `( I
- SetSysClockTo48();
7 h4 t* `5 X# ^% X1 e# Y! E( U - #elif defined SYSCLK_FREQ_56MHz
4 [) `9 S/ ]4 |1 X4 Q - SetSysClockTo56();
5 T9 w1 B) a: W+ S! o - #elif defined SYSCLK_FREQ_72MHz
, n6 k7 q3 U. M+ G! D - SetSysClockTo72();* J; Y; p. }( k& ~, [/ Y C0 o" o
- #endif
- {* @" ^) Y2 b( A: j! u* N$ |
9 q5 _" a0 v: ]3 G9 C+ F% H# S- /* If none of the define above is enabled, the HSI is used as System clock
7 J h5 k2 l7 S- m1 [# T( X - source (default after reset) */
- w2 P% w8 n, P) V - }
; h0 @1 P" N& m$ o" E4 @; j3 a" J, X
复制代码
( A$ c2 s5 a1 s& L: P系统设置函数,根据不同的晶振和单片机型号,选择相应频率的时钟进行设置。
1 _! ]) t/ E- Z( K/ ?6 i t1 l7 @' @) [5 E' h% d$ \6 ?
- static void SetSysClockTo72( void )6 L" [ T3 V4 ]
- {# ^9 S/ A1 w/ ]- H/ V2 n* u
- __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
2 K7 w3 B/ {* _6 ^' p9 L - 7 Z# W+ w i5 w1 |7 ]* k
- /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
* M- X- }; X( }+ b. n - /* Enable HSE */
; j0 g- N6 E a - RCC->CR |= ( ( uint32_t )RCC_CR_HSEON );
: K1 |" v, \! o" }) P
* O/ e- {# r! L" F% I1 ~- /* Wait till HSE is ready and if Time out is reached exit */
& Z6 F( o! c$ o. _$ S - do* ~8 ]6 W" {# ~
- {% ^) ^+ Z* e" l9 s3 p6 B
- HSEStatus = RCC->CR & RCC_CR_HSERDY;0 I H) B( I* w0 `3 U
- StartUpCounter++; p. q! d3 j- ^+ h( H ]- f0 c/ n
- } Z4 @$ b7 a7 h, N" g- q
- while( ( HSEStatus == 0 ) && ( StartUpCounter != HSE_STARTUP_TIMEOUT ) );
* I* C) z& I! g6 ], p, f( @# e - 8 T) l1 c4 t7 U( J3 P8 U6 p) o
- if ( ( RCC->CR & RCC_CR_HSERDY ) != RESET ). r$ B: `5 N# c
- {+ K1 T! h; r: K9 b, b0 b1 b+ @9 ^
- HSEStatus = ( uint32_t )0x01;
; x9 ?$ v- Y+ ~; { - }
7 Y3 f; j, Y! l4 ] e; j' \0 ] - else9 l( D9 g) R! y1 I7 D
- {
; ^4 j' |3 M; l% C4 a& ~ - HSEStatus = ( uint32_t )0x00;
- y4 Z" }$ O' q& e - }
/ c9 _% L j3 {3 i
" [, L7 a5 l* O7 w q& T R2 J- if ( HSEStatus == ( uint32_t )0x01 )
1 p" G. |* N( I5 j& T7 i& p - {
! i: ?- Q! i3 |& O% g Q - /* Enable Prefetch Buffer */" t- m, @- E: L+ K5 E" M6 e0 m/ M
- FLASH->ACR |= FLASH_ACR_PRFTBE;
: v8 Q: }) L& H6 g
' K0 S. L x* v* k9 H- /* Flash 2 wait state */+ i& L' f. }; d" \; R
- FLASH->ACR &= ( uint32_t )( ( uint32_t )~FLASH_ACR_LATENCY );( `% q- P5 i) O1 a8 j3 r
- FLASH->ACR |= ( uint32_t )FLASH_ACR_LATENCY_2;( n9 M) E4 y, x1 a% d) w
) \! X- ^+ [* [! l$ a8 z
7 }: \1 o2 j2 E( B% R; \ Y- /* HCLK = SYSCLK */4 G ?; t# U3 h' e1 w
- RCC->CFGR |= ( uint32_t )RCC_CFGR_HPRE_DIV1;
7 |, E7 N* y! B0 P# _- W# E
1 U2 T6 U9 L* u8 p* x% s# D& {- /* PCLK2 = HCLK */, q& o3 V+ e8 @
- RCC->CFGR |= ( uint32_t )RCC_CFGR_PPRE2_DIV1;
" O, Y# T7 p: ?7 ~% `: d - * P' i+ r$ P% U/ x9 p; e! v
- /* PCLK1 = HCLK */. V* t0 C3 o* |/ K
- RCC->CFGR |= ( uint32_t )RCC_CFGR_PPRE1_DIV2;
( G& Q; k2 H1 g/ ^( k. C' u* c - 4 s8 Q5 M* e4 z3 D( I6 E
- #ifdef STM32F10X_CL' v: g! n4 _; H$ w
- /* Configure PLLs ------------------------------------------------------*/" n% O j% d, @& l( U, K& c
- /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
4 L% a7 Z% {$ d! J - /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */4 O- k4 F: s. v6 j5 b* Z
5 x# \3 k6 }% Q5 X' b$ v6 I* O- RCC->CFGR2 &= ( uint32_t )~( RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |& T- h9 P% E4 b! ]2 G# `5 c
- RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC );2 t9 [# W6 N0 Q- k/ G7 `
- RCC->CFGR2 |= ( uint32_t )( RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
K# q3 a9 N9 [& m' j( H - RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5 );
* L, W: e! N2 W6 N. j% d; h3 } N
( S h# Z b k- A- /* Enable PLL2 */
, W# x- d$ k, S$ q" h' R# l! q" G7 @ - RCC->CR |= RCC_CR_PLL2ON;( P: p" a* u& T2 w) f
- /* Wait till PLL2 is ready */3 h% Z% f$ r" k
- while( ( RCC->CR & RCC_CR_PLL2RDY ) == 0 )0 J8 }) B/ T% d7 h' S0 y
- {( o; ]- U+ T9 j3 ~
- }4 f0 ~$ v& V5 [) R$ K- l5 n# V3 h W
- . \7 r3 U; Z! ?9 K( \5 D
- ( f. W2 X' ^1 p. H' l
- /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
& ~4 ?# [" f$ k: Q w - RCC->CFGR &= ( uint32_t )~( RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL );$ p* z! E9 Y& E; w# B
- RCC->CFGR |= ( uint32_t )( RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |5 C2 k; R9 e6 j3 o0 m2 ~/ K
- RCC_CFGR_PLLMULL9 );) W4 s( h0 _/ L( K5 g8 s
- #else
$ k* k. L3 j3 k$ z7 T3 d - /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
' Q0 P A6 ]' |0 Z. E4 y5 ^ S - RCC->CFGR &= ( uint32_t )( ( uint32_t )~( RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |7 k5 ?7 v* \. ~+ X% o
- RCC_CFGR_PLLMULL ) );
, G/ p9 f* U* | U - RCC->CFGR |= ( uint32_t )( RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9 );9 F0 [: T+ V8 d6 b9 x
- #endif /* STM32F10X_CL */
. p- L# ]! M8 B' H
$ Y) U! y$ `4 {# e- /* Enable PLL */( [; W" b; z" n6 n. K
- RCC->CR |= RCC_CR_PLLON;+ H! M* t0 U) M8 Q7 N5 L
- @2 \4 g7 `/ \- /* Wait till PLL is ready */
/ p* U% f8 r5 |0 V4 Z6 v5 i8 M - while( ( RCC->CR & RCC_CR_PLLRDY ) == 0 )
; p" n0 I0 {5 g) d4 w3 n- e - {) c9 r& ?; @& Q6 \
- }$ E7 t% A# z0 m7 I# R4 A
- " B5 `! n* m! b E8 e0 R4 |- N) r
- /* Select PLL as system clock source */
1 r! z' r8 W6 N - RCC->CFGR &= ( uint32_t )( ( uint32_t )~( RCC_CFGR_SW ) );+ \; ?1 I( J# H/ \4 c+ L* ?
- RCC->CFGR |= ( uint32_t )RCC_CFGR_SW_PLL;5 o5 n5 R1 Z- t$ [& y
* m: K+ y0 A" U- /* Wait till PLL is used as system clock source */0 r1 K* \7 h8 k8 Q9 s% O8 K
- while ( ( RCC->CFGR & ( uint32_t )RCC_CFGR_SWS ) != ( uint32_t )0x08 )- Z8 Y* Z/ R: P# b3 l. ?4 u
- {. q- N2 J# K, u6 x9 W$ ]
- }# T9 k# g- R8 l8 M$ [; c. S
- }: Y" _% a% ]- G
- else
1 d& h. S. z4 d9 }% M% k - {, v# k3 ?: _% e7 i7 F; q2 H8 L
- /* If HSE fails to start-up, the application will have wrong clock
+ D: s1 U* e7 E/ H; o - configuration. User can add here some code to deal with this error */2 q4 J6 L# l( y2 c0 i
- }
, y. J) F4 Z: P/ X - }
b; l$ o- v3 i# X6 d( | @- U8 | - #endif
复制代码
. R: \" j: r4 }' a1 Q4 b v2 d0 G在系统时钟设置函数中,设置锁相环,及各个时钟的频率。
+ l, M& }4 h; s7 S7 |/ \4 k. ~' M& U" \2 R3 H
关于时钟的具体设置,可以参考下面这张图。
) G7 g" K. ^1 w
; Y* O' i! t( Y( q. r. |
; \; [& u) Q6 n) m7 s# I
; ?7 y+ s$ \+ P) \/ b: @
( {% f, z M f2 L# c" S1 n6 {& |$ I4 {5 B
通过上面的分析,可以知道,程序在进入main之前执行流程为
2 [8 h. h4 U) G: D. O' T
2 o" X8 S: o. u
~+ @/ c7 h3 \+ X- V/ @" ^
; \9 j; Q7 V* S/ f, V5 }% b这里大概分析了单片机在进入main函数之前,大概都做了哪些工作,对于具体的启动文件代码没做分析。对于启动文件startup_stm32f10x_hd.s的详细分析,在另一篇文章中进行说明。! w; Z( H t% M( w) j% H8 G
9 k; M$ v# e1 x" h
9 h' ~4 [* ]+ x" a* ^+ A
6 G5 p! h1 Z. s N. f: m& S |