|
下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的。( D; b8 N( w. m! X( r& H; m& H0 f) {
) ?& T$ R0 T/ o1 t+ N
01 时钟系统介绍0 z) [- l% |. U$ l$ ?9 Y
7 _$ W$ E0 R+ h
▲时钟系统专业名词缩写 ) X+ W8 `+ {6 W, G9 L+ e0 U$ P7 `1 r
时钟系统关键组成部分
5 P8 J' d# H5 ` $ Q3 z0 }" p% |# E S3 i
6 }( F: B1 w) Q3 Y01 内部高速时钟(HSI)
# M% ^6 g1 X9 w- SHSI时钟信号可以通过内部16MHZ的RC振荡器产生,可以直接用于系统时钟或者用于PLL输入。 ) I. O8 L# J; C) y+ e$ n
HSI的RC振荡器的优势是:在最小成本(没有外部器件)情况下提供一个时钟源。它的启动速度要比HSE晶体振荡器更快,但是即使校准频率后,它的精度仍然小于外部晶体振荡器或陶瓷谐振器。 9 N$ Y3 j, t. |, K
$ `) J& a' C: V7 N+ |' `02 外部高速时钟(HSE)
1 N7 S& R3 B, G2 N6 Q* m- z D8 \9 I1 \* _/ V5 }7 n* `
外部高速时钟信息(HSE)可以通过两个时钟源产生:
4 d( I$ H- V% k4 o; T
① 外部晶体/陶瓷谐振器 5 H" Z. u: W0 a! G$ M# e3 z/ r- |$ j( x
② 外部用户时钟 9 R# Z! ^( Z7 B- m
▲两种时钟源接入示意图 2 v' v% h; o! s) ~3 u
03 主锁相环时钟(PLL)9 D0 b& n1 i* Q( f6 L8 C* \+ R, a
9 ^) N: z; e6 L6 B
STM32F2xx具有两个PLL
2 t% V- l8 V. x$ o' _! |$ x
① 主要的PLL通过HSE或HSI提供时钟,并且有两个输出时钟;
7 P: z8 V. V, Y4 J6 L. O: d: s$ |( K6 |
② 专用的PLL(PLLI2S)被用于产生一个精确的时钟去实现高质量音频效果在I2S接口; - i+ `0 B6 [" t/ h! Z' i7 d- g0 I
" H; q, L; ~# w
HSE/M*N/P得到PLL时钟 关于PLL锁相环说明
& u' [; i; n! Y) _0 D$ g7 G$ O) ^9 ^5 n) L0 {7 i
从1处输入,3处输出是1的N倍。 3处除以N又作为输入,当1和2的频率一样,就锁定了。(之所以图上是xN,因为从2看向3的) 04 低速外部时钟(LSE): F! x8 Y7 Q! K" W. c- w
, f0 r9 u; C# @
LSE是一个32.768KHZ低速外部晶振或陶瓷谐振器。
) I3 s( H4 N! e" U9 I, c3 b* O
它的优点:提供低速但是高精度时钟给RTC外设,为时钟/日历或其他时间应用。
+ \/ ~! m% z/ n- D) ^5 ]7 s- G05 低速内部时钟(LSI)
: a* v) s/ q0 h) z+ b: A2 m$ p+ @* m8 V, v* t. _! ]
LSI RC作为一个低速时钟源,它可以运行在停止和待机模式中给独立看门狗(IWDG)和自动唤醒(AWU)。它的时钟频率在32MHZ左右。 " b& E% T/ P' c, l# ?" D+ P
02 代码分析6 b) t) k1 T" k- Y; x2 M
时钟初始化代码在system_stm32f2xx.c文件中,大部分时候我们不需要修改时钟代码的,各个总线的频率我们可以在文件头看到。- |+ k4 V. X2 R* W$ u" h
* P) x' o! k# l
1 P' x- X! G7 H' B( s) X' O4 F! \
- $ i. r8 x3 {' t$ V6 o5 V( W" J" A
- =============================================================================
, V* p' H3 J# h7 p8 o. j - *=============================================================================
, N& H) s( A% q# I i - * Supported STM32F2xx device revision | Rev B and Y
( g8 s7 U' b) L1 s" n6 Q - *-----------------------------------------------------------------------------/ U/ p! T; Y9 m0 s
- * System Clock source | PLL (HSE): V8 _' c7 @ S0 g+ }& U
- *-----------------------------------------------------------------------------3 {! [) X# J$ u' B1 U+ N$ x
- * SYSCLK(Hz) | 1200000000 M. f3 u3 @# \% h
- *-----------------------------------------------------------------------------
. d! s6 S! c% { - * HCLK(Hz) | 120000000
+ h' u- B7 a6 E$ t3 i( f - *-----------------------------------------------------------------------------3 d1 o6 k0 @1 J/ E+ F& Z3 b3 V$ p% C
- * AHB Prescaler | 1
, g, ?3 v! H2 b) w- D - *-----------------------------------------------------------------------------+ z) A- }) x( }3 Z4 |
- * APB1 Prescaler | 4
6 D" q+ P8 r2 I5 ]8 j0 ~ - *-----------------------------------------------------------------------------
4 w. S# g; H3 r0 g - * APB2 Prescaler | 25 l% _9 ]3 x$ v+ e
- *-----------------------------------------------------------------------------
: Q3 j: ]) Y) [. u: y' n - * HSE Frequency(Hz) | 25000000! Q% R ]: Z! V* D$ f3 w& K- Q
- *------------------------------------------------------------------------------ }/ a. C$ Z' N+ \7 {
- * PLL_M | 259 W* F. W$ @: Z7 r( b! k
- *-----------------------------------------------------------------------------
: L5 ` o, B: { - * PLL_N | 240
8 L u2 Y& S4 S9 S9 B6 Y& Y/ V - *-----------------------------------------------------------------------------2 C" q# e& E# |, H
- * PLL_P | 23 ~: ]$ y* i, K0 W6 w
- *-----------------------------------------------------------------------------3 r, C7 e2 L6 U& }5 P' {
- * PLL_Q | 5
8 r5 f. k$ y* ~# K' J - *-----------------------------------------------------------------------------4 {) _9 M8 J* v5 i) v. L
- * PLLI2S_N | NA/ O0 C1 e% p" g- f7 \
- *-----------------------------------------------------------------------------6 V- d' ]/ s% Y" J& e6 z
- * PLLI2S_R | NA8 g6 R6 ^/ b! r% D
- *-----------------------------------------------------------------------------
: U+ i# ^+ U& r5 U - * I2S input clock | NA
; r3 u* X. ~+ i6 t. {$ K; z$ f - *-----------------------------------------------------------------------------% g* I4 L7 M. ^ Q
- * VDD(V) | 3.3
% U& I0 f4 i0 i+ ~, q - *----------------------------------------------------------------------------- ^1 T) m( g) p9 {$ p
- * Flash Latency(WS) | 3# ~% D2 |% G/ M7 k6 a( G" `
- *-----------------------------------------------------------------------------6 k% c, Q( Y6 G
- * Prefetch Buffer | ON& X9 d0 m, u" w3 D5 V. u
- *----------------------------------------------------------------------------- K8 z! T7 `+ O0 \2 R" z. T4 S
- * Instruction cache | ON- A2 [( i2 L; K( B0 ~
- *-----------------------------------------------------------------------------
9 [5 ] l( E# B4 Y+ j7 l4 M) ]. w - * Data cache | ON
4 M) a8 q% x' u k, z - *-----------------------------------------------------------------------------
1 {8 i4 V7 g3 W9 u: X - * Require 48MHz for USB OTG FS, | Enabled
+ d: w' T8 B9 J: H& r/ _, Y6 \ - * SDIO and RNG clock |
. B# {' L; o- G0 m8 B6 ] - *-----------------------------------------------------------------------------
' S5 O0 G) F* u - *=============================================================================' o0 S3 \+ j8 ^. I
- ******************************************************************************
复制代码 6 g7 U! L- {3 B& M& j
5 Y7 w5 O5 Y8 k4 _
( L2 u4 k% Z; T% g: q% t a4 l# A
5 L1 u' g7 G! W* s4 H
' j/ D3 L9 P/ @% @+ a在文件开始定义的有系统时钟频率的全局变量SystemCoreClock,其他地方需要时钟频率,可以直接使用该变量。
2 j/ X6 l+ w2 G8 H' L! D
7 X3 f) K8 D1 k& w* r3 G
- 2 g ^! {9 @" W, D
- uint32_t SystemCoreClock = 120000000;
+ v& \# R0 K O6 G9 T5 o
复制代码 $ ?- F6 b& y+ J$ ~% f, O
+ ^, s1 S5 L6 K s2 W
时钟配置从SystemInit函数执行,调用SystemInit的在汇编文件中startup_stm32f2xx.s(Keil编译环境)。
" W" Q% S" ?. D, F% X' l
1 k% y3 z0 o( m7 @: B3 |- IMPORT __main
2 K* f7 H$ s; V. ?3 F/ P5 l - LDR R0, =SystemInit
( Z0 ?, V- P6 ~" G c# X- ]8 P - BLX R09 i* h ]! n) \6 s+ L1 T" z
- LDR R0, =__main
9 ~* S, ?4 M: x8 x* Y. u - BX R0
( ?" ~; F* J- K - ENDP
复制代码 6 O* L: Z" Z: R; p0 l
+ Q7 \, F6 v! Z5 {9 D
; {& r# E- p# N) k8 \0 z! Z6 G- b
) {5 r" J9 l/ ]% B8 E+ V
在这里说明一下文档版本的问题:1 ]0 \: f6 {1 ]4 ]* }! S
1 ^* s0 R. E1 h( d& y9 S+ \
▲STM32F20X_User_manual的V7版和V8版对比图
+ l( B+ O2 D( U上述两图的区别是系统最大时钟从120MHZ变成了168MHZ,我的理解是同样是STM32F20X,ST由于技术进步或其他,使得新版STM32F207芯片超频支持168MHZ。
$ M- d q. x4 h+ T3 l5 r下面我们主要分析SystemCoreClock的120M时钟怎么从一个外部25MHZ的HSE得到的。
. ]; E0 ?$ |& f: k1 P& A# b) f, }( T# A
* W* N8 K+ K3 N! f w2 t! H& Y
我们要从25MHZ的外部时钟得到120M的系统时钟,需要上图中标注的重要4点: 2 q( M/ n9 J/ \6 \' s2 q5 r* M3 r
1、使能HSE 4 J/ p* c8 o7 z) y
2、选择HSE作为主PLL的输入时钟
* r8 l* f$ r% H: L
3、主PLL倍频后得到120MHZ时钟
7 H; ?: W! Z" r2 Y
4、系统时钟选择主PLL时钟输出作为系统时钟
' {: a: D' v( y
我们找到对应的代码
7 J7 S% k4 z2 {6 F+ P- d8 ^ 1、使能HSE" d6 u# P( O' E. {
- /* Enable HSE */
# F; _# [. A. U0 L5 F& A4 y! ? - RCC->CR |= ((uint32_t)RCC_CR_HSEON);
复制代码 6 j" @6 E( q0 `
. [# S/ P5 Z2 m5 L
9 Z. I) b9 o9 u+ R' x! O3 @* [在RCC_CR寄存器(RCCclock control register RCC时钟控制器)中,有打开HSE的控制位
" f4 v3 C8 U/ Y2 E8 f 0 |% a$ x% n5 C; g8 H: O
$ ]1 B/ J i- t+ h2、选择HSE作为主PLL的输入时钟
4 t7 g9 \: P; d
3 Q6 l* q" D% `6 y
$ H; M. l/ b7 B2 ?6 L) A
/ H- k" J- p* ~0 E K- /* Configure the main PLL */
$ Q2 \. U6 G6 J% t% M - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
+ f Y1 r9 e8 x& ` - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
; F0 Z+ h$ Y/ F9 d4 B h) J , q6 o3 N& \% V
7 D% C3 m3 Y* K6 p/ M6 g3 s% t/ i+ z. r( V7 X
/ J S+ c$ Y. ?3 R" oRCC_PLLCFGR_PLLSRC_HSE就是配置HSE作为主PLL的输入时钟& t# ^( k1 Q' L6 i, p9 R; a* a" L
3、主PLL倍频后得到120MHZ时钟
4 y% R+ {+ k8 @7 K- ; Y" M$ I+ |1 O# Z2 E
- /* Configure the main PLL */. `9 w( d) h- h; b* F
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
! K/ `8 H$ m* W X- N* k" `' m* O - (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码
1 O& @# t2 ~. b& x0 g3 D: O1 X5 B& x1 A 8 @5 `6 C( M" R( r9 o; t
- /* Select the main PLL as system clock source */: L$ b& J8 @3 t& h3 o$ J, `1 M
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));# I( J3 l/ e8 T. A
- RCC->CFGR |= RCC_CFGR_SW_PL
复制代码 ! ]0 n) |# i# ^' X
" E6 w L y2 I
对于主PLL的配置寄存器,在RCC_PLLCFGR寄存器中有说明 ' r/ N1 E2 \# @
_+ @& O- Y% T7 K7 I7 W/ q
整理后得知f(out)=f(in)* N / M / P - /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */1 Q3 O6 w4 ~9 Q! X/ d$ c
- #define PLL_M 25
; s" k; H+ k) i* I+ ~ - #define PLL_N 240
1 B) r5 H7 [- k' g- I; {) z* Y9 O
2 p: ]2 S6 m/ x+ |9 H- /* SYSCLK = PLL_VCO / PLL_P */
' f! Q- |/ T. E* \ - #define PLL_P 2
复制代码 ! t0 u+ W$ Q. l
3 b0 H/ |6 x0 V" ?8 Q7 @
& @$ F4 [3 `4 w; H* y0 n+ M
, u, c0 j' ~, R0 ?0 [0 \/ u# A! ^7 `) o4 q5 I1 y* k4 |9 s
% v/ i+ m5 {: r% a" [, |/ l: w7 K
, k. N* t( Z h. F+ ^" {& I3 K
4 J" X/ h) h7 n8 @. x- R6 u
ST并没有使用if或case语句判断,因为对应的数据除以2减去1就是寄存器这两位的值,所以可以按照下面这样写,这种写法值得我们学习。 - (((PLL_P >> 1) -1) << 16)
复制代码
9 P! Q8 C! I- j% y ! b6 D1 d1 k, o7 B& N5 R
- 7 ]3 R) q b( V+ }8 w( C3 r2 U
- /* HCLK = SYSCLK / 1*/
( I) a% h$ P) F) O; T! W! w - RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
% w: Y& o2 Y, b1 q% W4 f
3 g; @- O8 J; R- /* PCLK2 = HCLK / 2*/9 a( J1 k: j f( M# M/ Q0 o+ d0 M. l3 `
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
9 `6 F6 a( i3 u& v* L% D" h8 ^
5 }2 @! f; n" }$ L3 b+ l' [) Y2 b- /* PCLK1 = HCLK / 4*/4 q! k3 P) \3 C3 w2 e; f$ J
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
复制代码
: I* l" l& h+ d( A1 C 0 M7 }+ N% Y$ d( ]" c3 m8 _; v
" ?! D4 M+ w- } e; j
9 H& S% K; f% ~% ~$ d备 注
$ V6 @& ~3 d* n3 g9 N/ B6 z0 S6 h/ A+ u# [
' f, q- m( Z) O2 |& H6 b
7 w5 }5 Y9 p+ v, T3 X$ t7 H
& s0 _8 n" c: o# I4 U8 s' p+ t
# w( y k7 |& y7 z; I$ u可以配置外部晶振出错时的中断,还有RCC中断,因此我们可以在外部时钟出问题时,切换为内部时钟,不至于整个系统挂掉。具体见ST给的官方代码。
8 r9 Y, E) k- w; V. S# m9 X
& D' ]( ]2 u9 G3 M3 m
`, @0 |* m" z# U5 S' R1 g( S没有程序,无源晶振是不起振的,需要配置RCC时钟控制寄存器的HSEON位打开或关闭HSE振荡器。具体可以看之前的文章《[color=var(--weui-LINK)]晶振原理解析》。
: `- O+ ?( r7 c6 `' z4 e! \2 X- Q4 S7 a, i
! w4 ?/ v1 T3 \! ^5 T; ^; A7 v
F207是时钟图没有显示PCLK1和PCLK2,应该就是APB1和APB2 应该指的是一个PCLK应该是PeripheralClock的简称,看F105手册 `' r3 M: G! ?; q, q
3 [/ o7 A- t5 m2 `; X* ~8 p, y- m0 z
& ^1 i" {; `0 Y; J% ?8 i) l$ ~; j$ B, H3 F' o. _8 f# f3 o
4 D6 Y; ?$ Q% V+ S2 F B |