板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
) p" H$ d+ D+ H% X& B0 T' d5 a
0 [8 U7 m& |5 g0 ?; e01、时钟系统介绍
; K8 h j5 k/ k6 v, X2 P; C4 E' @: U- w0 }% A Z& g1 n
' ~; T& M$ F% R( a: z
▲时钟系统专业名词缩写
9 M5 v% h L8 J* v- P6 o- l! g3 Q9 P0 P+ w
时钟系统关键组成部分9 y4 p7 A! j1 A/ r! ]
0 g. q/ C2 R: D; U- u: I: L01 内部高速时钟(HSI)
; S2 o( L. C) l- z* w' E
5 k* r2 S4 m2 l3 r; hHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。- R9 Y; y4 q1 c9 l
( V& Q! b$ b u, p6 g; yHSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。+ A( t/ g2 \' ~5 O' ]; ]/ Q
1 h: B( N5 F! D$ m5 }5 g
02 外部高速时钟(HSE)
* e# e2 c8 u; [5 ]" s! b) Y0 O
9 p) R! |7 A9 L/ y! B" k外部高速时钟信息(HSE)可以通过两个时钟源产生: I( {# `# w) v1 e, {: A
$ K6 b& G& h3 b9 I0 n/ V① 外部晶体/陶瓷谐振器
( o8 B, o- `! Y7 W: U7 N5 `% h, Z$ t
$ r; }% }7 [8 i- K6 w② 外部用户时钟
+ @. ]* I# @. B5 a/ F0 |' p, U4 F6 c. J, F N- o
- ]6 U/ [, B9 d2 U7 s
8 ~. _4 ]' t8 H& t7 d+ W
▲两种时钟源接入示意图
% I0 j8 M5 f" t
' ?& \. G6 l& [4 \4 Q5 o
3 v- Y. P2 l/ Q! }" ~3 ?5 ]+ z N03 主锁相环时钟(PLL); z2 L) w) i+ X
9 I8 A. A9 C d8 bSTM32F2xx具有两个PLL
& n0 Z8 ]; H [4 ]' q" R9 ]0 |0 v$ o
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;/ ^$ @, Z. Q- Q' r E$ b
3 s& x" W9 a) K- D% P
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;
8 \# Y3 M! s' L& }! C9 T/ C' G& e6 |7 f. _: ^
' y6 @6 |) Z8 J0 o0 K' v
* e# ?7 K9 o7 S0 e) [8 _7 o
3 v8 Z/ N- P/ THSE/M*N/P得到PLL时钟: ^# A( J3 R0 N% b O, y& [
) h% `' Q6 |( Q% r/ [1 M$ H( d3 }" J
关于PLL锁相环说明
% H, N$ t, i! Z
3 ]. R+ s1 p1 O7 i& D
/ D2 H' T0 n$ Q2 n1 o7 u. A: S7 B" o
* M2 |: t# }. p3 L
从1处输入,3处输出是1的N倍。
9 K+ o, l; x. ^/ g7 q7 S* f; u0 Z+ n% W( j8 q8 }. a: {( A& L( I* c
3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)4 M8 Z' m2 p6 e8 t
! A. [6 J/ i3 {! T/ _9 l8 g6 X- ]; O8 @# ?2 ?1 V1 U) ?
8 E( i s5 @2 q4 P; C04 低速外部时钟(LSE)
; U: q! c6 P% ~# @( `* H" }9 Y$ N& b3 A; c" Z& G3 D1 z
LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。3 ]6 U. Y# e: D3 t6 M- h
: h' A1 z1 H* y7 `# [6 l. v) k
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
( P |5 ^& ^4 [, s! V- D' u
0 l J# h% _8 Q
* p) N7 _* ?2 C, h4 I" z( X: f+ ~9 `05低速内部时钟(LSI)' P/ p7 i) n& \" u4 \5 u
% u- j: E/ C# b" i; DLSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
. Y) T2 G. R2 T3 ]2 D3 g. S m! w5 f
02、代码分析! w& l/ I8 m, X) l$ z) p( h# d
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。; d8 e2 h' [2 L+ u% ^: f7 W1 _5 z
! J" N; U* r$ P- =============================================================================) X5 |) W/ c. B" H
- *=============================================================================8 Z8 |& c6 V$ T
- * Supported STM32F2xx device revision | Rev B and Y
' w _4 p8 D( b4 ? - *-----------------------------------------------------------------------------; R3 ^/ y% t1 M# g; Q
- * System Clock source | PLL (HSE)7 H- D8 s( Q1 h6 R! V4 e& A
- *----------------------------------------------------------------------------- A- h0 v9 X) V) X. {$ Z2 e
- * SYSCLK(Hz) | 120000000
* K* u2 s( K1 t# ? - *-----------------------------------------------------------------------------
( b6 i, W7 J7 K% x2 Q5 f" r - * HCLK(Hz) | 120000000
3 l7 E2 M/ {% T) O9 }0 T - *-----------------------------------------------------------------------------
7 I4 i! k' | Z! S( k# P2 q - * AHB Prescaler | 1
7 D( l0 l5 y( b& R - *-----------------------------------------------------------------------------
* ^& m0 G" @! H- ^. D - * APB1 Prescaler | 4
4 S& p! ^$ q, e) J: L- y v6 o" Z1 a - *-----------------------------------------------------------------------------
/ Q7 r( ?$ o# u& Y" `/ E: ~! w - * APB2 Prescaler | 2
G) o% l$ T. V& k. S d - *-----------------------------------------------------------------------------
+ [- z+ t9 y) v - * HSE Frequency(Hz) | 25000000
g$ k: s5 Z9 Z8 S5 W0 O - *-----------------------------------------------------------------------------
n. D2 e' A2 @5 y9 ?* l - * PLL_M | 253 q' L- {4 c# C$ K% I) S
- *-----------------------------------------------------------------------------' N; b8 v6 R; S5 E" K
- * PLL_N | 2400 V% P8 x2 E( D4 b k- _
- *-----------------------------------------------------------------------------
! R8 }# m) `+ U4 B - * PLL_P | 2! F" ^; _2 E- _
- *-----------------------------------------------------------------------------( w, x) g( V% n* B1 M
- * PLL_Q | 5! Q0 J% J9 }0 m' C- t
- *-----------------------------------------------------------------------------
5 g- g6 G8 Y' `2 a5 [- m& z- P- ^ - * PLLI2S_N | NA0 a5 e* K% |0 c% C9 D$ j
- *-----------------------------------------------------------------------------
0 s% [1 q- p6 h - * PLLI2S_R | NA4 P/ m f+ W1 T6 l4 `
- *-----------------------------------------------------------------------------$ M1 z' b9 t( v' ]6 c) n" _- v* n _
- * I2S input clock | NA
' U" ^) M g" Q/ _- ]: O) J) f - *-----------------------------------------------------------------------------) d. X1 b% ~5 A" C/ t
- * VDD(V) | 3.3
3 s2 g. M. T7 L4 ^9 @ - *----------------------------------------------------------------------------- N' @* E2 a" x( C. b
- * Flash Latency(WS) | 3
5 l% P5 i# P. h6 D: _ - *-----------------------------------------------------------------------------5 |# e: Z- A4 ]/ d% p% H0 V& M
- * Prefetch Buffer | ON4 m ^! w9 v; H) m! h8 S
- *-----------------------------------------------------------------------------$ u- n/ D; K$ Y" O' g6 j
- * Instruction cache | ON u7 k$ _4 r- M
- *-----------------------------------------------------------------------------2 F* s* _0 Q" k# Y
- * Data cache | ON
. z# `, C6 \" o9 H - *-----------------------------------------------------------------------------; |7 T# g4 _- O: Q# R. E
- * Require 48MHz for USB OTG FS, | Enabled
0 V# \% t) U' @5 e6 u% t - * SDIO and RNG clock |
, ?; T& s. a0 z4 D - *-----------------------------------------------------------------------------
' i3 \' K" _* p* j4 Q) A - *=============================================================================
+ ?$ e- f2 b. g6 x6 L - ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。2 j7 C) p* g2 v( _7 K; b1 H* q" a
- uint32_t SystemCoreClock = 120000000;
复制代码 % Y1 C/ l) I/ L" \! Z
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。1 ]7 i( Z: {' H7 i
5 x- E% n( N% C0 e- IMPORT __main
, p4 T3 w0 v0 k% T) ^+ O - LDR R0, =SystemInit t" h2 B* G4 j1 u
- BLX R00 G3 v; ^5 L& Y
- LDR R0, =__main8 X* I r+ Z- m# Y8 A
- BX R0' p4 G! j/ ~* A! v
- ENDP
复制代码 3 Y6 Q8 g6 q0 b3 C
在这里说明一下文档版本的问题:. j1 I2 W, B$ m# W t0 o; Q- |
& \! R- Z8 H: N" i# Z1 ^
0 S, } f6 o6 j1 M' F; U* w+ c/ c* u. J4 A0 ]3 Z
▲STM32F20X_User_manual的V7版和V8版对比图- O I: r5 o3 {/ r5 I% S0 z3 I' Q1 L
C3 N0 i5 h1 o3 |
上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
( q. t4 b: U1 V2 K
7 o- W* q' ?) D5 E. }8 o& V下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。' N% G" y7 g9 F- x$ a! S0 D7 K
1 `7 [# g2 f. B
: z# I$ O% |9 d e; L
( }/ {6 x+ ~1 ]& M* I2 Q. ~4 G5 a$ U" ^! E7 t我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:
5 w: ?8 _1 u9 L* k4 u
1 Y$ J" b% v; Z* v* m- s1、使能HSE1 w3 A9 M! Q# c$ L- R/ c8 r* V2 y
$ z: C0 h# H, {
2、选择HSE作为主PLL的输入时钟( x$ J9 j" W7 n7 \" W+ \: x: ^
4 d1 v3 W5 Z# N) c E3、主PLL倍频后得到120MHZ时钟
! i3 F( l( u" |$ F9 u
" f3 z5 r* v& `4、系统时钟选择主PLL时钟输出作为系统时钟
3 d' ~( w; o( m2 O3 G& c3 |% n' m6 ~0 @
我们找到对应的代码
; D2 b- \$ H9 v! ^- B. D& e- L' @$ D0 i, C: f
1、使能HSE
! y) {1 K1 N' K8 K- g- /* Enable HSE */
# d% _+ d( U \* j% x! j, ? - RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码
' o9 z; @$ ~3 F& ^6 u在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
$ H- a9 v' t" T) Z8 I b' D/ u5 x% i( c
9 \8 [3 R6 a" ], v% \/ X, K
2、选择HSE作为主PLL的输入时钟& Q( S& [$ ]0 M
- /* Configure the main PLL */. K/ t! C. z( |+ d) `) L2 i
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
/ W( j. Z9 V$ \( s% Y S' H! H$ A - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 5 `% ?; b8 D5 q! ^1 b5 N
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟9 c% U8 i8 A% c) U& o
1 i4 h& V0 ~9 w+ ?: K 3、主PLL倍频后得到120MHZ时钟
* i! V |6 d* Y; _- /* Configure the main PLL */) I1 k. t0 P0 W8 Q- I
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
- L, A: {, j1 G7 |/ A - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
+ O% o6 \2 e! _6 R, Z4、配置主PLL作为系统时钟的输入时钟6 M; Q5 f% n' f
- /* Select the main PLL as system clock source */# q% T; J7 k6 q* B
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));; K7 p @: c/ s3 J1 t7 b7 |3 a
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码
* y' }( O& X$ g$ A4 a' J对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明
D; W4 j* S/ [: n5 y8 ?9 a
, f- d" A& h6 k4 K/ J) l" f; l6 k. G* t, B0 K
& _8 u8 a8 q$ M% b
整理后得知f(out)=f(in)* N / M / P3 R) z" h: G9 Y, C* I! u- W
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
! k, X: U6 O" F1 \% g. K - #define PLL_M 25
0 C) U/ B0 }6 |- Y" j' N - #define PLL_N 240
2 m8 r/ k$ S* y5 ^" ? - /* SYSCLK = PLL_VCO / PLL_P */( A) K& i4 J" [1 H# Z
- #define PLL_P 2
复制代码
% p. s+ S. M, m, {; W这样就获得了120M时钟+ t& O+ k c: d9 C! D
$ U) Y" ~: u! r* K
注意:' C4 H6 C( H) R2 n+ E5 \/ p9 n% _
' C$ d, ?! F, m9 e: S, @
7 B$ P8 u7 ^& B5 @( n& g
+ i& I# a8 X+ f2 r- J/ ~PLL_M大于等于2且小于等于63
3 e; V8 d( ~2 S; _/ }9 w" t4 M$ ?4 Z
% w' d* e4 J) f+ W$ }, _0 L$ G, Y7 V3 Q
! H% T& z) F" f* E0 p
PLL_N大于等于64且小于等于4322 w; b( t U& B' U
) G2 O4 j6 }- D( o
& d1 x. }% G' k% |: q2 r. o6 \9 V
; l' Z; i, ~, ^
PLL_P只能是2、4、6、或8) K; x' q+ c! i" k. O! _0 {
8 r- G- x7 [9 g+ J. Q但2对应0,4对应1,6对应2,8对应3。
. r$ m& p, f6 D7 ^. y4 q5 r. M, i) f- i, n, c
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。+ s T3 ?! G. P6 }; S* [4 a
9 v) X$ O1 [; k0 b. M(((PLL_P >> 1) -1) << 16)
: U, m& L% F( A) l, G, E0 G其他外设的时钟配置时. K* u. G7 C5 u4 p' w8 ]
- /* HCLK = SYSCLK / 1*/
- Y7 o A7 X8 z- h - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;( d n* h2 l5 M- n$ d
- /* PCLK2 = HCLK / 2*/
. f/ q* T+ |; f! U - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
( A/ q; _4 R; [. G e! |' S - /* PCLK1 = HCLK / 4*/. D% B: u. o$ x8 @
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
' C" K2 x; q( z7 q" R03、备 注 - b' ^$ j% I) p0 _* d: x) S+ M
时钟中断
2 s2 V/ i7 C3 \! y! Y4 n! l" J
! d! {! B- N# ]6 K0 P# z( D
+ ~ W" s; F, i
) f/ j$ c6 ]- [. A( I可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
3 d- d! i, D; a$ t
" ~: ]7 H2 ]8 k1 K! Q 无源晶振不起振 |7 G: N& I1 H) L1 r6 P& M
: v J, y B/ e% y: @/ ?没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。5 c# v; c/ L4 c9 T# f
7 ?' A% b, Q2 V5 T# \
关于APB和PCLK1 U+ ^7 X6 ?8 {& `; B t; ?! c" o
( Y- o- o) B3 V0 [9 I6 D% R
F207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2 ~+ d5 S! `& r+ @9 H( Y
1 X+ ]% v7 W- A, g! z1 E应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册* v. O' c; i9 c5 j! D; v* Q3 j
L$ N( |" K$ D0 c9 g( H0 R# _1 c1 ^3 M- `8 I# ]1 [6 x. }, ~2 P
" I# b& ~7 v- I+ }
4 O$ U. l5 E4 b. N$ E4 k' g# L' d4 ~- P* {, G3 R: B, u
7 Q$ q0 H( k* a/ W' y$ {4 J6 ?0 j& q8 w
5 r' k$ H1 i2 w6 l* u! K/ m2 l1 ^* n K( Q& |
, u i2 |+ H# I- g
|