板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
i) _ R* f4 P; U0 H7 E# M- W9 d& c! _8 R
01、时钟系统介绍
2 ~! j- C- Z' e6 b: n+ [
0 g/ D$ t' b6 t: z; a: p
! s- Z8 }$ Y) c( u ▲时钟系统专业名词缩写# u% \. q* _4 \# d. c# |! E* }+ |- x
+ b0 T- `( W6 j/ m) w8 @( Z/ Y" p
时钟系统关键组成部分4 }1 Y" f# r3 z* w% s7 Q) C4 h9 p/ Y L
! |* B) T. r4 O8 J" o( h
01 内部高速时钟(HSI). |8 B! {7 Z$ }
8 y* K: H9 P" h( T) fHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。
5 `# q, x. M5 d% f8 C
' ]2 N: N- M) ~. R* Q* x. }8 p: \HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。
- J3 Q3 _3 s4 A9 r- W
! G$ o$ ^. h5 f. R/ O& g! W02 外部高速时钟(HSE)
1 N9 x- _# f0 b' g$ J8 S
& X: o& l0 w. ?% I w+ y; T外部高速时钟信息(HSE)可以通过两个时钟源产生:
3 S' W! [" E M& u. [+ v- @1 C, ?$ m7 \% ?! a1 L* ?
① 外部晶体/陶瓷谐振器
# d+ f0 }6 J3 Q6 {$ K9 U$ a5 ~! O* ?9 P& x" r) j
② 外部用户时钟! C8 ^/ o& j0 \+ m' }0 b
5 O- z" T1 X- J% }$ \$ ?
- a7 j$ D; \/ G0 N' i) @
3 [% W* r$ L# c! ]; k" D
▲两种时钟源接入示意图
" ~' ]- M0 q7 k$ m8 {8 P
3 l9 }: a! @: M) E2 a
% m- |7 c! ? K' o" U8 e03 主锁相环时钟(PLL)
" O5 S m; D2 O1 W$ Z: S( m3 S- @
STM32F2xx具有两个PLL
1 R' L3 S9 s) n0 @! K$ o/ R& F+ j' g, e( Q
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;5 y9 `0 t9 |$ _% H
9 [4 z4 n$ g' Q$ G' T2 K② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;( i/ i3 M1 X: j/ }, Z4 A
9 k/ B- s" I$ _; ]: q% n' W: q( k: b: j6 L( I
. a4 F# j9 k3 N C# @
0 V- i% s( T1 [# M# qHSE/M*N/P得到PLL时钟
8 [- w7 N) y' N
& S: V& L6 [- w1 \) M( s3 `! X" j关于PLL锁相环说明, h6 d& V+ B, K# {# l
: Z7 O' f9 z% G; w
6 p" X- o$ m1 I7 L* h# e1 ~1 W5 e# k! f" P
$ P b$ L& a2 F0 \5 ]从1处输入,3处输出是1的N倍。
) l6 _4 @$ @) `: u: _5 |
1 K) V% W$ F' N8 s3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)
, q0 T/ K1 {# g. j+ N4 \: j- b; R. w8 r3 W- T a7 z5 `% a
' `* ?" U6 E, \
( [" j$ Q0 u* M- g# n4 M$ d04 低速外部时钟(LSE)) z3 b/ M d3 R; h
% q3 k7 ^2 L6 X# u; T CLSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
3 W, p3 o) \) ]% N6 F& x- F" e/ z6 o3 R- r+ i1 f% p5 V( P
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
" u( P2 D6 ]4 x4 F* o
9 s O( v% r- |3 n3 g' W- o: _" \; f! C! S3 @: n
05低速内部时钟(LSI)6 Q( X5 B) K4 L0 N6 D3 s
1 P2 v" F9 O9 I' w* ?
LSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
0 i$ o/ `8 t' B7 p7 Z
- B1 z P2 e8 [( X/ G, b: q/ p5 B02、代码分析, V# p% O: P" q1 p- K) r
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。8 k4 _4 J. p( N" x( B5 _' j; |- W; s N
; j# Z- _; O1 {3 ~7 s1 L, M: G
- =============================================================================
& }" f B; ^/ l! { @$ c - *=============================================================================
; \( |. K- X& {& E/ h L' y; k( l) r - * Supported STM32F2xx device revision | Rev B and Y2 o0 a2 V) B; J' |4 W, e7 x
- *------------------------------------------------------------------------------ ^. R' N; b U9 y# P
- * System Clock source | PLL (HSE)
% j- G0 V: M/ u: E- X; U$ Z - *-----------------------------------------------------------------------------
8 R7 b. i. O3 E, l+ L - * SYSCLK(Hz) | 120000000' S+ |3 d( W( E1 E6 U, O& V* t
- *-----------------------------------------------------------------------------
) @' L8 ]$ E8 b7 W7 h - * HCLK(Hz) | 120000000
7 G4 C) t' ` V W - *-----------------------------------------------------------------------------
5 {% P' D* e; U - * AHB Prescaler | 1, P% D* j1 ?5 U+ p% @
- *-----------------------------------------------------------------------------' n p6 x+ @. w* ]$ C
- * APB1 Prescaler | 4
6 i* c/ ~' d! m: p. F9 m - *-----------------------------------------------------------------------------) x( l. e% C+ U3 O6 Y$ T2 z
- * APB2 Prescaler | 2
p2 [9 U; w7 d2 A+ X X - *-----------------------------------------------------------------------------5 j/ I0 q# k& V. `8 K
- * HSE Frequency(Hz) | 25000000) ?* k( L: U2 W7 f) u. @
- *-----------------------------------------------------------------------------5 p& L/ Z: r9 J8 A: a8 h
- * PLL_M | 25* {3 G$ g4 E' m$ G& t
- *-----------------------------------------------------------------------------3 p! P& `/ B; V2 s$ v$ L9 w# i
- * PLL_N | 240
- v( G( D9 x, Y/ Z8 P- Y* y - *-----------------------------------------------------------------------------
8 I) o7 m* G- R* d+ k: k# l - * PLL_P | 27 W4 [5 h' E! s% y
- *-----------------------------------------------------------------------------
# M6 {+ ?0 C* c" Y - * PLL_Q | 5' k( p% `1 s( _8 o; x
- *-----------------------------------------------------------------------------
6 `+ a0 t# T# Q$ N# t - * PLLI2S_N | NA$ g3 h1 E T, J8 g3 j
- *-----------------------------------------------------------------------------! v- O) g! T& S( z( u
- * PLLI2S_R | NA
! K6 B0 ^. e" ^9 L e9 a - *-----------------------------------------------------------------------------
! g; k! @6 X2 m. v5 } - * I2S input clock | NA
* ^$ ?0 ^0 Y: O$ y3 Y0 Q - *-----------------------------------------------------------------------------
: H7 x, M" R1 w& y% { - * VDD(V) | 3.34 V4 @* H' J8 a9 _% C
- *-----------------------------------------------------------------------------& X1 h' v7 ~5 x( A9 _- A
- * Flash Latency(WS) | 3
! h3 B6 J& d+ s7 z. Y; m7 W - *-----------------------------------------------------------------------------
1 {/ G2 G/ R( f( `) {4 L - * Prefetch Buffer | ON
5 W& g+ H* Z2 {7 L - *-----------------------------------------------------------------------------/ [! \ r$ L7 ]/ @' K
- * Instruction cache | ON
* P. W3 W% D' I9 p! @ - *-----------------------------------------------------------------------------' H0 z& a. J* a2 d8 I
- * Data cache | ON
8 l% B, @9 D0 g& d- Q G2 v0 s - *-----------------------------------------------------------------------------6 w; Z: w) P. P; }0 U& U7 q2 g0 R
- * Require 48MHz for USB OTG FS, | Enabled% V( p9 f5 R( m/ O1 X
- * SDIO and RNG clock |
) W+ _$ _4 e2 @; @# c ? - *-----------------------------------------------------------------------------
% X' ^; f8 S- B3 B( F. s% b0 J - *=============================================================================
* x" |, F, _+ B7 [' `/ G - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
z9 L. K7 T3 Q7 a9 q3 n% v- uint32_t SystemCoreClock = 120000000;
复制代码
3 e/ M" t$ u- {时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
7 O3 M7 s0 i6 Y6 L5 @, D: \4 F
" A! `" M2 Y3 h8 |$ d- IMPORT __main- J; q6 P5 o/ r" f' M4 Q
- LDR R0, =SystemInit1 k9 J' z0 ?& J
- BLX R0( }3 O" w/ B! e. q. w
- LDR R0, =__main* z' @" z8 }, Q8 M- j# \
- BX R0
8 p0 w. U: w( @5 v: V+ E - ENDP
复制代码
' i8 \6 N4 U' D. {在这里说明一下文档版本的问题:) k9 Q2 j& r8 x) j f
& V1 p# }( x, Z8 j' z' _
8 N. _' [1 R! X
- ^5 \. q( t( T' H+ d) x▲STM32F20X_User_manual的V7版和V8版对比图
) g; ]$ g7 z9 n9 d. }1 H# v6 D
6 c, r0 b4 c7 K# ]; K+ _" E上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。: y ^9 X( f, {, z/ F8 a
" l$ W* i# K2 }* Q5 g下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。
7 t& C4 Z/ u% ^$ P' J2 a% u6 L+ L$ v: H; Y( P
, e' } g4 [, G$ ^4 Y
/ w% Z$ z) c7 |/ g: \
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:* u/ g" i5 C* _7 K; H' R6 S! x ~
! b3 M$ c* V/ R' M; l7 j, U) C1、使能HSE
3 ]8 M* [- Y! \
' Q2 W8 }8 o" M- l( E& g& z0 @2、选择HSE作为主PLL的输入时钟
) Q9 n9 \" R0 E+ ^& b! a
' S9 u- d2 A6 P3、主PLL倍频后得到120MHZ时钟; u+ K, Z1 O" x- o
) p+ y; k$ X9 J2 _5 p# T$ R; V) V$ J4、系统时钟选择主PLL时钟输出作为系统时钟2 q4 J P1 H% w* A4 v9 M& i: u
: m# }1 w8 o( G我们找到对应的代码
- p. f& z& _. ]8 @: b5 j) H9 ~- X! H4 o# l5 X
1、使能HSE
4 Q, G! T: g! n9 ~# r3 J; J- /* Enable HSE */6 |( D" c) b# O" Y d' c0 a
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 ! P. V( \& x/ @ z% L+ P
在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
( W1 n& z; Z2 T6 |8 ]8 c
% L1 T3 c0 v' a4 T* m- b6 S
; l2 J! M. S) d# e$ d: [5 k2、选择HSE作为主PLL的输入时钟+ F4 @+ f* b6 W
- /* Configure the main PLL */& j1 N0 d5 D# v5 q4 L; ]3 N4 q
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |/ L& v) S- \* k! [
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 # h d# ?; x+ {/ X
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟
: ~# f5 N% M# Y( I3 O3 Q9 {/ ]
7 u. k- S+ Y4 E6 u9 l4 r/ l 3、主PLL倍频后得到120MHZ时钟
% }6 k# V( s9 a5 E5 b' g- /* Configure the main PLL */
! `' K! o7 f1 p3 G) \ - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
/ X, k/ X# N2 z& R" B" ~ - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 8 x* z1 u! j( ?6 B9 ?& p
4、配置主PLL作为系统时钟的输入时钟
) W+ X9 V& b8 f$ v9 E# o% Q- /* Select the main PLL as system clock source */! [, x) C) x7 l
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); R3 I+ e5 [4 K* E! E7 W
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码
. w' \; B& a8 g) g; @对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明; Y8 g1 @, i! W
' T+ \- n+ o: f [( t) L- c( t5 P5 u& Q7 I
9 Z c( r1 @0 d9 Y- g* R
整理后得知f(out)=f(in)* N / M / P
: e; Z1 E/ ?! ^" Y8 F7 F8 E- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
# |) t) n C* r- `* I - #define PLL_M 25
; m( u) l6 B, i- t3 [( D$ H - #define PLL_N 240
, [- q* M$ i: I, m - /* SYSCLK = PLL_VCO / PLL_P */
: m! U8 I X {8 e - #define PLL_P 2
复制代码
5 Z* ?/ s* b! A5 p# M. O: J这样就获得了120M时钟
: D3 t! ~* g) z& a. Q+ V) O
j" ~: v; O; A6 Z" c注意:
' [7 d+ x4 @5 r, ]2 `5 T+ Z' @" G! i+ A6 m' d% F5 d/ m3 ]
1 I0 V; v& q# t0 j+ [
- |' X+ y ~5 ~PLL_M大于等于2且小于等于630 `8 {4 w+ ?5 v0 L( P
# e# n! m q* |4 }( N% C0 }( I8 P! |! x i5 c5 s
$ W. M9 ]; g* M& i( L
PLL_N大于等于64且小于等于432
1 V3 s9 |) T, @5 _, Z m6 u0 H& \. ]" _0 e% v- v% V
: I1 T$ \1 M+ t V' K p$ [
$ ?1 f c; I+ {PLL_P只能是2、4、6、或8
1 @: [* F8 D& r2 s3 \+ V" g
. G) |* q( e" p' P$ T但2对应0,4对应1,6对应2,8对应3。
7 c; [9 G$ ?* U0 n" F" K( N% e* q$ d @& {/ c ]9 i6 j1 i$ V& `* F
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。
5 S k4 |' B# E$ z5 K* t+ c2 Z( O
(((PLL_P >> 1) -1) << 16)# ?" x4 i- C9 ?5 z
其他外设的时钟配置时
; u% J7 y( u8 D4 |; P% f1 B- /* HCLK = SYSCLK / 1*/8 ^# o& t7 U" U& E4 W" E8 T; f# F
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;, _. d, l( @0 f% x E, L8 {
- /* PCLK2 = HCLK / 2*/9 x/ ~7 d/ J/ R' B* x
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
. P* P: D+ f/ O9 S. ^' v - /* PCLK1 = HCLK / 4*/
6 d9 K7 s2 l1 |' p - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
+ T0 Q6 K5 P; d" P03、备 注 ( \6 Z% m5 O5 j. @& \
时钟中断+ Z6 u0 u) S, J( O* J
' Y% `: R8 C! D4 l8 m. }* }4 g8 \
) C. o% Q3 r: x0 {
* ^) @8 D1 T+ f1 X4 x可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
+ S' J) @- B( }* {- p& N
" K: I z% i0 P4 X* u$ g 无源晶振不起振
6 Z+ u# n8 w' s; w- v( c! }& P9 R
! U. Z# S; e0 z/ m' h' ?没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。+ @4 |* z* @" Y4 n) @+ E( [: ]6 _
' u5 i6 i2 e, Y; }1 { 关于APB和PCLK; v" k) u' h- C" C4 D9 o
4 v- I: i2 y' M8 M9 v& w" {. u, QF207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2
( @& S6 h4 m; Y' _; H3 w+ \8 Y1 c( W! [) N f( A' R
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册
, t1 O/ y) l4 E" q4 F8 I5 I3 G# E$ M0 E) [" H, ?9 s
/ b0 Z: L* L- J% P+ O0 o; k: @* ~
$ X& f9 y( I- K+ B/ x% `# m& S! c+ p9 q! S2 @% _8 u
& ~( o m1 |6 b* t1 w' K
: n# {& k7 J$ A6 Q4 U
0 a# f' S6 K3 q% v$ M
/ Z' r7 p/ I4 U" T5 o
6 P0 b9 S0 i6 E: u |