板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
5 L- b2 j7 C6 M! l' m1 T5 w
8 X3 v% ?- q1 ]) E$ w3 S0 G2 E$ ]" l01、时钟系统介绍
% C5 q0 b) E8 R$ t
8 p9 H( T/ \: o% c5 B8 ]- r1 @$ l+ r; [% r" r4 h. h
▲时钟系统专业名词缩写. W4 g3 p$ b( w7 P
" v: b! f9 S% ]5 y时钟系统关键组成部分
$ U3 y" u4 i# j8 s$ q
% F/ U ]- ]8 U7 ^$ I( z! L01 内部高速时钟(HSI)& m' b- x2 d' x& w: n7 }- c9 `
# F* P8 T9 C7 X9 [% a( F0 q' SHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。
) m! Z( m4 C7 g0 [( m
* I0 P4 t4 u/ ~ [+ @/ p h1 QHSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。- d# s8 [2 a/ ~0 U) X/ ^
/ [, a3 g: x z4 N02 外部高速时钟(HSE)
9 e5 `5 P2 z6 z
) l3 B" r8 ?4 w3 p1 B外部高速时钟信息(HSE)可以通过两个时钟源产生:
! c9 H6 X! s0 ~' j9 v$ B
% x) |2 p9 y$ E① 外部晶体/陶瓷谐振器
0 F" o+ L, ?7 k' T# w, w. @
, J3 U& T9 |7 H4 W② 外部用户时钟5 g* |8 b0 e9 T+ H& a& p
5 C9 X! p1 H" f6 v' K: k, j
# e. J8 l: \& R8 k2 D
$ A& ~ l6 ?$ C6 T▲两种时钟源接入示意图
, p, V( i" T( @1 y) T) N/ R$ e/ d: F9 V" e( r: N
0 P9 }( l; G0 e03 主锁相环时钟(PLL)% V: a; H$ p6 Z; M9 U
1 j8 B2 R& x6 O. w1 @
STM32F2xx具有两个PLL3 Q2 C8 z* m' s( Y
/ x1 V9 q: @4 X2 Z! o
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;; K: l, Y3 V4 Y5 G# W& k
0 h! w7 p) p" c0 k9 l4 _; I
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;
- t9 c! B9 M. b9 ] C. o8 Z/ z! m# Z
/ ?8 N3 j5 J0 {) V( X' j
p4 _; O. b! B t
1 f4 {6 B3 D: H! w; I# _1 Q" N4 J7 W/ H/ [2 w8 v5 y" ?9 j
HSE/M*N/P得到PLL时钟
+ E/ R; I0 m# u% t4 K! b& m0 s2 W9 e) |8 u$ ]
关于PLL锁相环说明
. Z9 K6 d1 D' L$ b( b2 i; B
7 Y! y7 I1 i7 a( @
/ V# }+ J3 Q1 J4 G4 F. l2 a3 K2 \
3 h1 g' z' I E8 E" j, P* h- w: G1 h% V q6 M, h5 j
从1处输入,3处输出是1的N倍。% A& i& H, u, |6 L% h0 R9 {
7 E* I8 [8 i+ b& B/ Z1 }/ U. b: `3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)
7 C2 Y, [4 L. o6 U
1 T) @: G7 K* \1 x9 y# b: T; e5 s& ?/ d( c- j/ }% R8 x3 f ]
4 ^1 \8 q6 e M; u) k' z04 低速外部时钟(LSE)
, c @( q+ @3 V u
# U) b8 l. D" D+ |3 `LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
: G( H# M7 G; m8 |3 K
) K- t' {& s2 {0 H9 y6 N1 e它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
* E4 L, Q' ?: P5 `' C/ G, B
9 G$ U. I+ F0 U2 m* O
" n8 \4 R% @' H/ K/ s# S7 N/ L05低速内部时钟(LSI)
& ]8 I; X# |" J) K; N9 h4 l$ V( X: E6 h0 }! l
LSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
& x# Y& U) j* s- Y
e( D A# y3 L4 u/ ~02、代码分析0 C# f# F, s8 \! G
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。# V' f3 `- C; ~
2 b# o5 `% x K& |* e
- =============================================================================( @& S( C: o. E! N, v F3 ~1 T, ~
- *=============================================================================/ `+ E5 b4 p4 ~ {8 F, h; \& S' f. }: `
- * Supported STM32F2xx device revision | Rev B and Y% W: m7 L6 x: `# q: W" ~
- *-----------------------------------------------------------------------------; `3 ^ g/ z# M* o" G& H
- * System Clock source | PLL (HSE)3 S/ m7 n) j# }5 ?
- *-----------------------------------------------------------------------------
: [* j3 A3 {$ k - * SYSCLK(Hz) | 120000000
" @; y4 k3 b8 K3 s: E! R4 S) I3 U9 m - *-----------------------------------------------------------------------------
7 E! w, u/ E2 B! X R+ S( I4 L2 w - * HCLK(Hz) | 120000000% w; k3 ^) L' ` ~1 J J9 ]: s
- *-----------------------------------------------------------------------------' W' J/ j0 H" K! Y
- * AHB Prescaler | 14 I4 u6 T5 p2 G) v
- *-----------------------------------------------------------------------------
8 s$ X s# n2 `2 }! G - * APB1 Prescaler | 4! O, e2 D8 Q5 c6 i6 V
- *-----------------------------------------------------------------------------/ p$ L+ D# _8 i" o4 w' J! Q. M
- * APB2 Prescaler | 2
" c/ r; G% ?% V* f" {/ G$ O - *-----------------------------------------------------------------------------7 G' e4 s; [# |3 V8 U% _
- * HSE Frequency(Hz) | 25000000
, Q, |! [1 W8 d - *-----------------------------------------------------------------------------' Y5 h! Q8 N) f' `0 q# l
- * PLL_M | 25/ K; n4 F& N' O( _' m: x
- *-----------------------------------------------------------------------------$ Z- \' ^$ z. D5 Y) a9 m* c
- * PLL_N | 2409 E0 b7 |2 R6 B$ u$ ?
- *-----------------------------------------------------------------------------
, D: T( z1 _( P8 s - * PLL_P | 2# ?6 j/ e3 Z# O
- *-----------------------------------------------------------------------------1 {5 a+ @8 _' P* ], b
- * PLL_Q | 5( u; T0 G& A+ v5 O; a0 t
- *-----------------------------------------------------------------------------
: b1 U! H' L0 K: G+ d - * PLLI2S_N | NA/ f2 B2 u. ?2 u7 i, Z' ?, R
- *-----------------------------------------------------------------------------
; d3 m, j, T3 F0 o/ C - * PLLI2S_R | NA: Q5 N" u; B u
- *-----------------------------------------------------------------------------& E- j6 H" f( ?3 D
- * I2S input clock | NA/ @2 T* A; e, B2 M. d! \* o+ T
- *-----------------------------------------------------------------------------% E, L1 m- K) r' q+ O
- * VDD(V) | 3.3- D; U" R. q8 A. C# q
- *-----------------------------------------------------------------------------0 g) M! i8 p5 Q- ]
- * Flash Latency(WS) | 3
, s9 m: Q# e1 U3 L- P/ w - *-----------------------------------------------------------------------------8 u, h k( N& H
- * Prefetch Buffer | ON
& W, W0 v7 l6 T8 _ - *-----------------------------------------------------------------------------5 b6 _' I3 g, T/ k
- * Instruction cache | ON: N) s7 G! i0 N s4 C. T% l D
- *------------------------------------------------------------------------------ j* d0 ?$ H( p2 w! e0 V" f
- * Data cache | ON
3 f% [; d/ @. T - *-----------------------------------------------------------------------------1 b( D8 }8 t1 \
- * Require 48MHz for USB OTG FS, | Enabled
i# _! }4 d0 L* q8 U - * SDIO and RNG clock |" ]6 F* n, n, W- B) n# Z3 O
- *-----------------------------------------------------------------------------
/ E" Q2 T' ^. G; K% ~ J# R: A - *=============================================================================
' ~" k- ~+ g @9 _0 d2 P - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
$ `9 @- T" _% r. }7 |! a+ m t2 E- uint32_t SystemCoreClock = 120000000;
复制代码
k1 ^; B0 t: l- `% F时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
: y' A/ R$ e5 m6 A
5 e7 `1 o( M! c8 D1 g8 Z6 y- IMPORT __main
1 A' N) E3 g& \ - LDR R0, =SystemInit! W. F% s+ P, \- ?5 Q
- BLX R0
2 D, p: g- Q& B, T1 c6 w/ Z/ t - LDR R0, =__main& n$ m" J9 y+ o2 b, l4 S, k9 _
- BX R05 ?0 N, p* ~- m3 k
- ENDP
复制代码 ) O0 Y7 S( A4 a' @) o
在这里说明一下文档版本的问题:
9 N, d( E/ X! {2 `) z9 q
2 y8 o' r5 U+ X. f! _3 I8 y
* r/ t' Y: `5 G
' c: o( |* x+ u& \. ~$ g+ w: Q▲STM32F20X_User_manual的V7版和V8版对比图1 q0 k% c, T9 f. W. m/ h5 w
5 P9 @$ }' p' a6 _% e* o上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
8 d: `* L5 f- M( k; t, S2 x1 Z h5 A2 [7 W9 ~1 T0 r5 C! W- r
下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。
5 c e+ r: |4 J1 _0 n5 j, Z, l! \& }% c4 ?6 S: J! n
7 w9 x& u( M/ u4 ~. j6 ?
" Z; w3 p @2 `3 X; ~9 U我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:' R T! @* W5 [# \0 h, c2 A
" G# X) `4 V4 O& b3 T/ ~
1、使能HSE
2 f/ `9 |( y8 q
' p7 S' E0 h: g* ^# c2、选择HSE作为主PLL的输入时钟7 G+ m# y# ]2 L M, g* T
. y9 z/ e0 z/ K9 [% W& B3、主PLL倍频后得到120MHZ时钟
$ D+ s, I4 I6 g+ W) ?' a2 r
! N0 J- C) c$ I4、系统时钟选择主PLL时钟输出作为系统时钟, A$ [5 `& f. X; J
- W5 d3 J' y0 \8 b我们找到对应的代码
/ m/ D+ { o# t, g$ C/ Q' k- B# P; n6 b8 C7 B1 |
1、使能HSE) p/ ?5 `* y) I M: |3 R. w
- /* Enable HSE */
^+ }5 ^0 |% K- H - RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 & W; T) O/ u3 v, a% O
在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位" b& h; T. R f
. q* g: W1 C, V7 c6 x+ f" S. L# V8 C0 v2 m( ?- b1 O7 U
2、选择HSE作为主PLL的输入时钟
6 Z% H& X0 `, J$ h- /* Configure the main PLL */
- c$ v9 x2 I1 D6 g, Z0 Z+ P - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |. M p; S9 q) N$ e' h
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 5 L3 p1 W( C7 |! g" C) q4 h" I7 |9 h
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟
& C- l, H$ I3 E! M) s4 _ I( k! H- l b3 J
3、主PLL倍频后得到120MHZ时钟 q/ T5 M6 h+ k+ X8 z
- /* Configure the main PLL */
. R% C6 K, X" N0 Z) B* o - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |: w% |$ D7 H# G2 Z/ G5 m, w8 B( ^
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
: L( U4 N! \8 `2 Q# e1 i4、配置主PLL作为系统时钟的输入时钟& [+ e [9 R T' v3 U' ~5 w6 a
- /* Select the main PLL as system clock source */& `1 z/ V% [$ W% L2 J. H
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));9 G$ ^* l% |( }: h, t
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码
5 ~+ ~7 v- |, Y" f对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明
8 j! q$ C+ g5 ~1 e! M
2 C5 |% P' |; X; `" t+ r
* s; ?$ e e4 O) V9 U2 z
% e$ r; P, d3 w9 t" v) g整理后得知f(out)=f(in)* N / M / P5 ~$ T3 W- B3 ]# Z& Z% |
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
( U. Q- C8 S# i) }& g& {# o7 E - #define PLL_M 25( r( G) l a7 F8 _, z1 X8 K
- #define PLL_N 240
; o2 }$ y4 O h0 O [8 f: C5 c - /* SYSCLK = PLL_VCO / PLL_P */& A) }1 F/ d( U$ s- P
- #define PLL_P 2
复制代码 " a7 ?0 H5 g0 g) E g, p( d
这样就获得了120M时钟+ `3 B' U9 y7 Q$ u
D0 j0 T) D) F$ E注意:
! C) k% O0 d& B- f! D2 s+ R. Z% I
# d% i! w* ^& j3 `
. V' X1 i6 k- w9 I1 e
$ i! ]7 G! n; A5 |; z1 k# DPLL_M大于等于2且小于等于63! r/ L/ ]& ^6 X; }/ [) W
/ @4 \8 B/ W' b1 J& H
9 H% q4 u- ]" U( E$ d& q$ q/ F
& g$ I, {7 Q5 N/ ]/ K3 ePLL_N大于等于64且小于等于4324 x! I5 h3 U$ |+ P9 x
7 q1 c K, _' T. `7 m1 }2 v. Z
/ C& C5 i7 E8 J
: Y- u) \, H2 T5 h- LPLL_P只能是2、4、6、或8/ N" p+ U u! E% G, z; v
5 Y2 p' G2 Z0 L& t9 R+ P- z O但2对应0,4对应1,6对应2,8对应3。4 f( ]( V6 W0 \ [1 j1 i2 q
, w4 d0 A, ]# V& D
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。: H/ ?9 a4 n8 h7 Q5 H9 p0 e1 j
( z% T; o; C' p9 O" A$ A) C2 `( u2 P
(((PLL_P >> 1) -1) << 16)$ R% y5 |: c/ |5 E" q) m6 X* f* |
其他外设的时钟配置时1 X, d2 B: A0 J' N% g: b x) T
- /* HCLK = SYSCLK / 1*/6 q! u# e* H. y" h
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
( s$ ]0 I5 V6 X8 f# w7 O6 J8 K7 H - /* PCLK2 = HCLK / 2*/. t/ h, F) K( D1 Q/ s' ~# Q
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;! n* N3 ^4 e) V3 U2 P$ ]
- /* PCLK1 = HCLK / 4*/0 w/ a# F3 S) ~3 w+ a0 s) u& g. T
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
! B- f- V# v- O5 ^) J) s* v" f03、备 注 * @, h) l1 U* S, N
时钟中断8 T8 e7 M; e. {
7 C9 S5 d+ ?# `+ }) ~; X
) J8 Y" _7 ~0 N
8 @0 `4 J/ }; l7 b5 f- j- w8 y b可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
' c/ S; U# i: N: o$ b$ `( K7 j* g7 y1 F! M
无源晶振不起振
6 V5 h. N0 e4 ~8 ?9 E
2 q; s8 c9 w @5 E2 i没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。
- W, n/ J! p3 s% d! {# x: N; n% C+ y' ^- H! I
关于APB和PCLK
% T) J3 U) [- F6 ^; K, ?; L. U/ W5 d( E8 ]
F207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2
1 J# a, f" h/ E/ T8 H2 g# Q3 K/ W6 F- P. z
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册
) K/ F# r9 [# L: D, L
' ~/ I3 a( D9 U3 s4 @. z+ {$ Z/ e& p0 `2 Z( o7 ~& O1 M( G
: P* l, v" x1 ^8 i# C" _5 C6 I1 J
- z0 Y7 N+ F4 l) f) \( |
8 `7 M7 @0 T6 L u9 |/ u
/ Z7 V$ _7 i) n% _ @! K; _0 a& ]1 j
2 P- D* p4 ~; y3 g0 K, z/ }- v" I2 @! Z
|