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