01. STM32F4时钟系统概述
9 h7 C! a* Q1 E2 }: a0 M" z, M时钟系统是 CPU 的脉搏,就像人的心跳一样。所以时钟系统的重要性就不言而喻了。 STM32F4 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。于是有人要问,采用一个系统时钟不是很简单吗?为什么 STM32 要有多个时钟源呢? 因为首先 STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。$ N7 c4 q' g( B8 A. @
' g1 A) N. ^7 X/ i" H02. STM32F4时钟系统图
# k0 |7 b# {6 P4 K I; E
1 W I# h3 W n: ^# Y. [1 m0 x7 v4 W
* ~* X& S f5 I" q, n# @1 d1 h% v n* o5 l7 L$ z m3 l
在 STM32F4 中,有 5 个最重要的时钟源,为 HSI、HSE、LSI、LSE、PLL。其中 PLL 实际是分为两个时钟源,分别为主 PLL 和专用 PLL。从时钟频率来分可以分为高速时钟源和低速时钟源,在这 5 个中 HSI,HSE 以及 PLL 是高速时钟,LSI 和 LSE 是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 HSE 和LSE 是外部时钟源,其他的是内部时钟源。下面我们看看 STM32F4 的这 5 个时钟源,我们讲解顺序是按图中红圈标示的顺序:* o' p" ?8 |! p% h1 f
& M' F; `5 N( k, X①、LSI 是低速内部时钟,RC 振荡器,频率为 32kHz 左右。供独立看门狗和自动唤醒单元使用。
) x# `; G* w; }% m! c4 A, {1 t②、LSE 是低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。
* r& B* c# V% F ?③、HSE 是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz~26MHz。我们的开发板接的是 8M 的晶振。HSE 也可以直接做为系统时钟或者 PLL 输入。: q% g! l |# M& }3 i5 m4 m
④、HSI 是高速内部时钟,RC 振荡器,频率为 16MHz。可以直接作为系统时钟或者用作 PLL输入。8 n( b v: O/ M, @* n% @6 t
$ S+ \0 |7 w3 m' }( p8 `4 I
⑤、PLL 为锁相环倍频输出。STM32F4 有两个 PLL:
4 y- V; F! {. T$ p3 J1) 主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。
! o6 g* z7 F* u8 r第一个输出 PLLP 用于生成高速的系统时钟(最高 168MHz)
/ j4 q e- J& H; W7 _* X第二个输出 PLLQ 用于生成 USB OTG FS 的时钟(48MHz),随机数发生器的时钟和 SDIO时钟。$ f" W, W7 E# T$ `
2)专用 PLL(PLLI2S)用于生成精确时钟,从而在 I2S 接口实现高品质音频性能。6 C# Q$ N4 v( U; s- b# c/ H4 V3 `
5 C" z- Z' x# ?0 AA. 这里是看门狗时钟输入。从图中可以看出,看门狗时钟源只能是低速的 LSI 时钟。
8 t8 v7 ~, }8 Y" D# V' W% M( v1 k
$ `8 b n+ {. oB. 这里是 RTC 时钟源,从图上可以看出,RTC 的时钟源可以选择 LSI,LSE,以及HSE 分频后的时钟,HSE 分频系数为 2~31。 @. h7 U9 W9 ^6 ?* K6 L. `$ B
/ d; [& Y4 \: `0 {, H8 k
C. 这里是 STM32F4 输出时钟 MCO1 和 MCO2。MCO1 是向芯片的 PA8 引脚输出时钟。它有四个时钟来源分别为:HSI,LSE,HSE 和 PLL 时钟。MCO2 是向芯片的PC9 输出时钟,它同样有四个时钟来源分别为:HSE,PLL,SYSCLK 以及 PLLI2S时钟。MCO 输出时钟频率最大不超过 100MHz。
a$ m5 w' ]3 F, E7 D8 V) I" W, D- I
D. 这里是系统时钟。从图 4.3.1 可以看出,SYSCLK 系统时钟来源有三个方面:HSI,HSE 和 PLL。在我们实际应用中,因为对时钟速度要求都比较高我们才会选用 STM32F4 这种级别的处理器,所以一般情况下,都是采用 PLL 作为 SYSCLK时钟源。根据前面的计算公式,大家就可以算出你的系统的 SYSCLK 是多少。# b4 i" A. O& _- g+ j% I
/ R2 Z @& Y) e! t4 g- gE. 这里我们指的是以太网 PTP 时钟,AHB 时钟,APB2 高速时钟,APB1 低速时钟。这些时钟都是来源于 SYSCLK 系统时钟。其中以太网 PTP 时钟是使用系统时钟。AHB,APB2 和 APB1 时钟是经过 SYSCLK 时钟分频得来。这里大家记住,AHB最大时钟为168MHz, APB2高速时钟最大频率为84MHz,而APB1低速时钟最大频率为 42MHz。
3 I: A9 z4 a6 y- ], ]% n: u$ j: @5 `& O
F. 这里是指 I2S 时钟源。从图 4.3.1 可以看出,I2S 的时钟源来源于 PLLI2S 或者映射到 I2S_CKIN 引脚的外部时钟。I2S 出于音质的考虑,对时钟精度要求很高。探索者 STM32F4 开发板使用的是内部 PLLI2SCLK。6 n& K3 b( p0 l' {9 t3 }
3 f2 Z" \* N) {" x
G. 这是 STM32F4 内部以太网 MAC 时钟的来源。对于 MII 接口来说,必须向外部PHY 芯片提供 25Mhz 的时钟,这个时钟,可以由 PHY 芯片外接晶振,或者使用STM32F4 的 MCO 输出来提供。然后,PHY 芯片再给 STM32F4 提供ETH_MII_TX_CLK 和 ETH_MII_RX_CLK 时钟。对于 RMII 接口来说,外部必须提供 50Mhz 的时钟驱动 PHY 和 STM32F4 的 ETH_RMII_REF_CLK,这个 50Mhz时钟可以来自 PHY、有源晶振或者 STM32F4 的 MCO。我们的开发板使用的是RMII 接 口 , 使 用 PHY 芯 片 提 供 50Mhz 时 钟 驱 动 STM32F4 的ETH_RMII_REF_CLK。
) ^' V/ k5 \* h# }
8 J, b5 M" Y2 B& x4 ^H. 这里是指外部 PHY 提供的 USB OTG HS(60MHZ)时钟。
2 }1 o, O2 R6 y9 O y& B, w" U8 R* B3 w I7 o* T
03. STM32F4时钟初始化配置
+ `+ |8 F$ e+ j" F- d# P; CSTM32F4 时钟系统初始化是在 system_stm32f4xx.c 中的 SystemInit()函数中完成的。对于系统时钟关键寄存器设置主要是在 SystemInit 函数中调用 SetSysClock()函数来设置的。我们可以先看看 SystemInit ()函数体。
, A) o( O" n9 d0 A6 r% @4 q# o% d& e* r* u6 o
- /**! o. b: b" ` F ]# U
- * @brief Setup the microcontroller system
?8 k6 I) H* e5 ?: _6 I6 u - * Initialize the Embedded Flash Interface, the PLL and update the
- L( c- X" l, i2 p" k - * SystemFrequency variable." }9 b6 {' q- p) O- w' u7 r
- * @param None/ @0 O* \- ~ p4 Z: V' i3 m
- * @retval None/ s( d/ W0 m s' L! L1 P) n/ g
- */* T& G7 ~9 E" r5 |9 K
- void SystemInit(void)( \6 p6 N% j) B4 [4 l2 ~/ S) F
- {
$ m- B/ w2 W. ?% u - /* FPU settings ------------------------------------------------------------*/! `' U& G. ]: }" X. k0 Q/ B, L- ~
- #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
) i5 w6 y/ ^, C1 {4 ] A# K - SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
# h3 z) q3 ~# q2 Y. R- N6 e7 O; E - #endif5 P7 Z) ]( \+ S3 ]
- /* Reset the RCC clock configuration to the default reset state ------------*/
0 O" R9 X) m; u o - /* Set HSION bit */
0 S# f: {) _% n# U) }$ w - RCC->CR |= (uint32_t)0x00000001;
0 w( [ a8 A$ O& @ - * S8 r: i( y7 W% U7 I. h+ T) w
- /* Reset CFGR register */) S$ N( f2 U( M# o; p+ X: n M
- RCC->CFGR = 0x00000000;
6 j/ i. f: `8 v/ q
% P9 O8 I* }- {- y, n- /* Reset HSEON, CSSON and PLLON bits */
/ O: ], T3 x3 i' C8 s1 {# R - RCC->CR &= (uint32_t)0xFEF6FFFF;
& o# B! ~/ G! j% P& x' k
$ o- z6 ~8 |& m: K; \" h& ~6 _- /* Reset PLLCFGR register */
3 D- z4 a, B, w( l( n - RCC->PLLCFGR = 0x24003010;
+ m$ n, v# `, U/ |; V - ! Y& G& S3 }# {4 P" n; q0 f# H
- /* Reset HSEBYP bit */
' v5 J" w4 o# W& g* _! } - RCC->CR &= (uint32_t)0xFFFBFFFF;6 ^- L/ I0 G( ?, L. B0 B' [1 m
8 r6 u0 L5 y- u, u. a8 l, d, i- /* Disable all interrupts */
" E" Q/ j, y$ Q - RCC->CIR = 0x00000000;
( L, \) M/ X7 m( U- e - 1 \2 T- ~- H& Y0 S& u1 [ _8 Y
- #if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM)
! Z( R! V' a% c( P - SystemInit_ExtMemCtl();
. T. o! G& n ?4 G) H - #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
$ B8 R# ]) M& ? - / {, h% x9 X% h$ q% l" m! e ~
- /* Configure the System clock source, PLL Multiplier and Divider factors,
4 }! b9 K t: K4 s/ j7 } - AHB/APBx prescalers and Flash settings ----------------------------------*/ Z. m8 a9 y* f& p9 @. I
- SetSysClock();
2 c2 u ^, |, m - ) n+ O M- B0 Z* d/ s
- /* Configure the Vector Table location add offset address ------------------*/5 I) J% `: b5 k! v# I% X5 |) K
- #ifdef VECT_TAB_SRAM
' M' h9 [) ^) d$ ` - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */" t0 G9 _ i! A% q
- #else4 m- V, D$ F+ B' n" t. R3 W
- SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */4 \1 a4 T& A# R! N6 W
- #endif
2 ~7 f" Q0 e9 E1 `/ p$ Q1 P* {( O - }
复制代码 1 X, F6 J" K" ]+ }
在设置完相关寄存器后,接下来 SystemInit 函数内部会调用 SetSysClock 函数。
3 k2 P1 s7 V3 Z" U- H6 S) o8 u" p7 r3 x5 J: _
- /**0 S- k: ~$ q- f6 x# i r
- * @brief Configures the System clock source, PLL Multiplier and Divider factors,
& Q) J6 Z: n$ f& Q7 S; X - * AHB/APBx prescalers and Flash settings3 m3 t9 I6 C8 \; N, ~
- * @Note This function should be called only once the RCC clock configuration , }/ m0 N3 Z5 P: {2 t+ d s+ L
- * is reset to the default reset state (done in SystemInit() function).
9 t" h4 T; |$ w; B - * @param None
& f. F8 t& R, K2 J - * @retval None
5 R7 a' {6 K0 {, o; M; G+ \( T- m - */
' q( W6 u+ v( a! l" a; _ - static void SetSysClock(void)
4 e9 d# v$ c; T G9 w1 G5 g - {# ?7 q* {& ?4 S/ p! b+ j
- #if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)|| defined(STM32F469_479xx)
; ]6 A: ?( K! ]! _. d6 t - /******************************************************************************/) D# m* ]+ h# P6 S3 t' Z
- /* PLL (clocked by HSE) used as System clock source */
+ V5 }, R: w0 f+ z9 c# W @0 I+ Y - /******************************************************************************/
/ ]5 @/ M% N8 Q; Y" g$ D0 f5 M - __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
: }, |' O2 u8 s1 B; X. ^ - - n" z; a- h1 p& h, K4 _
- /* Enable HSE */ //HSE时钟使能
5 x- y# z5 {2 I. x/ P# e) l - RCC->CR |= ((uint32_t)RCC_CR_HSEON);0 h/ @$ @4 w- g6 ]( M$ s# u
- //等待HSE时钟稳定
# @5 S2 h% I2 G - /* Wait till HSE is ready and if Time out is reached exit */
& i0 }1 N" C) i0 v" e/ |( [ - do
% t3 L' l8 v+ @0 F- p - {
\/ {* i# \- Z1 F3 m7 d0 O - HSEStatus = RCC->CR & RCC_CR_HSERDY;, S0 ]/ }+ C# Z
- StartUpCounter++;- Y' v% l# l3 u( o
- } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
: P* f( O, D# Q0 B1 J: a; D
3 C P: _, Z* ?' ]% Q' `3 ~. _- if ((RCC->CR & RCC_CR_HSERDY) != RESET)" c8 u1 z" a: x$ r
- {
% V u0 C! \8 \8 J - HSEStatus = (uint32_t)0x01;
. m% }% v& y/ m! t7 s! m" d - }
) M* h; T2 h5 ]2 I9 i - else- @. t l9 F4 F9 u5 s
- {
& s Y2 z! m8 ~/ }% j w - HSEStatus = (uint32_t)0x00;
2 r5 X. u; V7 F& B4 I# m- P+ d - }& {! \3 L& ~* t1 L1 o. V5 J" ]
- + f. {! `- @" T
- if (HSEStatus == (uint32_t)0x01), U! Z( ^; }) k H+ n- U b
- {+ i' p3 V' q& v: ]9 p/ K! N
- /* Select regulator voltage output Scale 1 mode */
5 W% o2 f. A$ y2 c; D( L$ ~ - RCC->APB1ENR |= RCC_APB1ENR_PWREN;+ D2 ]' P. `# k, {- v$ H1 ?
- PWR->CR |= PWR_CR_VOS;& G4 s6 Z% u* Q4 F
- //AHB不分频 [5 [2 Z" I8 x% D( Y. G3 i2 q; f; r
- /* HCLK = SYSCLK / 1*/
( G0 C( Y5 s9 t, r' [% e1 p6 m - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
; @8 n* q+ k1 X9 w
: O4 I( N* T$ j- [- #if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F412xG) || defined(STM32F446xx) || defined(STM32F469_479xx) % f& L0 Z# g$ I# |3 F7 |$ `( F
- /* PCLK2 = HCLK / 2*/ //高速APB二分频
& L, @/ `3 ~/ ^! V) j$ N - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
- U; V* W, [( w% I
) V B$ [ I' | ^- /* PCLK1 = HCLK / 4*/ //低速APB4分频7 A+ s/ u* M/ G: Y0 t( ]5 M
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
9 F+ j" x4 j7 p2 y Q F - #endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx || STM32F412xG || STM32F446xx || STM32F469_479xx */
0 W6 ~% K" X; J( `$ |+ R: d - 2 g% `% w& s' y2 T% c5 t0 m7 n
- #if defined(STM32F401xx) || defined(STM32F413_423xx)* {5 r" H+ F. ?. e& @8 I3 B, T
- /* PCLK2 = HCLK / 1*/
: c) l- {! C( h8 V: ]" m - RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
) x4 l: v2 ^- E5 Q/ I0 ? - ; Q$ [. l: G9 ^0 k
- /* PCLK1 = HCLK / 2*/+ s7 `7 `. H5 a. G1 J0 @# y
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;" E# T0 m M4 e e' q( I
- #endif /* STM32F401xx || STM32F413_423xx */9 ]5 X J3 h) N4 }, E
! I# I& [% P+ b$ ?/ Y" P( m+ R/ _- #if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx) ( L/ R+ U6 M& K: U9 _, P" e
- /* Configure the main PLL */) @/ Z9 h. l% n4 e
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |1 F$ g1 B' p0 Q: v! q8 f6 |% y( ~* Z: a
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/ v. u1 q P5 W; g: W - #endif /* STM32F40_41xxx || STM32F401xx || STM32F427_437x || STM32F429_439xx || STM32F469_479xx */6 O4 g" V0 w9 l4 t
- " v5 `# |8 p& L% [* M" @0 J) K
- #if defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)
+ Y$ M+ K7 C/ i3 B! w: i) K - /* Configure the main PLL */* M9 C% G: h" z, @
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |, o( v$ W7 W. m
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24) | (PLL_R << 28);, O5 r2 Z$ _' F/ m+ H; m
- #endif /* STM32F412xG || STM32F413_423xx || STM32F446xx */
* {& j: z7 R( [/ t' k! Z/ `7 o! {
' f e) ]5 V3 ]5 U6 s- c- /* Enable the main PLL */
- R, z) ]$ F. T# q R) F g H; V - RCC->CR |= RCC_CR_PLLON;
u, ^% I# y0 M& C( @ - + d/ J8 x) Y! t1 q
- /* Wait till the main PLL is ready */
8 S3 Y8 O6 w- o2 C - while((RCC->CR & RCC_CR_PLLRDY) == 0)
$ [# c% J5 j5 P% L# C9 T% d) Q - {6 j$ k8 \6 s6 ]$ C
- }
( X$ ?9 f/ W+ Y4 G2 s: W, E# `! t4 `
) B5 X+ n" m& v8 Y8 ^1 B- #if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) || defined(STM32F469_479xx)% W* W7 C9 ~& L5 o2 z3 r; Z
- /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
" e+ E9 r: f8 l" a9 \5 ^" o) G ` - PWR->CR |= PWR_CR_ODEN;2 i4 t/ V _1 B3 P
- while((PWR->CSR & PWR_CSR_ODRDY) == 0)
- m" W/ f" n, O; V/ z$ ^ - {( D# h1 O: b4 Q- d, r8 S2 w
- }
7 I6 w0 i5 S0 E3 D/ w5 j' t - PWR->CR |= PWR_CR_ODSWEN;& M4 X; V# ^1 G
- while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)5 h9 V3 x% G3 G' i: }
- {
5 C+ G2 v8 G) o$ L- y: h. K3 t - } 6 E7 y3 S4 S" ~6 s; E5 k5 l9 z
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
; N# T% {- j6 N, b" v - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
$ Y, {$ x' t1 H2 n3 ] - #endif /* STM32F427_437x || STM32F429_439xx || STM32F446xx || STM32F469_479xx */
, y, q2 r- o, Q
2 L' [, {' n3 ?& N1 b/ i- #if defined(STM32F40_41xxx) || defined(STM32F412xG) * t" I( j6 {2 A/ W6 j: V
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */9 ?5 a9 a! Q4 }
- FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;6 Y b. ~2 y- W
- #endif /* STM32F40_41xxx || STM32F412xG */
% |5 L, q3 N4 ^7 }
- k- @' X3 o; \) S- #if defined(STM32F413_423xx) , c4 v4 r+ `% ^$ @+ H( N6 z
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */+ u" d* _' N6 M9 {5 ?! ~
- FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
1 [! O& x# E7 |4 Q4 e - #endif /* STM32F413_423xx */8 t; J: Q1 X; L3 F- R3 o
1 b& e3 [- u( I4 N- #if defined(STM32F401xx)
$ L* a8 s5 T! z* |$ ]% e I5 S - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */$ a, p3 Y9 o: J$ L$ f
- FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;2 e+ X1 z: X8 X; s# Q! Q! k) _
- #endif /* STM32F401xx */
$ i) c8 g% G. d. l$ |5 R$ y- Z8 s- V* D - 7 b- r4 x* \$ G0 _% d- X/ P* z
- /* Select the main PLL as system clock source */6 ~% J7 o1 V3 u' S5 c
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); M0 {- u; P) ]# `6 l3 l
- RCC->CFGR |= RCC_CFGR_SW_PLL;
; l6 k9 @5 A3 |% E - 2 k3 S1 g7 }1 x8 l1 h4 v
- /* Wait till the main PLL is used as system clock source */
8 ?) b) [. C) g5 H% a) \- e6 P8 M% s - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
/ k3 U) j' g5 x& E% i/ }8 g - {$ l. e+ r0 G! o3 ^. J; c/ {7 B
- }- }, {) _. q c- N
- }
! [* t& U- u) ~0 t) m; w. E - else6 z/ x; v+ K/ F* a) K' q* p" r
- { /* If HSE fails to start-up, the application will have wrong clock
8 n; v1 o! Q0 G) U - configuration. User can add here some code to deal with this error */
1 m8 { {- }/ C' `: V9 @ - }# A: I8 @! s1 Q$ h0 U" l" j( |0 H
- #elif defined(STM32F410xx) || defined(STM32F411xE)" p; k$ |, \) B6 G+ t
- #if defined(USE_HSE_BYPASS)
- u( N1 {& J. ^3 [7 T# x @ - /******************************************************************************/8 |6 D# C. V4 i. [% M: k
- /* PLL (clocked by HSE) used as System clock source */
; c6 _2 e4 a. @' h - /******************************************************************************/
) M7 r9 v) d6 O* u: w - __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
( n" G! Z" Z6 C
3 a# o" L% B3 c% D+ B: B$ ^- /* Enable HSE and HSE BYPASS */& ]/ |$ e* b/ y6 o; n1 b; r, n/ ]
- RCC->CR |= ((uint32_t)RCC_CR_HSEON | RCC_CR_HSEBYP);
% y. F- s+ r5 L# B
4 \: @- `7 J- O- /* Wait till HSE is ready and if Time out is reached exit */8 k( ]( A; w3 E
- do' m8 l `, G+ W$ t- Y( V: w' M
- {
6 V2 t$ B' k7 N* W, N - HSEStatus = RCC->CR & RCC_CR_HSERDY;' X A1 F M+ i% o. \, j) s8 Q
- StartUpCounter++;
: Y2 u( F2 ?% ^. t - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
/ v( x$ v: T- E' ~# ]+ _
0 L% T' M8 p7 b3 S7 K" I- if ((RCC->CR & RCC_CR_HSERDY) != RESET)
1 |# V3 x+ q4 E3 p( x - {0 G9 {7 g% }- r7 }
- HSEStatus = (uint32_t)0x01;
$ ]; N4 i: F' M V" h# [$ O. T7 k - }% K2 H' w# Y8 a- P
- else. \2 w. A3 p+ i m5 L
- {' A+ F! A; W5 A+ D/ z6 C6 V
- HSEStatus = (uint32_t)0x00;
& Q& T, e: A3 k( m T% t1 e8 { - }1 _. E5 I: c T8 P4 X/ T
/ s. F/ J# V9 d) P- if (HSEStatus == (uint32_t)0x01)
2 c/ E" }' _9 M* w; G+ u - {
& f$ }" z2 O6 P - /* Select regulator voltage output Scale 1 mode */
2 ]+ _' }4 J+ e4 } - RCC->APB1ENR |= RCC_APB1ENR_PWREN;% A1 P3 d% B' ]/ J) @/ i* x
- PWR->CR |= PWR_CR_VOS;
4 E. a5 a! T* U9 e* Q+ z$ f3 p( R, d
3 e& \5 m* c7 P- /* HCLK = SYSCLK / 1*/
- q1 e+ K9 m6 R# g8 u+ a9 C - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;+ u4 m+ f; \9 y+ F* c d
3 d( d- k' I' r- /* PCLK2 = HCLK / 2*/$ V6 i) ]% x y
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;! P' c8 R& c8 [: W6 V- ^
) T5 @( g5 R' {: v( Y. B& |- /* PCLK1 = HCLK / 4*/
. t/ l4 E) z4 `, n - RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
. S8 U6 S2 `' F. s5 m
* ^6 u2 ?8 e+ {2 Y7 T5 [- /* Configure the main PLL */ q c6 k+ [. j3 S
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
3 I) Y7 ^9 h$ f' D. F - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);" A. F/ M G% c$ C2 z
- 8 C5 K8 m" T+ l# Y
- /* Enable the main PLL */: h; t5 l! T; _$ G0 Q: w
- RCC->CR |= RCC_CR_PLLON;
- V) V* v2 _# {4 S: L
- u* A& W4 F, K) o2 C# W2 n, Y- /* Wait till the main PLL is ready */
* Q' ?4 b8 N$ W5 Z- q& f5 I+ b - while((RCC->CR & RCC_CR_PLLRDY) == 0)
, [/ O7 R K& o% h - {- D; r2 y% C& ?$ L. A" ^. i3 a
- }
. G% J6 U+ F* [# [/ p, R
' E- n' k( |2 W- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */8 z! V% |4 c* r) f- _
- FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;8 Z0 V& _, W6 H
- 0 {9 k, a& X/ d
- /* Select the main PLL as system clock source */
$ B0 D4 w9 H' T2 B! G8 a# c# D - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));; F9 m! H6 G6 K
- RCC->CFGR |= RCC_CFGR_SW_PLL;$ l% S$ L" S! S* r; K6 P# d
- ; v' b- a" Y& S) y
- /* Wait till the main PLL is used as system clock source *// ^" A3 m x3 a5 ] F
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
; A( t$ Y6 g5 c/ f) N0 ? - {$ L$ H" A# B; R) R: V2 M: T
- }
5 o9 o) z9 f: \3 c) ], { - }' T0 }, w6 }1 h% ?$ K1 j7 j
- else
( \) U9 E* }8 E - { /* If HSE fails to start-up, the application will have wrong clock
0 N- J/ r4 U0 b5 o! o6 L$ } - configuration. User can add here some code to deal with this error */
. _0 B8 s% L% v5 `+ O7 V - }
/ W; r) a n5 a9 p3 w @$ @ - #else /* HSI will be used as PLL clock source */
) Q& ^/ i! B0 f- n - /* Select regulator voltage output Scale 1 mode */8 V( W k" Z5 A
- RCC->APB1ENR |= RCC_APB1ENR_PWREN;' G1 d; F# v) u! W5 j/ T N
- PWR->CR |= PWR_CR_VOS;
+ [7 ~% {/ Z' Z( ? - 1 E6 g2 o# I1 Y, d
- /* HCLK = SYSCLK / 1*/: V: A/ N$ Q) K8 l# Y; M, w! T6 j
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
9 \3 r, [& r B' T) _ - 4 N; m$ F5 }: c# Q, z
- /* PCLK2 = HCLK / 2*/
; ~- b; J u9 ~ f; t3 S. o - RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
9 z3 j; b" ~( s
0 l3 g4 p" Y% @0 L+ w+ s- /* PCLK1 = HCLK / 4*/( [/ X+ _8 j2 d
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
% O2 k, A- @: X+ ^! d8 y u. S - ) Y" O5 [( ~3 q7 t
- /* Configure the main PLL */
: u4 O" A9 p x0 m; c7 Z/ \ - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (PLL_Q << 24);
) t3 {3 ?6 d; ~9 T( {5 y - + W8 {3 V0 F. j
- /* Enable the main PLL */
* L( E+ R2 v% \/ ` - RCC->CR |= RCC_CR_PLLON;
( T8 R, R' s7 F; R G% n
8 M, z% z7 U M( `2 N4 q1 a- /* Wait till the main PLL is ready */
7 J2 g* F7 q+ P2 L/ d7 ?2 M - while((RCC->CR & RCC_CR_PLLRDY) == 0)
% v/ ?+ n. q4 c1 y- c0 s4 V - {
( |/ \2 N' U/ t: p - }0 p% ]( \1 `/ ^
( o8 k: I/ O$ h5 S0 R( k3 ?! Z- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
5 Y; Y' i+ P& y5 r - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;$ ^ n3 [- ^2 s
- ! B u) M9 F, d9 }
- /* Select the main PLL as system clock source */
3 c1 ?4 {5 F! w% F2 J4 I' c/ |8 b - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
. h" Y/ Y- g8 Q0 [ - RCC->CFGR |= RCC_CFGR_SW_PLL;9 |1 d! ^6 x1 Z% r0 D( P
: n9 ]6 u' ^8 U* T& M2 [- /* Wait till the main PLL is used as system clock source */
' L8 V) @ f( o( ^0 I6 c) Q: f - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
1 E# L& }0 p! T, v7 X X - {+ {" }9 [2 \- |
- }% Y0 ]9 Q, F; a s
- #endif /* USE_HSE_BYPASS */
6 u/ U, f! b# f" B' \3 W - #endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F469_479xx */ $ r8 p, d7 r. c [
- }
复制代码 1 g- L. U9 n I2 N& w
先使能外部时钟 HSE,等待 HSE 稳定之后,配置AHB,APB1,APB2 时钟相关的分频因子,也就是相关外设的时钟。等待这些都配置完成之后,打开主 PLL 时钟,然后设置主 PLL 作为系统时钟 SYSCLK 时钟源。如果 HSE 不能达到就绪状态(比如外部晶振不能稳定或者没有外部晶振),那么依然会是 HSI 作为系统时钟。
3 G$ A6 ^2 o7 O1 Q7 j
+ z! ^- Q, X1 Q% i在这里要特别提出来,在设置主 PLL 时钟的时候,会要设置一系列的分频系数和倍频系数参数。大家可以从 SetSysClock 函数的这行代码看出:4 ?( d f( u5 `, k2 q0 O$ H6 O, ] p T
6 O7 ^; O8 Y4 `' ?* n! o, A
- /* Configure the main PLL */
t2 |" b' V' q - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |# c& P! ]) ~% {$ ~1 p' L
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
- t6 B: Y8 C% L2 ]% s" I这些参数是通过宏定义标识符的值来设置的。默认的配置在 System_stm32f4xx.c 文件开头的地方配置。对于我们开发板,我们的设置参数值如下:9 M, q" \+ f. m+ Q3 B2 w0 r6 z- N: i
! `- U# a* Z) t _9 K% S. @3 O
- #define PLL_M 8" D9 L- ?! h. N2 _2 m$ N
- #define PLL_Q 7
1 X- H; p) Z* L* b1 V - #define PLL_N 336
7 n; ?: O# D: M6 @3 v T8 U* o3 y - #define PLL_P 2
复制代码 % F+ u" u) o! R/ \" S
这里还有个特别需要注意的地方,就是我们还要同步修改 stm32f4xx.h 中宏定义标识符HSE_VALUE 的值为我们的外部时钟:8 S/ a- v! r; N2 c3 Q! V$ H3 N6 h
& W% G( f* L# J8 K- #if !defined (HSE_VALUE)9 ]1 p1 C* H; [, v
- #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */" q! N! E7 i2 K! y: h1 f
- #endif /* HSE_VALUE */
复制代码 2 G! K6 ~9 _) o
这里默认固件库配置的是 25000000,我们外部时钟为 8MHz,所以我们根据我们硬件情况修改为 8000000 即可。" v( c( J O* ]3 n U+ y
5 z3 @. [- y, ~# O# A% Z7 j
04. 时钟配置总结3 c' d- j A% z/ K
最后我们总结一下 SystemInit()函数中设置的系统时钟大小:
1 `4 Y1 {+ a. g/ I8 T) RSYSCLK(系统时钟) =168MHz3 w9 m0 ~$ a2 Q- E* I# V- E
AHB 总线时钟(HCLK=SYSCLK) =168MHz) p, u+ M9 L: I) {" Y5 m
APB1 总线时钟(PCLK1=SYSCLK/4) =42MHz
B, J3 q0 T0 X( mAPB2 总线时钟(PCLK2=SYSCLK/2) =84MHz
, K) i7 O4 Z0 e6 OPLL 主时钟 =168MHz
+ @! ~/ Q/ ~4 M, {, x. x5 C* n: Y. ^% `
7 x: I) B0 t$ ]$ C! h3 n Z( e& S6 D# A0 x
|