板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。/ Z. D& k+ D) V6 E1 b
8 \* R" J% Y) R0 {2 t6 i
01、时钟系统介绍4 M+ E2 y, D7 o4 I' J5 j. L9 ^ @ R
8 o* f% D9 R4 v+ n; j, E0 Y2 B+ d6 G8 N. L% g7 H2 u+ K& ]5 n9 m
▲时钟系统专业名词缩写
4 {. N; \; V; A; x! H! Q: ]9 D% H( s- d: [/ t& {
时钟系统关键组成部分) N' P% ]3 u v6 V1 N! t
- F$ N% F s3 k" \2 n' A" N
01 内部高速时钟(HSI); S. y8 n# h4 f. \0 j8 o
2 g6 R% Q' J+ [HSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。! t, T2 ]1 Q! I" ~" Q
x( P3 ]+ s8 o. n$ v
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。8 k5 W2 H2 d! b4 w4 s
+ V5 Y% h+ ?/ m5 z4 g1 ?02 外部高速时钟(HSE)
# U v9 a3 R3 c- B$ {
, U* j" E: u, z3 g外部高速时钟信息(HSE)可以通过两个时钟源产生:
( ?( i6 T6 H. E9 Q7 w$ w
2 D, v: x4 B# O' c$ y& W① 外部晶体/陶瓷谐振器5 e4 s' v) d3 R% M/ t0 {- |7 L& l
, }, B" y$ j* r/ `" C/ J② 外部用户时钟4 B3 J0 D7 @9 x$ _- C: [
6 v5 w8 Z% `2 T$ {1 \
% h4 h# U8 Q% c W0 p6 l: G0 d( ?5 y L0 O- I) t, p% a5 b
▲两种时钟源接入示意图# l3 o ~+ L' P7 b/ s! l
+ a% G; Q% k- F$ ^
+ Y$ E5 Q8 O6 e2 m6 Y+ D/ T03 主锁相环时钟(PLL)
. b6 }- g$ r% u* p4 @/ h, M, o5 x3 d2 A0 k8 i! \
STM32F2xx具有两个PLL
* n$ `& U) [! F2 r- a5 M8 {, O% o+ t6 B: G+ T( } n; ?
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟; u7 k3 e+ ]3 Y2 P4 k
% W( [; f+ g: ~% m# [
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口;( N; ]: v6 B2 D5 _
8 a7 y4 f+ K9 k! |; _0 y2 }3 P' j% n" e: W8 r# i2 |$ M
5 b% s0 |7 o* ]6 U: h) ^! h& i
, ^7 i% i+ n- W/ yHSE/M*N/P得到PLL时钟
8 ^# b: Z9 _: C8 |; D! g
( X5 Z" A2 {. k; _$ D4 x, E关于PLL锁相环说明
) P/ D" I/ I; I& X7 ]2 a# |
U" d5 }% K( a( x7 K# A5 }) V
( ]& y3 `7 x4 \' f- J" x) K1 r# Y
4 k" f, L+ |! e) s& z/ I H从1处输入,3处输出是1的N倍。
+ f+ ]0 j5 }+ d) V2 z9 t# g& m+ Z$ t/ B/ i H4 I& I
3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的)2 t. a$ ~# Y3 t- l, \: d) ~+ [3 Z( s
; _2 y E+ ~: s* q. A& s
8 Y" a% W0 Z& s5 Q2 p
* i, l7 c; J6 G7 \' w |7 p04 低速外部时钟(LSE)
8 I% L0 g3 R" A* k/ }. f) k6 _8 \" ^6 U4 e: e7 U7 E
LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。$ |& W2 s& u* i5 P' L& d
2 _4 G0 j) s/ h7 A8 S
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
/ M& p P4 p, z! k/ K: T- g
# T) W( ^7 P* `9 n3 z1 |; `7 B& T! i( S( l4 E2 p/ T" [8 `
05低速内部时钟(LSI)
$ ^) Q& Y* d! m3 G
* e! {2 F' K, h) ~& j# u# QLSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。
- u* R Z* R( Y0 i& P! w+ o- i/ M1 S% _3 ~% Z
02、代码分析- g6 Y, m( \# u( ~! p
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。
2 `7 C. j5 K4 N# D9 n& H& |; }0 A7 r$ v' b- q
- =============================================================================' P) l' z( p$ Z# y. _5 q
- *=============================================================================
M- N+ {6 r, x) t: N' r - * Supported STM32F2xx device revision | Rev B and Y
7 y6 S! {* @' [ - *-----------------------------------------------------------------------------
/ Z! m$ A3 ?" Y7 n D& e - * System Clock source | PLL (HSE)5 E( K# ]' J% c" L& U2 T) E
- *-----------------------------------------------------------------------------/ }- B' s8 h5 X7 O. r: X6 T
- * SYSCLK(Hz) | 120000000# `: y. r& }7 |, \! i, y
- *-----------------------------------------------------------------------------! a2 _* C5 B8 D& R: s; s
- * HCLK(Hz) | 120000000/ y) M. V! O# H3 u+ c
- *-----------------------------------------------------------------------------
/ g8 e! B* f3 g8 K. | - * AHB Prescaler | 15 c, @6 G' \+ q/ Y: k6 V. D$ R
- *-----------------------------------------------------------------------------$ h0 }, W: p0 |9 `
- * APB1 Prescaler | 4
; a9 G: i) Z. r - *-----------------------------------------------------------------------------9 ~- I$ }6 j1 }' H
- * APB2 Prescaler | 2
9 M( q- }8 ?4 |& V. ~" I$ i5 ?" Q - *-----------------------------------------------------------------------------
A s# I8 Q P' W, X( ^8 u0 s - * HSE Frequency(Hz) | 25000000" c* ~+ Z+ N9 k# F$ @* R) I2 I
- *-----------------------------------------------------------------------------9 R; r1 `3 N0 f- j- L' y" `3 B
- * PLL_M | 257 D, w" ~0 b* }4 B8 @
- *-----------------------------------------------------------------------------) k/ H8 U3 v4 G% s7 C
- * PLL_N | 240
; C$ i. w. Z* l, Z7 C4 [& ]: t - *-----------------------------------------------------------------------------& C$ `; q+ ?$ j9 ?9 Z; C' M
- * PLL_P | 2# m( [- u) y2 a$ }6 I+ U
- *-----------------------------------------------------------------------------
6 b% u" f( L4 T i5 W3 G - * PLL_Q | 5
8 D) P+ s$ w8 H( N- A& f4 J - *-----------------------------------------------------------------------------
$ @ L& O9 c" p - * PLLI2S_N | NA
9 l! L' O S# A6 L+ ]* ~0 r; } - *-----------------------------------------------------------------------------
0 B3 E( V/ P6 S* n1 j; i. h - * PLLI2S_R | NA
: m$ L' L" l. f. ]9 J) w - *-----------------------------------------------------------------------------4 p# M! ]$ `' A
- * I2S input clock | NA
! A, g+ h/ E, Z. h - *-----------------------------------------------------------------------------
, k) b' N' n: i" L' q5 l2 Q. z - * VDD(V) | 3.3
0 L& G- v( q( v: C - *-----------------------------------------------------------------------------
- j4 f7 d: |$ P% o$ S - * Flash Latency(WS) | 3
, u# Q2 U8 ~3 A/ e6 { P - *-----------------------------------------------------------------------------2 k, H. ^7 \+ ^3 d& O4 T
- * Prefetch Buffer | ON' L$ `! b. b) m' X+ N
- *-----------------------------------------------------------------------------
! Z# `" V7 Y' n1 f) Y - * Instruction cache | ON! [" a9 u( T/ B+ E7 @4 t$ C- e' @4 V
- *-----------------------------------------------------------------------------: i6 C8 F" n9 ?+ l+ M& v1 j
- * Data cache | ON; D& U, o4 Z# o9 W9 i
- *-----------------------------------------------------------------------------+ x3 K7 }8 v; s4 U8 ?" p
- * Require 48MHz for USB OTG FS, | Enabled
- K0 W$ ?' G8 W( W' U, [+ Y" P - * SDIO and RNG clock |6 S" y5 o, e1 n6 D
- *-----------------------------------------------------------------------------
2 J8 b( t* G9 e. G - *=============================================================================7 M* d4 w i; O
- ******************************************************************************
复制代码 在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
1 ^ y2 N6 \* c1 x" g! A% j- uint32_t SystemCoreClock = 120000000;
复制代码 4 K6 M8 v5 H1 L m* f
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
# c; j( u. w5 Q4 I/ i' o O; `" l" S
- IMPORT __main
# [6 a5 G0 H4 M2 `8 b - LDR R0, =SystemInit
" \5 M) Y2 J" r0 ?$ G - BLX R06 ?; ~" Z% _( L! E7 @
- LDR R0, =__main+ z( Z9 R/ ^* o9 E1 W5 a5 k
- BX R0
8 X# j1 U5 k) C. u - ENDP
复制代码 / v9 U4 {1 G0 V' H6 e6 `( [
在这里说明一下文档版本的问题:- g" J0 i |/ T
2 c$ u) h3 T' R! a( `! T5 E
* }9 A; N9 p0 g2 y$ W" f$ k( W* Z1 W6 q) t
▲STM32F20X_User_manual的V7版和V8版对比图
1 O4 h5 X' \1 V) Y5 V
2 A8 {" I0 l! K$ a( Q# c上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
3 p! `: p* l% w0 n2 G$ T# s* v7 n$ ?0 a% O- q0 J; e- o
下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。/ _' o( Q$ B" |) Z; N
/ ?- J7 z( W4 g H! X3 q
8 f$ H4 X; i7 g6 J5 q+ G3 R
: |6 b, j( E' o3 S
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点:9 ~9 T& H7 g6 o7 o) [- R z4 d V
: \2 Y# T0 L7 }1、使能HSE
- R7 J) h. i3 w( c; _
) ]9 h& N2 J+ R |: V( _2、选择HSE作为主PLL的输入时钟
3 e1 q+ ^4 ~/ u' |
7 E" e, D6 k a7 ~3、主PLL倍频后得到120MHZ时钟) P7 i, t& |& Y3 S* Q" b/ ^, X+ o
& l# A% T" L' i7 O1 B
4、系统时钟选择主PLL时钟输出作为系统时钟
: f0 c1 v. y N" J9 b( h/ M8 z' o3 |( l% Z& X- p! l5 x J/ F
我们找到对应的代码
" B' y3 o- H* W8 _( I2 }& `4 K7 P7 M# m5 {
1、使能HSE
; k% P/ f; h# r% o; Y- /* Enable HSE */4 z- k. J4 `, @. s6 q3 Y( Q& ?7 N
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码
. v: k& W/ Y; m) L3 u( O0 C在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
6 \+ a* { ]. i4 ^5 [) L0 k6 n
' Z5 O$ v$ G @& Z0 Y, s2 J& J0 m
2、选择HSE作为主PLL的输入时钟
; K& G: g3 k; w- /* Configure the main PLL */; }$ A; c$ u2 e r! K4 {
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |) u. R- J5 F$ H; \0 K7 n) k; { E
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
6 h* Q* o6 z* d( V9 g* iRCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟$ R. c, ]) [/ J& G
' q' U1 N6 n7 l/ e- T! n 3、主PLL倍频后得到120MHZ时钟
0 m6 b' h" j2 y- /* Configure the main PLL */" q6 s8 p/ l! N9 k9 j- g5 C
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |4 B1 b* x0 A s- v8 ]
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
6 K' B4 n7 N$ L( B1 z) U' T4、配置主PLL作为系统时钟的输入时钟+ h+ ?9 O8 l! `$ Z* g
- /* Select the main PLL as system clock source */
' z; n/ f0 k- C j - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));9 @# O a [+ a# H8 d3 P' u
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码
5 i& k2 c1 _; j4 J3 @1 a, z* E对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明
, @( Q" H0 |/ z8 w
$ V; q" C, S, G& h' V. \
6 x8 j% i1 [/ c# m
, ?' M2 f) l# G" M& G4 N整理后得知f(out)=f(in)* N / M / P
6 z/ H8 W1 F3 R2 M; e1 E% @& s2 z( |- /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
# x, @3 |+ k; V7 R - #define PLL_M 256 i) \7 @: R9 R% z# j: G
- #define PLL_N 240
# D- b5 k$ Y/ B- M2 d - /* SYSCLK = PLL_VCO / PLL_P */ L& S$ O# L) u
- #define PLL_P 2
复制代码
' u5 s8 @) ?- f这样就获得了120M时钟
+ y- o$ v$ x2 a, F# }1 ?, w3 g
6 D$ W$ g& T* M7 h& {& U注意:% D: b7 Q- \# F
6 L+ { h* R% t% \' B7 h+ X$ ^5 a3 {$ i( n9 F1 }$ G- O
7 u$ S: m9 I; D7 G8 V/ p# X5 r
PLL_M大于等于2且小于等于63
2 X, s6 o% q$ b4 A- c) N
" `: c' ]- L& }% h) l+ O& f
- @8 M; g- b% w0 J3 i( J$ M4 j' ?5 a6 I# L+ e" E( s9 o+ {( X
PLL_N大于等于64且小于等于432$ m2 W0 L# }, \* M+ ~1 @
0 X6 Y6 K8 l( V( |
' `$ i. b% l* ?5 V) T" _1 S8 D! g) p4 O; E
PLL_P只能是2、4、6、或8. ~) s% z' }* w' G- T ^% @
: b: W4 S! I# C) Z- Z/ |5 j但2对应0,4对应1,6对应2,8对应3。
/ q* |( w- x0 Y6 W S; h/ t; i
# q4 a7 E6 h& Q H7 V) _2 Q; rST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。
; |3 \0 F8 M6 h1 N8 t% K+ N
% |" j: |6 v+ b) @2 h! L(((PLL_P >> 1) -1) << 16)
- |* H+ y, S1 h" h% Y其他外设的时钟配置时( |+ w+ m7 \8 ]; \& y
- /* HCLK = SYSCLK / 1*// H4 |4 K% c# @' t9 |
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1;0 L& T2 }7 [6 r# U3 j0 ]
- /* PCLK2 = HCLK / 2*/* h$ w8 x3 t$ o4 I6 K, w
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;* G- [5 D4 g: V3 ]1 q* @- F
- /* PCLK1 = HCLK / 4*/* j; }2 i0 r+ M4 E7 c1 g
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码 " N: g6 ?1 g4 @* Q! J
03、备 注 # S, @, F0 Q; X! v
时钟中断
0 d3 A' M5 M# A- J$ ]2 K. K* r; y- a0 w, N0 i
: {' u+ ]5 M& G b; `+ ^1 i7 _0 X0 _# x' Y' O' y* ]
可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
1 @3 m. Q! G. G9 C# a( Y( F$ P K- n! ?% I. v/ i4 d5 s+ u
无源晶振不起振
y# s& c/ B% K& K/ k0 s( k5 b+ Q8 s4 n6 z
没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《晶振原理解析》。
7 ^ q/ T6 }1 ^5 b( A7 Q# Q E6 D. A. h0 n/ }
关于APB和PCLK& t$ i' A% v; j8 t- U+ g: e$ ~
5 |, F1 u& \. h- M- b& a, c* IF207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2
( A& J! ~/ b6 l0 z; v4 k1 _! B/ d% t
应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册7 W- I2 j+ G& [- s p2 N+ B" {
" b; ]$ q/ A/ |- }5 I
* g. F: g) m* R$ u, n( t
* y3 N- ?3 E* r' R. U/ M
! d3 O/ y& t9 A- J9 ?2 d% F% g! T9 R# e1 |( u2 N: `
( D" u9 a* R. E* W7 w# U
8 v1 e; a% h4 d. Q1 z* m0 a- r/ U0 v
# E- e6 p1 i0 Y4 m1 ?, |* a9 X* V! v) t |