下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
0 e/ B$ [/ R! q; c+ _1 A5 N4 o 8 C! Q, @& w, Y# v5 Q
01 时钟系统介绍0 K1 t* v* t* H6 w5 ^. u
. d+ B, Q% X8 N2 }$ a. k
▲时钟系统专业名词缩写 & u p% _3 Q( J. K9 E% k: W
时钟系统关键组成部分9 ~+ P4 r$ i& G5 w
! W5 I0 M0 K. K
7 U, \6 ]+ \4 {, \& ?01 内部高速时钟(HSI)
: `" D" \+ e" w: F, N& sHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。 ) x- y' x6 m/ z! x4 @8 A: A
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。 5 r# k, G) W* F
! g$ z1 a: F% x# m2 o/ I3 C9 K- w02 外部高速时钟(HSE)+ l0 @& v) |0 f$ P% i9 p7 ^
& n) F' V& I. b% j2 U4 H/ P: f6 B
外部高速时钟信息(HSE)可以通过两个时钟源产生: & ^2 J4 z7 x; T! }& Q6 \/ J
① 外部晶体/陶瓷谐振器 % v2 J* e; p8 D0 O4 y
② 外部用户时钟
) X6 ?( W% [& j$ s▲两种时钟源接入示意图
* R5 v, C& r, W3 w# u03 主锁相环时钟(PLL), p2 p3 u; B$ } L( N2 J
1 i6 @2 V6 @+ rSTM32F2xx具有两个PLL , p0 p2 o- b% `1 v; S
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;
: @) a6 `: j( Q0 Q0 n
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口; ) y8 M: v1 l- j# c0 h% h) H
4 ?/ [6 K7 B6 {, x8 E! V+ i: t
HSE/M*N/P得到PLL时钟 关于PLL锁相环说明 7 X7 c8 f3 ~- b( k
9 R* T: ], N' d从1处输入,3处输出是1的N倍。 3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的) 04 低速外部时钟(LSE)
! y9 S7 c: |9 a
- M& D+ H) N6 A5 ]% E% wLSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
+ q" z- T1 h/ W; m: Z* R0 H
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。 , J* n5 p" p+ ^' q( u8 o' T
05 低速内部时钟(LSI)
# t W/ ]% ]5 q' K" M
9 n& C$ i- n2 `: `8 _+ uLSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
$ d% [/ @6 f3 w5 I; k02 代码分析5 p" M: X/ ]: c( U" ?" L" X2 g/ V
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。
( R x1 a0 l0 X5 _8 J! |8 M: T% Z! v2 r) m5 t& \% L1 d6 _
; g6 I: v% d ^. F4 [) b
& p* D8 f- u, S% i! t+ g- =============================================================================
; f! N2 n( {4 V* z/ @ - *=============================================================================
. C. l0 f8 ?9 Z% C5 R6 e1 l - * Supported STM32F2xx device revision | Rev B and Y4 o+ b7 e$ j. T' z. E. H* u
- *-----------------------------------------------------------------------------, `% @; a; r; Y ^8 y
- * System Clock source | PLL (HSE)
* i4 D: ]# l+ V9 I2 [$ ` - *------------------------------------------------------------------------------ p: N: J! G2 d& e1 D$ H( H% z
- * SYSCLK(Hz) | 1200000005 @5 ?- g8 P6 n8 V0 N
- *-----------------------------------------------------------------------------
/ S" ]- c" Q, {$ Y9 v2 ^: r - * HCLK(Hz) | 1200000003 j! }+ \6 w9 Q! e8 ^! V, w
- *------------------------------------------------------------------------------ X: R" w. M% ~' b5 H8 }+ P
- * AHB Prescaler | 17 @! l# S( O4 [# M, ~9 a* e
- *-----------------------------------------------------------------------------
1 z" J& j; g* j c/ [" ` ?4 x. P5 O - * APB1 Prescaler | 4
6 `0 V: X8 K7 i8 q - *-----------------------------------------------------------------------------% @& O3 w3 ?: b) [9 M) E
- * APB2 Prescaler | 2
]3 O1 H( Q- y - *-----------------------------------------------------------------------------
h* e( R @1 ]% ]7 x/ h - * HSE Frequency(Hz) | 25000000) }! z$ x* U) b, I
- *-----------------------------------------------------------------------------
% Z3 C6 t0 |# d' A9 D - * PLL_M | 25
& D; i. c+ O2 J! u4 k - *-----------------------------------------------------------------------------% ?3 S" B& E1 j, f& g
- * PLL_N | 240, H# Z+ H' U5 d, T7 E
- *-----------------------------------------------------------------------------" F; H1 o( b* \( Q6 ~- Z
- * PLL_P | 2! x- Z1 O1 K5 R5 K5 E
- *-----------------------------------------------------------------------------
4 l4 A! B4 R/ B2 d - * PLL_Q | 5! t8 Z8 P: l# U) y5 f
- *-----------------------------------------------------------------------------
) p; R, ], B) U! E+ Y - * PLLI2S_N | NA* Y! |; {' X p' T$ m
- *-----------------------------------------------------------------------------0 z6 l ]5 ^" C$ D% i) B
- * PLLI2S_R | NA
- ?" ?: l# h! v# U* P8 @ - *-----------------------------------------------------------------------------( m: f3 I6 U/ O9 D2 ^# n
- * I2S input clock | NA
: x& O# T& O1 ]# @ - *-----------------------------------------------------------------------------
* D: v( }5 P2 E0 T/ }+ N5 z6 y$ e - * VDD(V) | 3.3
2 G5 Z6 Z. s( q7 ?" H/ L7 F1 T - *-----------------------------------------------------------------------------
9 v$ D" g( B5 Q& ^) R! l - * Flash Latency(WS) | 3! M* w8 }1 b% T$ X7 X3 ]" Z
- *-----------------------------------------------------------------------------$ E4 s5 V1 q- o
- * Prefetch Buffer | ON
5 f' d1 b2 y/ A6 j - *-----------------------------------------------------------------------------
* g# A1 |/ l$ P T/ a! O+ b - * Instruction cache | ON1 W: ~. p7 d4 s
- *-----------------------------------------------------------------------------4 F! R) B) Y0 A; h0 n
- * Data cache | ON) @& l7 D6 p, C% |4 e S& y* o8 j
- *-----------------------------------------------------------------------------; p6 h! S2 o$ m5 U
- * Require 48MHz for USB OTG FS, | Enabled* I( C; p" e- }! O- j$ R2 @/ n0 W
- * SDIO and RNG clock |6 x- i3 p) M# K& `% j S, X
- *-----------------------------------------------------------------------------
4 _2 R) ]' s; K - *=============================================================================5 B/ |8 }! t- L: h9 M
- ******************************************************************************
复制代码
( [( n3 E/ S, f' L7 o# I/ r5 |- j2 F4 u8 e/ u
, F1 Q& q6 p# w: Y
0 z) Y8 i0 ~ K I9 ^( R" H% p7 \
- z, t) S& j! p7 B在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。 / q7 ?" t( m- W7 G; V: n9 `6 ~
+ T0 |2 N9 e, c2 O8 N
- ) Q8 S8 ~2 O7 [
- uint32_t SystemCoreClock = 120000000;
8 i V3 M: }- k8 u$ B* z2 K
复制代码 + t4 J; `2 E+ k2 n; C" ` j8 w6 s
) z$ i/ y. p; f8 k. x l8 v9 c, ?
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
F5 b* n! z1 k6 p! v
& ^; |0 Z; Z' ^: e- IMPORT __main
1 b. y& ?4 G) I0 K$ p2 J - LDR R0, =SystemInit
3 W( @9 Z: W& ?7 x - BLX R09 c7 `4 \4 H/ K0 [
- LDR R0, =__main
# ^4 H& O4 K# D. v( U - BX R0+ y) t' @: M) Q
- ENDP
复制代码 / [ b0 J% b, t, s3 I8 v' e
& O4 f& b9 X" K! b9 Z0 t3 h! W6 j
+ F* t# k- w4 d. ]( x4 E! L2 @- T
8 B( n) c5 C# d! j
在这里说明一下文档版本的问题:
7 B+ c" {1 _: S9 _3 x* m* ? 2 h2 _! p2 i4 m7 L( R9 j( n( i
▲STM32F20X_User_manual的V7版和V8版对比图
4 A; S: w: z* c- A上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
$ ^3 J1 R0 w! }0 I下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。# _2 _8 q L' P; L
. A4 i1 j) a$ X- T) J. `
Y9 z6 f( V( Q7 r- ]5 l
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点: 7 v, ^, t4 \+ I# O! v
1、使能HSE
, [0 z) G/ ^ D* Z
2、选择HSE作为主PLL的输入时钟
6 e- q% Y4 k( L1 @! _% ?
3、主PLL倍频后得到120MHZ时钟 % E+ ~4 W8 L) U- }" t
4、系统时钟选择主PLL时钟输出作为系统时钟
6 i- h7 S+ k3 X
我们找到对应的代码 2 K3 N; l+ ^7 |( |1 e. |
1、使能HSE# a# w K# q; m# `& ?. p! @
- /* Enable HSE */; G! a. }, l" {2 E6 z1 v
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 2 }) W+ Q, e. f& [4 \) I B$ g- V# D8 ]
" L- c# w/ ^* F4 q1 Z
" s3 @" g5 a4 E, N; `3 y& q在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位 4 F* x" i- j& s7 d) n
; d3 m c I, b4 O8 L. A* C) g9 R2 b, B( |, o* l1 k
2、选择HSE作为主PLL的输入时钟
j2 }$ a# ? c+ o
1 N6 u2 ~& w/ {/ R+ \& Z * U7 I9 G5 h$ q. S8 u) u
3 z) j1 @2 u B/ g0 b- /* Configure the main PLL */
/ m/ b, r, ^: F - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
[+ u0 ?! X$ W2 n, U4 a - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
' \; \& C' U, v$ e& C4 R& } ' l2 w5 S! e& v- W- }. O4 Q8 M8 g
8 u- H6 j6 {5 y) ?- X4 y
K5 s0 c7 F* q6 Y- s) u5 ]9 `" N! x
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟
% [ {+ x6 o0 c& ` 3、主PLL倍频后得到120MHZ时钟
4 v/ F6 a8 t% z3 Z5 p; |8 O
/ M: `' F# c, F; r4 |8 L" j8 L+ H; C6 i- /* Configure the main PLL */
) o8 O) o |3 F" y8 m+ | - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
% q2 K5 J3 D; R* |, d! y1 H1 R - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 / W( K. }7 U' w$ J4 l9 {1 A% Y+ H# J
U$ _8 A$ G4 s1 b6 I( ~: {8 N$ a: x1 |2 t. B
- /* Select the main PLL as system clock source */
: e) z. }; Q% O/ F( B; g - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));6 U$ T7 w: S% } @
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码
: _: ]$ y% I4 Q2 q 9 p6 W c7 w$ B& Y- r
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明 % _8 W* q' \ t
% C4 p( p7 Q3 v
整理后得知f(out)=f(in)* N / M / P 3 p) A: I. p# h- c2 u0 h: c
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
) g" [3 `/ u2 a. T8 c* E/ Q) m$ o - #define PLL_M 257 P4 f5 K" Z/ ?1 @! T+ x
- #define PLL_N 240
& j9 D' [' O" j: z* U- }5 F
( J+ b! ^# J e6 T1 Z, F- /* SYSCLK = PLL_VCO / PLL_P */# w% O; j" i6 M9 v
- #define PLL_P 2
复制代码 5 z5 u1 M& W6 i' [
$ c7 z q5 q! r$ y) Q
) J5 `8 w! @' r+ w+ w# n3 A
& o2 ^& m$ H3 ~1 Z2 B
0 E! L0 l) l$ z& J! j, ?$ \2 ?9 i
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。 - (((PLL_P >> 1) -1) << 16)
复制代码 3 S$ C6 W; z% O1 |9 V$ q7 c
4 ?3 b% V; ?: [% P; h% o7 c0 O2 C. M- . ]% ^/ y) t. D# E; C
- /* HCLK = SYSCLK / 1*/( f& ~6 g% G/ ^/ i" z
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;) y! d! r, B5 {8 h0 _
8 N. b% R) }; ]6 r; p- /* PCLK2 = HCLK / 2*/7 `1 `, w( E0 E, w' Z, N' z; w3 I
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;5 p7 U- T; r; T" { x
' }- M! r% L j9 E2 b& y- /* PCLK1 = HCLK / 4*/( `3 u4 i k9 _5 h4 r- u: |4 A0 X
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码 , T/ ]7 c' w! Q/ A/ m$ O' k3 ^
3 A/ S2 }2 a5 O; o
- ^/ Z9 e1 `; b
5 H4 F8 S# o o$ L% C W
备 注
7 P, m4 x' _2 d0 f& T9 M' R/ z
! c$ K6 j. E5 p. \9 A4 @) J, }2 x- P& T5 k5 N; I
8 G. z* Q7 e0 C) g7 i
' T, u/ l. w" f% d1 S8 D
4 W( Y$ P7 y6 J可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
( ~1 T' H" M5 M8 L5 V! P v! W0 d
8 X3 C# m: O- D. G# _2 ^没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《[color=var(--weui-LINK)]晶振原理解析》。
, t2 Z1 n6 _2 s' \8 a
5 i' g, E- Q3 o8 Z7 ?- b
& w* B8 {1 q' g I7 ? ^* u! rF207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2 应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册 , A7 E1 C& O1 e+ X/ m3 k8 e
# r5 J! G/ t5 M$ f4 U' N# o- D$ [4 N8 u- w2 N
9 r, i6 n z7 O" n/ f) B
/ q/ @' h* o! i" @& M( @
|