板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
E4 ?8 B& c3 g% S: b0 J
! c6 v# m- m) S# p01、时钟系统介绍! D% Z0 l ^% f
8 Y, _$ `' F( d+ Y' }3 |4 g( G( \1 \& Q
▲时钟系统专业名词缩写
) E: ]0 d6 h3 t( O9 ?3 X" H- @/ |) B1 G
时钟系统关键组成部分- V8 [; u0 V8 k7 \; o8 T
7 F: x# E: y ^9 @& d2 E01 内部高速时钟(HSI)
/ q: H; C4 |$ B$ h9 k7 h. l
2 n/ b! n) X( R, G" VHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。2 M/ l$ S6 L- C9 g! H0 K% X G: N
" Z$ Q; i' {- w0 o
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。
& m1 F6 p# j8 t# O8 C8 F0 |4 f0 T4 I$ b2 j; s
02 外部高速时钟(HSE)
; d: D+ G2 R* G: a1 u, a6 f2 Y, l: v
# i6 ?- o: ~& ^. B. T外部高速时钟信息(HSE)可以通过两个时钟源产生:& \+ t4 N; t q. o6 O
8 V$ k1 U+ Q- @8 W- d& C- x
① 外部晶体/陶瓷谐振器5 f: V$ v2 q1 M, t! u9 U
1 \8 F- g! J `+ R! K" O② 外部用户时钟) O. C/ T0 \: \/ v$ Q6 \6 r
. f& V4 H' j7 E$ ^. Q0 f( B+ U- Q" D% E# n# F G/ ]; z
7 c5 b2 T; { H0 f" z3 A▲两种时钟源接入示意图' k) N5 o0 H3 Q2 X0 F( ~! o0 Z
- _) O- e! b; J: x- T- W& S. `" p2 ?% \( a! |0 a1 K. B' z; q9 ]
03 主锁相环时钟(PLL), m& Z5 ^7 Q4 b* y! ~: P
6 | h' n! l3 {" {# }; c {STM32F2xx具有两个PLL
' I# v' S) \3 n `4 y% {0 ~( S% ~. @% f8 P7 y: ^6 o3 \5 h7 s2 f
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;
5 l+ }4 c5 z* \ {8 \$ H1 |% o! D0 v! Z
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;
/ Y' \8 D3 p+ ^! j
4 i7 C3 f% v$ A! {, m
( J2 m' u( k" t( ~
, }, k' d6 }8 ~; E* k I$ j) I8 w' U' ?7 O& i$ X" G1 Z
HSE/M*N/P得到PLL时钟
" i+ Z$ C! _9 l6 ^2 ?0 a% z* C
# ]5 |2 n1 W+ c关于PLL锁相环说明
( `' y9 v4 [* e& S7 u
1 J1 V" U M/ ]( u
7 m2 I* G: ^' I3 M6 Q% y$ S
% j" P# `' W0 B# y/ R) ^ u: h; T! Y9 r1 M) y
从1处输入,3处输出是1的N倍。( M) ]4 ]* s: i+ n: ]
( o3 F. V" Z" y
3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)
7 q+ U0 B, m6 L0 D, l& U( t2 @8 P3 \" c) {. B; H: K
8 e& \% h' d3 N# x7 t$ @* Q5 z
8 ~: F p, T4 j Z) X
04 低速外部时钟(LSE)
! A" L5 B H! N2 a# _ w- V/ q) ?' h
+ }8 u# F8 N3 w' K* ]+ oLSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。1 y/ p0 M& F o5 a3 @
: l$ k6 T" D! Y+ \
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
7 L% D% x, Y' Z9 E
+ L C8 H3 Z- f" l# H/ n) t1 r9 H9 {9 c) y% z
05低速内部时钟(LSI) @8 h1 V) S8 c* t1 x
5 `4 W3 `. m' x: u. k3 R
LSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
6 G* {6 V; }- D7 E& j5 }
9 X) L0 r. [3 Q, A0 o& t02、代码分析0 f3 ?, Y6 ?1 ~1 N7 O: c2 V+ {
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。4 }1 W. Q) Z6 L
# P B" Q' z2 ]! u7 w, B- =============================================================================
4 X$ m4 s( {& U D Y& H0 b - *=============================================================================
' C( ?2 B# @2 t. w. n! p, R - * Supported STM32F2xx device revision | Rev B and Y$ e8 L& v5 X$ k
- *----------------------------------------------------------------------------- G( | T1 Z" c2 o
- * System Clock source | PLL (HSE)
! n7 _& w" c- c) e/ j. L3 { - *-----------------------------------------------------------------------------+ |. G4 e+ S- k6 B
- * SYSCLK(Hz) | 120000000
* \ C8 p6 V5 ? - *-----------------------------------------------------------------------------8 f5 _' p7 s, b' N# v
- * HCLK(Hz) | 1200000003 Z/ a& w' T! N+ d1 t$ U
- *-----------------------------------------------------------------------------
! X* P( p, [- b* c! y$ V - * AHB Prescaler | 1
2 C7 H5 s: p+ V- j' \' a. @ - *-----------------------------------------------------------------------------# B2 ?: n6 d# Y, j5 ~
- * APB1 Prescaler | 4
0 P$ ~7 v1 t5 P+ x2 J - *-----------------------------------------------------------------------------. V. x5 @8 k/ P, j7 S( z! A$ m2 l
- * APB2 Prescaler | 22 }4 X! k2 J6 m6 T: J6 x+ _8 j
- *-----------------------------------------------------------------------------1 x# u8 g5 W( n+ m
- * HSE Frequency(Hz) | 25000000
' g) Q6 \5 z+ T. K: L% `% G - *-----------------------------------------------------------------------------
+ o( _+ J1 A/ N5 k# \: \0 ` - * PLL_M | 255 Z$ M4 X1 y; t: T" @$ p( S6 S
- *-----------------------------------------------------------------------------
6 w& W. z( v% v6 S) g+ h - * PLL_N | 240
/ P# y2 u+ r9 X5 | - *-----------------------------------------------------------------------------. r3 y" o/ J) t( b' C( O2 C
- * PLL_P | 2
# e$ O- \& V+ D8 R: G# \% s0 ?* O - *-----------------------------------------------------------------------------$ S4 p" l1 f& i3 _& }
- * PLL_Q | 5
; O# F! s- |( T; i7 [ z - *-----------------------------------------------------------------------------6 e4 V+ F" y { c
- * PLLI2S_N | NA
) p5 q R+ J* g/ G9 R- c( @3 ^: B - *-----------------------------------------------------------------------------
% O& k* }4 e& a! c+ K& q - * PLLI2S_R | NA
7 A) h: @+ q r* u - *-----------------------------------------------------------------------------9 I* m- j8 z" U; U) N$ m
- * I2S input clock | NA
2 _( {/ j; \$ n0 C# L( N - *-----------------------------------------------------------------------------
) O( U+ [# b# }. q7 p! W5 s7 `, v+ M - * VDD(V) | 3.3
- c, F F1 `8 l* V( t& ^- M4 Y) e - *-----------------------------------------------------------------------------8 k# K& l' v* w6 k9 Z% Z
- * Flash Latency(WS) | 3
6 K% w9 V$ h6 \+ |3 d - *-----------------------------------------------------------------------------
, f. y0 U% E) z4 J - * Prefetch Buffer | ON
5 w% R, F8 w+ g' T8 g$ I E - *-----------------------------------------------------------------------------3 I, `8 ^2 C5 h; p. e! p. j
- * Instruction cache | ON U: W" L8 ~ r
- *-----------------------------------------------------------------------------
. Z8 |3 S" }5 B5 h' J7 a - * Data cache | ON# ^, D" R6 G V
- *-----------------------------------------------------------------------------3 b7 F7 p( |/ Z0 q4 O
- * Require 48MHz for USB OTG FS, | Enabled
4 A8 L" N' b/ G6 L9 m - * SDIO and RNG clock |
# D4 g% f0 n e# f3 ~: e. q - *-----------------------------------------------------------------------------6 l/ T" Y. L! b# G8 j7 \6 k& U
- *=============================================================================
, L$ e' [2 o0 Q* a0 ^. t4 [ s0 j - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
4 j7 r& u! Y& p, Q* N- uint32_t SystemCoreClock = 120000000;
复制代码
1 ^8 y' h- r% p* R, e时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
8 c6 ]- W1 S2 T& h
: f8 v0 D6 |$ ]- IMPORT __main5 W# v4 b' f. d8 g! F
- LDR R0, =SystemInit- R: q2 J6 I1 z! i# Z
- BLX R0) i" q8 t# q$ [: `
- LDR R0, =__main" z4 ?* v! M( I, z) l8 y3 b0 R
- BX R0- V0 E2 \& U9 I; g3 P6 ]. H& P
- ENDP
复制代码 * d9 @6 c# t- o3 S
在这里说明一下文档版本的问题:
; S' [6 Y2 z' E' `* Q5 R
0 d. c* \/ B2 [# \/ W
v2 ~! y; Q& {9 z" H- b" b- k9 a# H$ @7 U, {/ _
▲STM32F20X_User_manual的V7版和V8版对比图" w6 D0 H6 M I7 [( A) Z# H
/ ~; f9 {4 n8 c9 V: Y* u
上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。0 t6 ~! k+ I! S1 P' m
% s# V7 w$ _$ r% Q; d
下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。8 }* l! N, I8 [
! y- e- T: b' B$ s0 P9 W2 A. B! D& O# |
1 _: e- e' Y2 F( @( Z. o" Y* D, P我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:
3 x6 |" i/ r4 ~ S6 B( W+ F; `: |: h, K& |% f
1、使能HSE
* o: e; }) q. ?7 j$ ~' ^
* s, W# U$ q* N" d! C2、选择HSE作为主PLL的输入时钟
- x* Z6 G+ W+ L) K0 H' ^, O) `# K$ ?4 A
3、主PLL倍频后得到120MHZ时钟
1 R0 k; x& C$ {. O {8 y
4 K$ o; Y, {: V3 O. p" y' N' f% F4、系统时钟选择主PLL时钟输出作为系统时钟' F0 j' Y4 V9 G% f$ C3 C
1 t7 `" v. y" e" ^% Q/ m3 V2 R9 J' H
我们找到对应的代码
: L8 g& s# M3 P4 N; M$ B" Y2 `- L. `7 F9 s# T2 J+ U: _
1、使能HSE% T7 G) [ K2 c3 m+ K) i4 e
- /* Enable HSE */0 W+ ]' `1 n n; q& |- t. n
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 # ~- V) ?, {7 v9 G1 x% A. `: Q2 q
在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位% ~8 ] B! n2 d
$ k3 m1 z5 _8 `3 n) _
3 u, v5 }$ z; i1 n2、选择HSE作为主PLL的输入时钟
0 [: h2 _5 }' e4 n9 O) j2 h- /* Configure the main PLL */9 M s9 N6 C9 n- J4 x: S, Z) |
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |4 Z- P' {% r6 W0 w* r5 Q T- J/ F b
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 : V. b! h7 i0 I& @6 ^
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟
. s# e n0 v0 h* W( d; ~9 M
/ ?! j Z3 L8 G, w9 Y# s 3、主PLL倍频后得到120MHZ时钟7 `6 A: v' }( J* J9 s U. C
- /* Configure the main PLL */
5 W* M3 k% k; A" O; m9 z - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |! q9 N" D1 Q& h' |8 z6 X) @
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
8 H+ `! \9 P) [! b4、配置主PLL作为系统时钟的输入时钟& M1 t. o" |) f& l% R+ i( j( g' j
- /* Select the main PLL as system clock source */- ]4 t: R) ~. x# \! Q6 I. @
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));/ D# N2 f$ A: a4 W! ?
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码 ( B+ l! b4 L P; Y1 O
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明% _) T }1 g7 u$ y
& v* x1 p0 Y# m" ^( m
( T' u. q: x2 D) k* I+ g. l0 g
$ ~# Q) G+ e! b( d; ^
整理后得知f(out)=f(in)* N / M / P
; s3 _) }1 ^2 i- w) i- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */- \5 L# J' s; f& t' b: o2 ^
- #define PLL_M 25. F: s8 H- L8 }2 E
- #define PLL_N 240
& w3 ?7 p: {# O - /* SYSCLK = PLL_VCO / PLL_P */
& W' r; P p1 m - #define PLL_P 2
复制代码 % }0 v' g: s: P+ ~# c5 H
这样就获得了120M时钟
6 W" Z8 m6 \( t" N
) H# A6 L6 _+ l注意:
; V( c5 X6 z1 H# K% [% w
8 X( o' b. w* L' H7 N8 X8 u
$ n; O& {( `9 B/ Y' U$ i& ~2 D3 h$ k- x4 w$ ]* @1 e, _0 ^7 d& ^& p
PLL_M大于等于2且小于等于63$ L! ]5 e) a2 `7 F0 S' G6 o% o
& Y5 Q4 G2 f$ }1 w# r
3 R; J8 F% O6 F
7 t6 Q M8 J a& m) |8 T0 D1 r
PLL_N大于等于64且小于等于432
8 V( s+ L! [ U# R% |1 k2 X6 a
2 j. }2 q$ p) m- ]% \3 |3 q8 _% U* Q/ M3 Z
. A4 g/ Y( C. w$ ^3 E* q8 ]9 {PLL_P只能是2、4、6、或83 \# p( {4 ?" M% K; I' O
; A+ r% F$ `) o* Y但2对应0,4对应1,6对应2,8对应3。, A9 m, P9 l6 b' F) B7 L7 m9 d% T" K
5 [1 c, W! y, w
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。
" p" o. B/ `4 u7 u7 I- _% B0 u. C; ?& M
(((PLL_P >> 1) -1) << 16)5 U; g* V1 e' k, V1 S. F6 \
其他外设的时钟配置时
4 O9 M, ~; K ~6 ?% l2 `" r5 i" G; r- /* HCLK = SYSCLK / 1*/
! Z. g- o8 h$ A! G - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
% s; Q% Y* q4 n+ ` - /* PCLK2 = HCLK / 2*/" h, o& K' ~9 z
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;' C/ P) `- g+ {8 Z7 S8 y; R/ a* D
- /* PCLK1 = HCLK / 4*/
# W. I! h9 l/ N5 {5 b2 o - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
/ k3 n9 l$ ^/ k: t; `03、备 注 + T" g7 r1 T! [; w7 h
时钟中断. V2 P. N" W; k1 s! A2 u* x
& [9 O; r' V2 o# x w
L: P0 H9 \0 L* L. X
m7 V. g ]) F2 {3 n
可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。. x; F6 r/ [: ^" h" X
1 M- O2 w: z5 _& D 无源晶振不起振; m! j* Y- ~: P6 E, v
( U3 w q2 n8 P2 a
没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。
/ {2 r% ~, C' m) o1 f. w- J( _
/ K3 ]7 w. Q$ q: e) U, V+ f 关于APB和PCLK6 y- q! l# D/ N) f
) F& a: @) b) O" wF207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2
4 F; [, v: |, [( r& f. j* C
) S" i6 }2 S5 H+ {应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册* U3 P, u3 Q& M( g5 p
! P8 S3 y% K( N4 ~
5 H% F/ V* a q& [/ J( _ @9 i: d( \! o+ e6 {
" V( g4 d) {9 }. x5 S
- S: b z( x" K$ U, {) e
5 J7 e G( j5 f E8 }, S2 x# p; w& B) O
; X! V& |% S. c9 { [0 D; s# G+ d, c9 `
& @6 S5 G/ N$ K: @% _ m$ X |