板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。
, w( w! G* q) [
) e2 b0 F7 H5 f7 m) s6 [01、时钟系统介绍: `9 @2 P& I+ T3 _& j) L4 u1 u
5 G P+ H3 p9 j/ o& p A* d1 K9 Q
▲时钟系统专业名词缩写* m7 D, n" o4 f: f8 M
# _9 i) C8 ?0 p1 C1 m# j时钟系统关键组成部分
) N: }1 O; m+ r/ z3 \
: h$ R d6 t% u% F; I: j01 内部高速时钟(HSI)) g# [- j, E1 o& b4 o0 t# H$ o
5 L1 W4 o3 q5 d! C& c8 E; e5 J
HSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。
: X& J" o: M! M4 @7 i0 l X
$ S' {2 `& t5 p" o6 ~* T- o A# DHSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。- }. h, t' `/ K. H( E, K" o+ d
# i, v( d3 Y& c02 外部高速时钟(HSE)
7 y3 i; R7 I: Q% D
" t, Y& ?2 }- C% s2 K, a$ V$ i% a% W外部高速时钟信息(HSE)可以通过两个时钟源产生:
" }0 |# H( j" m, r+ a0 x6 U! `; \+ N
① 外部晶体/陶瓷谐振器# i9 _9 W H* e
: P0 V! G G* U$ v0 E* s( }
② 外部用户时钟+ q$ F1 F7 D% V* P2 Y, D* ?+ y/ D
1 Q9 @6 Q$ y) _5 F8 Y5 R% C* `( i" }9 d6 o$ y( p9 S, A9 W
2 Z0 R) p- n( q, j▲两种时钟源接入示意图) c/ t3 b1 u) N8 V! q7 x! b, J: i
) w! f$ t8 H) V( x
! b. h( [/ O) E: k03 主锁相环时钟(PLL)1 {, O8 H+ y. d L: x$ \' N
4 V4 q/ u1 Y4 ]( i6 ^STM32F2xx具有两个PLL
?3 p; c ~6 Q7 g0 y" J
% Y n8 T/ m1 z0 Y① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;
" E) d2 Q g+ W- I: E, T/ }" Z$ K H; ]
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;
0 \. ?" D& h+ {4 T9 m$ N
' l. q5 @! R9 O7 d) c. w/ v* u
0 W V$ { V2 s3 a; T" |! }& _' s) j4 t9 ?4 `: z- D
# W8 _2 a0 F1 ]: q$ u v
HSE/M*N/P得到PLL时钟% j5 W/ |) F0 U) Q
4 x. K7 [0 _$ V关于PLL锁相环说明
. e$ s; I/ t0 i; \9 V# p& c
; v" e2 t! V* w5 }! O+ r& p; f0 F3 a+ F% o. p% `+ H7 Q
2 o; D' e+ S4 D
; i7 b3 S& U) \9 a从1处输入,3处输出是1的N倍。
* c9 K% g, y, I' r
3 e( c5 L5 Y6 X- p9 g* U3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)* Y& @% m' f( F& P- h" |
4 E/ I2 c' M" p: w& O5 i, q( s h& j
* t$ j4 k9 O# e L4 Y
04 低速外部时钟(LSE) T( A8 {+ W. F$ S7 c
9 u J& b9 B5 i) y
LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
+ t* @. A P9 A6 i
# A% Z- f8 f( I% H9 W它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
$ E6 v2 s6 D: K* p" j7 I3 R
/ D& r9 X. `5 |# l$ Z8 s7 O, G9 m# N, o7 t& M* W1 c, W) w
05低速内部时钟(LSI)$ G- O+ S& {. O c- M& J7 P* b
& u6 c. M4 j! Z3 N
LSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
* E6 d$ S' {/ S9 M* `
- [5 ]' I/ v- @) a5 H. c8 L: L02、代码分析$ V) t( W7 b& U' T
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。
- Z. H' }6 R7 Z
8 p" x, [2 p& t& p8 e0 r: s- =============================================================================% a& n$ g4 H) v0 i1 W- E% @$ v; H
- *=============================================================================. k9 h2 o" @0 m- L
- * Supported STM32F2xx device revision | Rev B and Y
& H- m+ Z; N2 Y& x8 Z" ?5 ^0 K - *-----------------------------------------------------------------------------
9 I% f% u4 \- d) g- s, X/ n$ i - * System Clock source | PLL (HSE)1 \' F5 e& U/ j
- *-----------------------------------------------------------------------------
! `) U( l- z/ T% z! ~( | - * SYSCLK(Hz) | 120000000/ N" ?, E# M" P0 j+ Q1 h
- *-----------------------------------------------------------------------------
+ L6 l* i5 A2 R# ] U: _ - * HCLK(Hz) | 120000000
1 S2 X1 t7 p: }; z* c6 U6 \ - *-----------------------------------------------------------------------------
: d; h5 J9 R! F2 A0 N - * AHB Prescaler | 1
4 q+ O# p5 t: U$ r3 l - *-----------------------------------------------------------------------------# [# N1 P. b+ e# o/ Y1 B8 v
- * APB1 Prescaler | 4
1 A0 _; y# x) x( S% P - *----------------------------------------------------------------------------- s8 w0 v) y3 B+ F
- * APB2 Prescaler | 21 i2 N/ F5 K. r K# n' w
- *-----------------------------------------------------------------------------
$ u, s' _2 i' n: I& ~! o! D - * HSE Frequency(Hz) | 25000000. {. j+ Y! o/ f2 X% x
- *-----------------------------------------------------------------------------
- {* K& |+ Z. ~( Y! S+ C' b - * PLL_M | 254 U5 p. m9 Q. a) O& \% G2 l
- *-----------------------------------------------------------------------------
. q) c; F+ P( F& S2 k - * PLL_N | 240: w/ x+ R8 G. h! b
- *-----------------------------------------------------------------------------! r% n Y1 N$ b }- e
- * PLL_P | 2
9 m+ K6 s9 @* }4 e0 J0 a - *-----------------------------------------------------------------------------
/ p S& v; y5 u - * PLL_Q | 54 D4 N) x: K8 \: t: B
- *-----------------------------------------------------------------------------# @ c- S6 Y7 K" n- l
- * PLLI2S_N | NA! p4 q" E) j% Y
- *-----------------------------------------------------------------------------
6 G9 b' e" b: k K0 y8 {' N - * PLLI2S_R | NA; P* c, ~5 u) U- r; s3 p8 l
- *-----------------------------------------------------------------------------/ z8 q4 v6 v, F0 K+ N
- * I2S input clock | NA
7 n+ M# F7 B/ d. e) Z/ p3 W" D - *-----------------------------------------------------------------------------
/ k5 E: B! x3 V9 C - * VDD(V) | 3.3- r7 P# k( ^9 Z' f% G. X9 f/ U( j5 E% L( Z
- *-----------------------------------------------------------------------------! {/ c' j# C$ |: g! V
- * Flash Latency(WS) | 3
/ u# C" t* s* Z; r6 H+ v- G( J) v - *-----------------------------------------------------------------------------" v3 i# z3 ]1 U
- * Prefetch Buffer | ON
( |' l$ W7 y4 R* c - *-----------------------------------------------------------------------------' v, @9 g, w: Y8 f8 r, p4 |
- * Instruction cache | ON
# E/ F9 \# _+ V. _% S/ y" R' C* l - *-----------------------------------------------------------------------------# ~& `! w N& X* B4 C# q5 G/ v
- * Data cache | ON
: _! d6 t" q8 p6 S8 I# Q - *-----------------------------------------------------------------------------
) ~) Q1 G" z+ W; u$ y& D' w - * Require 48MHz for USB OTG FS, | Enabled1 U" [* T1 Z! t: L+ a: h# k
- * SDIO and RNG clock |
! T5 I/ m, ?+ I# S4 x& |9 N. {& u - *-----------------------------------------------------------------------------
/ q) Z; ^# H; L* ]; B - *=============================================================================+ M, L) v8 v4 s: C) f0 ^- a+ H( k3 d, A
- ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。$ o1 d) |8 V3 r' B
- uint32_t SystemCoreClock = 120000000;
复制代码 6 ] m2 L8 L4 D2 L* F- h I
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。& H/ ^$ K0 u" r7 k |
4 {% J* R; B2 q% \7 O- IMPORT __main) u3 P4 O' _* p; x$ Z
- LDR R0, =SystemInit
. |2 {2 w7 X" U# p' C0 E8 k( j - BLX R0& |9 ^9 y1 S, K' e
- LDR R0, =__main
/ V+ |4 {/ n x0 W7 u# i - BX R0$ \% D" w; ?# _" ~: K$ q* p
- ENDP
复制代码 ( m5 K; _ G$ E$ w y5 W } g
在这里说明一下文档版本的问题:
, i# ~* g: n4 R' A3 o6 k5 s/ v4 Z+ B* V9 A3 o k& S9 E( Q
2 ~5 b0 a# K* n
! [- d! A1 H! C8 `$ v▲STM32F20X_User_manual的V7版和V8版对比图/ e/ e8 k7 [( s1 p6 a/ L
/ d9 _6 L. z4 C) M3 B
上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
* ?4 b8 Q: e- [3 e8 H- z
- Y9 H) C: J% m2 ]( G: A下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。! z$ \8 W8 S& u" N% s c+ o8 n
1 d4 q* _7 c+ S8 Q5 m# y4 j: k0 @
0 y2 o4 F0 d3 I5 `9 ~9 A1 X
/ i7 E# P# ]/ B+ N( \9 |4 p6 ^我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:5 a" t9 g" G0 ?8 G
@- F8 y9 q8 E+ V# O7 @" X
1、使能HSE
h6 Y( }" O; }5 w# A% }
* _% c$ v, t) L% q2、选择HSE作为主PLL的输入时钟5 z! u! y( A5 T2 I. W4 {
- R- ?! U! k, h) |" N9 i
3、主PLL倍频后得到120MHZ时钟
0 n+ v7 i+ h/ i9 ^) }+ x1 t
* ~9 }- R0 i0 ^; S9 N4、系统时钟选择主PLL时钟输出作为系统时钟
* M$ c6 a/ D7 x) x1 m8 [& o- D/ b, {# ?) ?7 K
我们找到对应的代码$ h- Y$ w4 {) y9 e
3 l) f+ A9 K: s& Y! J- O 1、使能HSE' k0 F9 K6 |5 W/ G7 L4 Z" b/ `
- /* Enable HSE */
8 d% d6 b) E; V$ X' ~ - RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 3 |' x' u- F+ b. z4 u
在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
. R8 e$ d+ ^: ]: X
7 C1 L; u3 n1 e0 R5 D+ {0 `" _- s* S0 \; i, J5 V* T( |
2、选择HSE作为主PLL的输入时钟- S, m% p- y. e2 I
- /* Configure the main PLL */3 m8 m* C8 n/ C, d
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |( w j9 M7 s( F
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码 # I% E% u+ M( r6 N4 E" C$ z
RCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟9 A- p8 u2 r- J1 l3 d
+ r j8 U5 K5 W$ O 3、主PLL倍频后得到120MHZ时钟" s4 ]* v- ~% h% v7 V. y; v
- /* Configure the main PLL */
; |% x4 o& Y2 a2 v. A+ s - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
3 A1 g0 f/ B# O1 y5 p& ]/ K* a - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
( R* E4 L' D" @ i9 f/ `4、配置主PLL作为系统时钟的输入时钟7 k5 }2 X9 Q/ d
- /* Select the main PLL as system clock source */& R; ]0 r& Y' L
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
: e9 q- X+ P5 K1 S! J/ [ - RCC->CFGR |= RCC_CFGR_SW_PL
复制代码 ) o* B0 \$ n7 v: _* O& `
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明7 O2 f( E2 @: _4 ~# ^! K$ F
! K* M6 H7 p, W0 l
% ~& ?9 R8 T! R% a% n' _6 `9 U4 e' }1 B. q4 |. t& A
整理后得知f(out)=f(in)* N / M / P* K- F- G) u" K: t( _
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ U, [( B( |0 N
- #define PLL_M 258 e$ u: w% P1 j% N9 d$ e1 L
- #define PLL_N 240" x% h+ i% }0 Q0 X5 L
- /* SYSCLK = PLL_VCO / PLL_P */% h) w' p+ F- v( Z1 B
- #define PLL_P 2
复制代码
5 {% v/ [' N5 W. \1 K8 F这样就获得了120M时钟
! Q K8 m; X \4 R( J2 c6 D4 u) [% F
注意:5 Z& _& g I4 f0 S
7 ?0 B5 C7 r% n C. x( r+ F& ?' {% c# B5 `! K2 Q
# B6 D x* I( F6 b% ?8 |PLL_M大于等于2且小于等于631 s6 i$ e W2 {6 p
. _' ~. l1 D& o$ V4 A6 H
) O# h) V2 o2 B4 Z: Y* F# a( R d! f6 A$ M' ^' U' f, {
PLL_N大于等于64且小于等于432
# ~6 M" g9 U0 U# b/ ~$ _& \1 j+ A( h: F9 F. D
! K t8 u/ m7 m) b6 [; x. c# u' C) f/ k" m2 E" O4 E
PLL_P只能是2、4、6、或89 i P* Y' M2 Y' {3 }$ V
3 D7 e8 o8 m6 i/ M+ w t但2对应0,4对应1,6对应2,8对应3。
* T+ p: u! T R# W% X0 M+ N9 Q$ ]( W7 y
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。1 k9 ^& X2 @2 [1 r8 r
6 `0 Y/ \9 E$ V: a# d(((PLL_P >> 1) -1) << 16)$ A1 J6 v4 H4 j% [
其他外设的时钟配置时
! \! J. Z3 Z( y" a& |) X7 O* M- /* HCLK = SYSCLK / 1*/
2 N7 ~7 @+ @) m" {4 z9 ? - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;& W: [9 n" H6 Q: t7 |
- /* PCLK2 = HCLK / 2*/
5 x! Y5 g0 F2 { - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;; }& ?7 _6 i2 T) C% H5 n5 O
- /* PCLK1 = HCLK / 4*/
8 i. q5 }1 d' j# F - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
4 o: x7 G* y0 l; h03、备 注
2 } x( r# y7 T时钟中断
% g' ^5 I3 b! P1 P; q: [
3 s$ r# M* [* [# y$ Y0 l5 Y
% e: k+ W6 u* n- T, Y# e3 r) G" v7 N f5 L0 o- X A
可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。4 I X0 d1 @$ i8 \% [2 e
, c8 k' G, {4 t. L6 v
无源晶振不起振0 g4 C6 g( P; S5 l3 O! E
5 ^3 h0 T, m5 L: q- H; p没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。
( a2 h& m: F3 a4 {
: t: Y! k& t7 M f 关于APB和PCLK
- c0 j' n1 D2 T) R5 W
. o" Y; `1 j0 }F207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2
1 C1 n+ V! [. S$ `1 L( v- D) T" x4 ?! n2 I; w( n
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册: R3 x" O9 Z* K$ v) f
6 l! S) {, X' R6 m0 M, k O n
9 p) K) z5 l7 x% d" V7 g) W$ D f$ w v4 T5 w2 H& Z# e4 ~
' I/ k3 t3 G0 `7 S# ~8 V- l( `5 U: l8 v g$ x: |% ^
8 y6 n. W6 W: Z' L3 V6 q+ e; n' }* c! k7 k0 s) X9 u+ n; n
/ ]( ?, ^- N" |! q0 _6 h5 u: d/ z5 y5 k
|