板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。' w. v, W, C, J
$ o9 \# j- l8 ]
01、时钟系统介绍
* ] f/ n# q7 e. k4 v6 p! l' X, E7 R' t3 d0 B
' k. ?' ~; b2 B! f* P c8 P ▲时钟系统专业名词缩写
& w0 h$ z0 I5 K4 Y4 Q# @' V
6 N1 I* l2 v8 i8 n( H; L时钟系统关键组成部分1 z) L+ ^ X. V& A" {
9 P8 q8 b9 n4 o
01 内部高速时钟(HSI)
8 W; S4 u2 ?& s& {" m6 K, w$ z. K: U7 w4 H# _2 u+ o m2 F, b2 H' u* l! V
HSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。) Y; Q' f) B6 [+ q8 @" e
$ I- Y! L0 r) Y
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。 K/ z9 h; l3 e! l9 V J+ T
7 E& J6 G |5 m* J
02 外部高速时钟(HSE)
0 D1 H7 c+ X9 _ V
5 |9 Q. W" `8 c! Z$ \# d* g/ k# Z外部高速时钟信息(HSE)可以通过两个时钟源产生:( ~1 d0 E3 G' K
# y. p0 L( g; g6 W- [4 i
① 外部晶体/陶瓷谐振器8 H( y: j- X X5 f4 ~! y1 R
' `& n9 i q" P4 L
② 外部用户时钟
) n. M% I: U7 C J4 ?; T
& A* D( z9 ^( C) s Z6 y* P. V( Z3 {: k9 {
3 c& Z8 A8 h' B8 R
▲两种时钟源接入示意图5 K" r' L2 C' Q6 ^( C$ o
I' e* v5 M) ]8 m
0 D8 V7 H6 U% ^03 主锁相环时钟(PLL)& z2 ]4 e& ~% d% M" E3 ^. [9 u
( s6 `& ^6 z! w& D; a2 hSTM32F2xx具有两个PLL$ I, |. r) r0 J# F8 A
) J0 P3 M1 C$ C& `9 |, f, J① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;
% H5 Z8 c o2 y, p" f
. P* f- Z6 j; N5 W+ l3 |② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;
0 [. v6 x" r0 @' Z! ~4 a( Q! V* J3 L. A& s9 W; N% W3 C( O
1 R9 G1 q* m7 B; @4 w0 h$ Q! H9 v
' _; Y) n& A( ~9 Y/ `% @ S! {9 n: I* j( F
HSE/M*N/P得到PLL时钟
: m+ k" B8 k2 G7 o( T/ T4 ^% X3 T$ B4 T7 V6 T, k9 o" v
关于PLL锁相环说明) G: H0 R* P( L, \- m7 ^4 g- Y( U
/ F+ p7 O& T/ e8 ~/ A1 I1 x# g6 e/ s7 s4 ~# T
; ^% J3 @, }% y1 t" Z5 `: t' m9 y; j: V/ W' F8 g3 c+ t* J
从1处输入,3处输出是1的N倍。( C- i7 q5 L8 \% s& X4 \
5 n0 n- p: Q* p( Q: q+ o3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)# I0 b I# t7 f4 i4 L8 j
( m$ R6 \( c6 M$ A8 q
& J. l0 W1 @% S6 r" `1 q% o: m O
: s* P8 ]3 ^* r, I+ O( u04 低速外部时钟(LSE)
) Z; u. N) a: H5 ~1 I# v
- ]2 F* t' u8 h$ X' a! g% LLSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。$ ~4 M% J' G, A' _8 \
5 l& _. i0 T- U; N
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。4 d( X; y( Q$ A: O* e! V
) Q; |2 I F; q V4 u- R( b2 M
+ [) V7 }( Y, P1 i
05低速内部时钟(LSI)
/ v( k- {1 y- d8 ]" m
: ?% E* F; b4 A7 C- [8 wLSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。3 w: ]6 S, h: g: P" |0 S l5 ?0 O
2 {* a& K! G) Q) j
02、代码分析
+ T; K4 G* `, U2 |( ?) C: s- k时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。
: S( [8 b9 s% t& x9 U8 [0 [
" ]- X% W: p9 M% _$ S& C1 R- =============================================================================' |- V- n0 m/ h2 D. D* C! ^+ D
- *=============================================================================
% z' Z& M+ Y/ D( ~% } - * Supported STM32F2xx device revision | Rev B and Y' E5 C/ k7 i* @" x; s* {
- *-----------------------------------------------------------------------------# m3 L0 M1 }3 F/ L0 \+ H' \
- * System Clock source | PLL (HSE)6 |$ t% R& ~: e9 ~2 X, [3 o
- *-----------------------------------------------------------------------------. A6 w9 R2 y3 u, I
- * SYSCLK(Hz) | 120000000$ @2 Y5 n1 X3 Q
- *-----------------------------------------------------------------------------, [! `9 c# ?$ n" O- ^8 J* S9 d5 l8 W
- * HCLK(Hz) | 120000000- i, L4 J5 x0 }* g; G
- *-----------------------------------------------------------------------------
6 o) w9 \$ y% i# y+ h - * AHB Prescaler | 13 k0 O8 q9 N+ A9 T# j: N7 e. {
- *-----------------------------------------------------------------------------
y' B( c; U% T: l1 c% E - * APB1 Prescaler | 4& _ Z! C0 R3 s: _
- *-----------------------------------------------------------------------------
/ Y9 r1 ^; }1 r% \/ i1 p K. E; b+ a5 x - * APB2 Prescaler | 2$ h9 A' `2 x0 N" a/ N
- *-----------------------------------------------------------------------------6 e% j3 H; g5 D: n5 y
- * HSE Frequency(Hz) | 25000000
+ D4 T* K7 g3 s8 } - *-----------------------------------------------------------------------------
6 y$ U: Q0 _! D - * PLL_M | 25
# t) U6 W: h% g$ o - *-----------------------------------------------------------------------------$ R# z# g2 O( y( A6 G
- * PLL_N | 240
% _ j& @4 t4 T; |! {) I( y - *-----------------------------------------------------------------------------+ H" E: z. @. e6 w7 j. s
- * PLL_P | 21 r5 X/ T8 o6 v# Z6 `& @; G
- *-----------------------------------------------------------------------------
) y2 B9 z3 M! d+ C3 G, j - * PLL_Q | 5; b, p# }# W1 ?% y) [
- *-----------------------------------------------------------------------------0 j" b+ t+ R, K8 s" T) D& F6 z
- * PLLI2S_N | NA7 M6 a: ~. I/ G' ]* C- W0 ] U
- *-----------------------------------------------------------------------------
L$ ~/ k# C9 ]' u" @% Z, X - * PLLI2S_R | NA
2 @5 y5 `' ]8 a& t - *-----------------------------------------------------------------------------% i7 f W& Y* B! b \
- * I2S input clock | NA3 _9 H9 z( Y! f7 b, V5 }
- *-----------------------------------------------------------------------------& X9 W9 \* e' R2 h
- * VDD(V) | 3.3
8 _( h; B$ ] m( l7 w - *-----------------------------------------------------------------------------
9 A+ H! Q- D% S' s% Y2 }) Y - * Flash Latency(WS) | 3% ?& P5 K' L* G7 I8 Q9 d
- *-----------------------------------------------------------------------------; U( Z( ^$ p I i& q! z
- * Prefetch Buffer | ON$ t0 U) a1 m/ p
- *-----------------------------------------------------------------------------. k7 h4 e* t1 k0 H z
- * Instruction cache | ON5 {6 `) S, n# I# b2 \
- *-----------------------------------------------------------------------------( y( h/ |$ \0 |" V& d. N( V
- * Data cache | ON3 C! {' b. O6 _3 w' \9 K. T
- *-----------------------------------------------------------------------------
" g" V/ q' G Q9 P( V - * Require 48MHz for USB OTG FS, | Enabled+ j0 e7 m6 {+ D4 h
- * SDIO and RNG clock |" h- U& J2 A5 q; _2 x# p1 k
- *-----------------------------------------------------------------------------' K$ B, ]% J( k: p
- *=============================================================================
: j5 E% P( p4 q. J3 D7 V" u) T- [ - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
! {; P8 [) o: t0 r, |- uint32_t SystemCoreClock = 120000000;
复制代码
. d: [ w* x! S4 l& e时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
6 F+ ]- ~/ w) v) R& [( `2 p& c# W! o3 h& g4 ?" p2 n
- IMPORT __main, v m0 h- J+ o* W5 I6 f
- LDR R0, =SystemInit( s6 q. g) C$ W5 [+ P: p
- BLX R00 L$ `5 j( E4 w7 h
- LDR R0, =__main8 C7 y; m& i6 C2 G
- BX R0
/ c& ~2 s: J0 I) D I- C" p; E - ENDP
复制代码
# \5 i3 i1 J2 t% |- x4 M- F在这里说明一下文档版本的问题:! }! K$ L. H" u9 D: h# E
9 v# n. s2 {7 e/ L' X2 o0 ~( a8 K0 I( C6 T
+ g$ p. I) R* j X! r& Y▲STM32F20X_User_manual的V7版和V8版对比图! M0 O$ M/ {$ W7 Z- s
" Z3 a7 J" D9 g8 f. R# h4 y
上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。( n7 Y% W6 B2 T0 l4 F( V5 Z
+ `4 {/ r. Z. ^6 ~: m7 c
下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。
. `9 R3 E% w0 M+ h4 S% R: a+ v- M" g* v
; S+ z! K7 q' h' m) q! N8 o
9 s- Q) s7 ~- m/ |7 F% `
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:
7 r9 M4 O5 \5 A
1 W. b1 N0 V( m) f1、使能HSE
" b9 C7 b' p+ Q$ R2 K U2 I, h& F- a6 V
2、选择HSE作为主PLL的输入时钟! B: w* K# h! }& |0 a. v: H) e
+ B& p4 o3 G, e' F9 }9 g
3、主PLL倍频后得到120MHZ时钟
# U; F2 n" m8 h" _$ B8 v# f
' B2 p* F* P$ a4 z# ^4、系统时钟选择主PLL时钟输出作为系统时钟
+ g5 n5 ?" c6 K5 y4 u1 ~7 y# Z' {8 {6 e$ D
我们找到对应的代码
- E7 m% a5 M1 C
. N2 v( p* \* W2 c. E 1、使能HSE
+ ~! r/ `) `/ F; @8 a$ H9 ]- /* Enable HSE */( i" U. q* x8 Y. e) Q* L$ s5 Q$ u# [
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码
% ~! P" }# l E0 K' U在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
3 h2 o9 [ v+ D a4 k
: f. i( L$ O7 W Y, A E
: m/ O3 B1 p7 H: N y2、选择HSE作为主PLL的输入时钟
4 a- H4 t' l" q! O- /* Configure the main PLL */3 m/ o8 j @/ F1 b
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |" X+ k" g) x. Z
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 . G: F4 U! m! [
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟
* @+ i7 j: s# \1 ]( h, V
9 ~& p8 J+ a' i {% n1 Z- o, } 3、主PLL倍频后得到120MHZ时钟
7 y9 s1 R# C, |0 G6 f" H- /* Configure the main PLL */4 Q5 L `! M) X3 u5 n: y3 R
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |+ E7 x* \6 C# \* K- ^& K
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
) M+ B* b$ f* z4 R# t8 N4、配置主PLL作为系统时钟的输入时钟
' `3 g% |4 c; \! @/ N0 _( M9 m- /* Select the main PLL as system clock source */% q1 p% Z" B" v/ V+ C& d
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+ t7 B( j" O* h' I: `6 ?0 z - RCC->CFGR |= RCC_CFGR_SW_PL
复制代码 # b* k* Q( e9 B7 T6 R
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明* R# X* C" Q& ]
6 g: T. x- K, _- N# F
7 T/ S$ w" L: k% F, O- {/ _, ~
; c: ]5 V; j2 V7 T2 L整理后得知f(out)=f(in)* N / M / P' _7 F V* Z! e* i8 Z4 c6 y
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
/ i0 o- Y! v3 I" i `+ v: Q - #define PLL_M 259 Q5 T9 @9 \8 x0 C
- #define PLL_N 240
' g$ a0 A$ |8 X# h: I* i! W - /* SYSCLK = PLL_VCO / PLL_P */# Q6 E$ S, V6 u0 w4 Q! s* I: i# J5 S
- #define PLL_P 2
复制代码
\9 u6 K9 E% z. |/ ]这样就获得了120M时钟
( ^- E2 E4 W% \! j; E: N9 Y2 y6 x' ] P( L. F4 ^5 w/ J
注意:* M' r/ Q: v* j5 M1 i+ q
/ s: U" `* R! w; R# B
6 a" B7 w6 c/ u, B2 R) W8 y5 j) A) v) F
! m4 i$ k4 V) h! oPLL_M大于等于2且小于等于63; ~8 k: l$ t2 C
6 N. o3 O& [3 U- ]) t6 F& c7 O
" K) W# O; `1 l
8 q- O4 u( s7 ^4 {5 I2 FPLL_N大于等于64且小于等于432
4 k. r: Q, W. }% ^; h# T0 ^2 f) S- ^5 u+ b( v
$ q f4 m, E! O2 q) b! k2 N9 Z. m2 V9 B1 ]' H
PLL_P只能是2、4、6、或8
% Q: g& A1 W/ h# C0 J' k$ k! r6 D% t7 L: z7 z ^: V3 R- p
但2对应0,4对应1,6对应2,8对应3。
' |' ]; E! V3 m9 E1 A5 r" e1 i- C# x5 G
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。& H/ ]: e8 n+ i5 s% Y d
- X5 U k1 Z( t9 l) i
(((PLL_P >> 1) -1) << 16)
A! W1 B! Q* v3 x% P7 L其他外设的时钟配置时0 h1 s4 f- n' F, x
- /* HCLK = SYSCLK / 1*/
" _; I. @% ?2 ^5 _. K - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
! j3 C6 a! @, Q s- B+ Y - /* PCLK2 = HCLK / 2*/
( s: W0 ]1 Y( d$ g3 O% ^ - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;' x# s% g( A. ~5 F" V6 p0 o5 _
- /* PCLK1 = HCLK / 4*/1 I% x) V4 [1 k- ?6 `
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码 % L+ w0 _) E1 W6 ]/ E5 z! K
03、备 注
2 q$ | [- c! e) [5 Q时钟中断% }8 ]; X: f9 q( S
! M2 R* r. L& F4 T: p8 K: `/ u9 X( Q+ u5 s+ F* c
4 ^; N. L1 p5 u4 r可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。. s- p' n D G, v5 v: A/ q2 ~
6 N h# ^* L# a) E7 [3 I, ^ 无源晶振不起振
. N# B! O5 J% O# r( m3 i
6 Z- z6 ~! e- J) h5 X+ D" C9 N没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。
) z5 o1 }+ B2 k" C1 j( u4 p2 ~1 Y# e, I; H+ l
关于APB和PCLK
# k, H8 J9 a7 A' y9 {0 C# [1 w7 q O% R
F207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB24 u/ A8 q( D+ O2 O5 I4 y! F' }) O# M
5 f3 e* Q) t0 ]& h
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册
. _ k2 e R+ J$ C" x, ~# F3 _# ^9 {9 g8 m' R5 B2 c, q( p0 W
" ?2 j T; f3 z s: l
- v0 L t6 ?; N& o3 t
# a+ D) F. {7 v8 y" w' A
7 O: L9 H' O' ]- k* z$ x' k- {, k/ q1 L: _" M
8 t& {) n9 \. k, ]- b
. D, A# L, {' m2 g7 _/ \+ x" X4 h) O' i# o
|