板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
# o2 n$ C% M7 u+ [! y# B0 ~" N7 D0 x# n+ i& d2 J
01、时钟系统介绍
$ L6 u. P" e. j& |' ~3 B- p; ]0 {% K) q1 L. [# v
3 z. T2 [, m; @: r8 Z ▲时钟系统专业名词缩写
+ K8 Z5 U0 \! ?+ s, e2 ]
+ A4 v- }3 v$ d. H0 x时钟系统关键组成部分. T) Z0 X/ ^& p* z
' Q4 S; z4 u) q9 f# L01 内部高速时钟(HSI)* N: R! ~% \9 W) R+ Z l
" m1 z, s3 q$ B, i* B0 j% T* n) s2 iHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。, i. a. C- |4 O* ~( g( a
$ B% Y9 I8 B; M2 h
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。' n/ ]% K0 d4 S _2 s
% ~( b6 ]# {4 O* R, c7 c6 P02 外部高速时钟(HSE)9 p B; i1 I7 C9 j3 b8 O" z
h5 F- e" D4 O' L8 \" B+ r
外部高速时钟信息(HSE)可以通过两个时钟源产生:9 k5 a, J- H1 O
8 j6 A8 ?- l4 w5 s [0 ?# j) S① 外部晶体/陶瓷谐振器- @# f4 z0 f$ v7 k
' p# j. {. a- c% s; u3 V
② 外部用户时钟
- b* g- n Q# q6 B8 ?& [/ E2 X' O
: j. b9 J. t; w! J( a
/ A6 F& Y" U- O, S9 g▲两种时钟源接入示意图- j" T# f0 }3 B
`) a: Y4 w; D9 ~+ \
; k4 K( `& U' E8 b& y$ @03 主锁相环时钟(PLL)% x; E* M5 `/ x8 J
, _! C, }& l2 [4 @- ]
STM32F2xx具有两个PLL
1 o9 q( a" i4 m. e' `, B( n& K0 p' u/ S" W! L
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;( t6 o0 V( B! e4 b: j
5 z7 b6 p1 J+ l) d② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;% ~( {0 P& s) H I/ }
; v& ?; y8 B P& [2 c, y8 Q$ B
T2 x6 x# X4 X' w! l _# p$ D
- W! D( g, D5 d9 W( E A) v* j' d y$ w+ d4 E
HSE/M*N/P得到PLL时钟' M3 i! Z* n* K% J! @$ T* M4 }* r
5 u! B+ N1 ?; I5 }+ u6 B5 \! d3 G关于PLL锁相环说明
* r0 X2 ?. p( \! V# l$ s. v, e& M" B' F. V) I' E# }; M) K5 b
, X* j6 Q. V6 s- E1 M
# a' ^1 Q, H4 g& r* o
! q# J. ~9 ^" [* Y- B. L& b% s从1处输入,3处输出是1的N倍。
8 ?" i: s! b5 l3 I: P8 G2 o, A$ E5 ~8 O& y) H* e
3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)+ P" j( l& H* A& S W; ?
& D+ C2 y$ n/ e( D" `1 U" ^
) V; S6 V) ?; m4 R$ ^$ a; o. J) T5 }+ m# y- o
04 低速外部时钟(LSE)
+ M9 S7 k' B2 S1 Q5 N2 s4 G+ c7 T
N$ d. n* N/ Y3 \1 N: J _LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
. ]& u* @; F; Z. l: f
8 n- u% h: [1 }' y5 X它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
6 K4 n- c! `" }, n# a
% O$ n8 e; r" P/ a5 ]* P9 X+ ~. a4 J6 S! z$ @( J
05低速内部时钟(LSI)! q- |, B5 [# @* d2 L# h: y
* |* N: Y" L. X5 q( ]: m9 V, ZLSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
6 W; s ], i9 [6 z/ \# `2 P4 ~
! h0 B5 {' Y9 z3 T02、代码分析
& n6 o! ~: D a时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。
/ [# w4 x* L) P% O9 y8 h- F- B7 Q8 y6 j- M a
- =============================================================================
6 b: O. g Z7 a: a- }+ _ - *=============================================================================: e& s G- Q0 k" G2 S6 N2 `$ j
- * Supported STM32F2xx device revision | Rev B and Y. e4 W( I5 X7 Y$ { E
- *-----------------------------------------------------------------------------
( z0 x, j' {) p4 E - * System Clock source | PLL (HSE)0 |4 s0 {' N4 M# g0 @
- *-----------------------------------------------------------------------------
) [. A6 M1 g- \1 Y) h - * SYSCLK(Hz) | 120000000! _! u7 k1 c( B; ]$ D% |1 O! d4 I: e
- *-----------------------------------------------------------------------------$ r$ T- p$ s6 Q5 [$ Q- O
- * HCLK(Hz) | 120000000
/ z* u& h- e6 z4 }+ _. o' x - *-----------------------------------------------------------------------------
% x$ T3 }* T0 d1 I( x+ q+ b0 G - * AHB Prescaler | 1
& R. |" K N8 o - *-----------------------------------------------------------------------------8 [" Q0 T G- r" E
- * APB1 Prescaler | 4 U0 e1 u: N8 Y0 ]
- *-----------------------------------------------------------------------------2 S- j4 b0 S. H6 l
- * APB2 Prescaler | 29 j y L5 D; S8 t7 T. C$ G* ?
- *-----------------------------------------------------------------------------
! \( e3 y7 I6 q) B3 e - * HSE Frequency(Hz) | 25000000
- ?4 K% g8 [ Y, c d- k - *-----------------------------------------------------------------------------5 V* E% y2 [* E; ?) P7 j& n6 z
- * PLL_M | 25
$ O8 u- v- {. m) `4 R - *-----------------------------------------------------------------------------
: c) H% Z. y) _& y% q. H. B - * PLL_N | 240
7 M7 n- T) H# C; \/ i$ b! E - *-----------------------------------------------------------------------------
0 L* H7 T) ~7 s- e% C - * PLL_P | 2
' T Y& V v2 f2 Z1 [0 C* Y$ l- O - *-----------------------------------------------------------------------------
6 |7 K/ I% h, _ - * PLL_Q | 50 w3 X1 `/ H% _
- *-----------------------------------------------------------------------------
0 Y* _9 k) k$ y$ @8 o5 L - * PLLI2S_N | NA6 }- P4 `, U: S" ~9 G
- *-----------------------------------------------------------------------------
' S9 k" |3 \4 a" k. Z - * PLLI2S_R | NA
' f2 H8 n3 d& Q2 U9 p, K1 J - *-----------------------------------------------------------------------------" b; w% |- h6 I# ~
- * I2S input clock | NA7 M" b2 ]$ m% f9 A. C1 o* K9 E
- *-----------------------------------------------------------------------------6 B6 j5 A4 V" L1 _0 ~
- * VDD(V) | 3.3
. j; l4 p" l1 ~. F4 n& v" f1 Q - *-----------------------------------------------------------------------------' u& }8 I) x! g7 S" @5 ^. ^+ j+ y0 C
- * Flash Latency(WS) | 3
: N7 Z0 ?" G1 D5 \( c - *-----------------------------------------------------------------------------
% U `0 o& [1 J& O7 g' g+ R9 i - * Prefetch Buffer | ON
; X1 s+ P0 C1 ~; ?8 M" u( t - *-----------------------------------------------------------------------------
4 L5 _; _: M9 ?7 `$ Y" k! o( R - * Instruction cache | ON: s3 Q7 t: |) C4 G/ M7 F/ L
- *-----------------------------------------------------------------------------. g# m+ l$ ], I0 L; O' ]1 _
- * Data cache | ON
3 L$ N) D& V, B4 a/ g: Q7 c7 m' w - *-----------------------------------------------------------------------------
* t- _( t9 E9 B- y. r - * Require 48MHz for USB OTG FS, | Enabled0 u: H* C' `, h4 z
- * SDIO and RNG clock |
$ k8 v& o% q! O - *-----------------------------------------------------------------------------/ ^, g8 B% Z$ I4 ~/ h
- *=============================================================================
Y2 B! M% u, b9 V, r* q - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。* a- i. O) L( B' R
- uint32_t SystemCoreClock = 120000000;
复制代码 $ d" L9 @) u3 ?. v
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
5 ], ?" e2 f( n3 J& \" o* |5 R N: H; g3 C6 y, N2 a1 n
- IMPORT __main5 }7 }7 N1 Z( \5 t" z
- LDR R0, =SystemInit4 Y) u( \+ ~$ X
- BLX R0
% X% W2 y( {# T9 s5 Q - LDR R0, =__main
8 ^7 p: F+ R y3 ~ - BX R0
& T6 n3 R' E2 P( v. x, i x* m" J - ENDP
复制代码 % L- U& T. f; h7 R/ d, r" O
在这里说明一下文档版本的问题:
/ G1 w' B& O. X9 Z5 G }, G1 u* J% H( f! X. H5 _( N
& d. q7 g* n3 f e) M$ T' h5 a9 N3 \! @
▲STM32F20X_User_manual的V7版和V8版对比图1 B* i0 W: w% Y/ ~1 b& R) @6 J
7 }# k8 E# k% [) e% P0 Q( c
上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。, V. D9 S6 @9 h2 m
, D x# m9 c" @/ @/ O- v下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。
; z; R5 V4 b2 G8 q, d& q9 R& x' `6 }* f: c
/ V: W$ ^8 k2 t+ x0 |) \; V+ @/ G% Y4 h6 J3 j$ t. R `
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:
0 f/ |1 n& E* \. D/ _. J+ [4 D0 ?7 x! u0 _ \! W# Z
1、使能HSE
7 \* a/ N0 g1 C# @, ^ a% h8 L" Y
2、选择HSE作为主PLL的输入时钟' i6 Q' o: t( I+ g0 p0 l
% l7 S: B. C2 W# e1 k% p
3、主PLL倍频后得到120MHZ时钟
, P, j+ q4 _5 k4 b( Z- ^
8 H5 O: k$ D4 W6 ]4 D4、系统时钟选择主PLL时钟输出作为系统时钟% {3 ?/ G9 O: J1 p% [) u- p
7 a3 n: b+ L9 D, R
我们找到对应的代码
" a2 u. ^8 P. \/ s% ?5 g/ k9 Q- [1 s9 X+ U$ p9 v- d
1、使能HSE8 \& l7 { o; y' H
- /* Enable HSE */9 k! v; I. ]2 _7 s. u
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码
7 o' m9 M# x* G: D在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位3 g, H9 x5 r3 I1 S6 w: a
& C9 L1 L1 X: W+ ~! t+ e/ O& u7 k, m5 p1 b. V; f5 ~
2、选择HSE作为主PLL的输入时钟! w1 i8 q6 ]) ?$ X$ X. M" Z6 ^
- /* Configure the main PLL */
% A8 r; s _8 G" G% f$ q$ @1 O - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |, I% g9 q$ u2 Z; |2 _/ W* @ {
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
! F/ q: d G j D/ `0 mRCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟& S! G9 E9 @, Y; l& j
* |1 [4 n) ^3 a! e8 R! \3 j
3、主PLL倍频后得到120MHZ时钟4 R. M/ _# c k1 r: h5 W
- /* Configure the main PLL */
, g& j" N" k, ]1 \% h, z - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
9 F$ H8 b- x6 E- R& E( J6 }) L3 s3 e- e - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 * F& c/ ]7 o7 V2 u
4、配置主PLL作为系统时钟的输入时钟5 J: s0 m; g( g/ C. S6 r! B
- /* Select the main PLL as system clock source */
! g& T W" c3 R$ U! g - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));5 |! f, p1 Q9 ~; ]
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码 $ X4 z# ?# F8 f9 c7 ?' d( y
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明
3 B @- s! |6 e2 b* w! I! V$ W0 _7 N+ D+ r
- n2 m/ [7 _& G* M9 f" X$ r2 t1 i+ `7 A7 |1 \/ }( Z0 U& C8 C2 O5 R
整理后得知f(out)=f(in)* N / M / P ]# i8 ~/ C( K, t' G; y
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
* N( U" k, U* a7 X - #define PLL_M 255 g7 X) ~ }" ^4 T
- #define PLL_N 240
' C$ J$ S2 B) M& x8 j; k" V+ C - /* SYSCLK = PLL_VCO / PLL_P */
9 S: D) v# k4 a. I6 i8 L% r/ m+ q- {% M - #define PLL_P 2
复制代码
6 q. [! u: k. y( G/ S这样就获得了120M时钟( j9 ]& ^6 E0 S
3 v0 a$ {( b3 C5 X
注意:% ?* U* {( S/ o9 ^6 w: j) q
- t1 }* k! J3 S" J4 `. g' G! }- c: M) e1 J$ s6 d& W5 C# n0 L
8 P. u# c- A2 j. @0 [# S, h! w/ XPLL_M大于等于2且小于等于63: u/ o3 Z: G/ A5 R- x
- u9 ^5 P; [ D% A6 I7 N, v7 X$ _ e) Z7 O. t% \% D6 V
' e1 `$ K8 f2 O( V% }PLL_N大于等于64且小于等于432
# Z* p3 ^( ^( M) ~6 J' |' F9 g$ G6 ]" k3 f @# k
& T% A4 g% W" e" n; N- W) u& i7 a7 w$ h6 f
+ B# e+ Q& N. N% m- NPLL_P只能是2、4、6、或86 ]! s+ q; k9 p* e. y) K# U) i
. [% K) }1 J' y2 K9 A* T6 A但2对应0,4对应1,6对应2,8对应3。
6 V( `9 {( I" P
3 \% t* d" O7 J2 oST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。
, {7 T) R9 k' l ~- }* B: d3 V- v' Q" D p8 h
(((PLL_P >> 1) -1) << 16)
2 p9 H9 o" A$ [9 Y其他外设的时钟配置时. Z- r8 N1 z2 s% i
- /* HCLK = SYSCLK / 1*/1 x2 E }1 r8 k3 `3 g4 x
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
* Q, {' ^+ K) M9 \# X! @ - /* PCLK2 = HCLK / 2*/
5 B% I9 h1 @0 {0 U; N. r1 \) R7 E - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;0 e7 P. y' I* J# B
- /* PCLK1 = HCLK / 4*/
) X3 z0 d8 B: M0 v) ?3 } - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
& ` F0 S" d$ K- e! I6 S# Z03、备 注
5 w9 a ^( s1 h: V8 ^时钟中断
$ W7 p6 `, z- v" ]9 ?* t: s5 Q0 l" ]9 G& ]+ ]3 M9 A
: o& d' V9 S$ s5 Q
0 c$ R5 C; G& c, J9 f2 ^* q
可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。1 }8 ?4 }( d+ F0 d; I& j! w6 @; d
0 U% d% L% Z% k3 J# k6 f8 l
无源晶振不起振" r1 g" I) ^7 t3 \5 j
$ A: R4 y) K9 o+ K/ u* |/ N I7 c没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。4 ?* v. m9 |0 D5 C2 N- ~
1 h; ~. t3 J8 \2 z
关于APB和PCLK$ }/ ?$ b5 B, c
8 J- @# A6 V( u$ CF207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2) A/ b' p' B% R1 Z
$ Y/ T' C c7 _! I) a; \, m
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册
; D1 v" Z0 b7 A G+ w' f9 p( `& ^# v: @6 `
, C6 S7 d$ a1 m& X1 |% e' U
, X, t3 d: O/ q4 ` |# j8 ?+ _2 z* m8 K- A: S( R* W, S
& v7 i, P5 h7 }" T0 Y8 ^
7 G' j5 y( r( s5 _! C9 U$ P8 {
, e6 G0 M4 r' M% ~
, ]. b/ u5 O3 E- t$ g
" n4 u" y7 L5 I: R- Y* u; n |