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