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