|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
STM32的时钟树& }" k5 Y W2 Y2 h- W* \; m, q
对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。
) _& D/ g# y9 T 众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运 作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如 51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状 态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固 定的,用户无法将此时钟速率更改,除非更换晶振。
, V" I% X" f! L 而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。" e' v+ v" W2 t/ v
标号 图1标号释义! \7 Z3 Z3 U5 y
1 内部低速振荡器(LSI,40Khz)
u& x- p5 y, p$ p5 Q2 ^; o2 外部低速振荡器(LSE,32.768Khz)7 a9 n' r/ D& s
3 外部高速振荡器(HSE,3-25MHz) g+ m, g. ]0 N7 E3 x
4 内部高速振荡器(HIS,8MHz)4 `+ Q& }( Y; K7 n
5 PLL输入选择位! F- y# X5 I9 d6 |' B9 c$ u% |6 x
6 RTC时钟选择位. ^* c# W) ]1 F, g
7 PLL1分频数寄存器
7 Q6 Q% i/ a* I8 PLL1倍频寄存器
5 ]1 F* q4 q( e% K" B# j9 系统时钟选择位! a; H! o' A/ Q3 c( z9 b
10 USB分频寄存器; H! J% \+ A( C" O
11 AHB分频寄存器
; S8 v" @$ s, u! p7 t8 C12 APB1分频寄存器
* a3 o/ A N2 H8 \/ }& K8 ~13 AHB总线
# L! @. M. A( I% C14 APB1外设总线% b3 d: P& F g# M. d; a* @
15 APB2分频寄存器7 r' v p" }' K; S
16 APB2外设总线6 C7 A% S+ E! Q
17 ADC预分频寄存器% e7 u+ g6 Q! {
18 ADC外设
; {8 ]7 @. k$ C& W! ~( j; K+ W19 PLL2分频数寄存器
$ }/ W$ E: r: a2 Q5 h0 ]20 PLL2倍频寄存器. i8 R- j7 c0 o" Z4 f) U
21 PLL时钟源选择寄存器
* x, ^7 v- i# _22 独立看门狗设备% B8 c2 `1 h* n: J S
23 RTC设备
" @2 o+ C% r! ?" J3 z# dSTM32时钟树.jpg
. Y* {5 x( y/ A# p! s Y4 v0 u图1 STM32的时钟树
+ x! A; z+ A& \ 在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个 8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:
% {/ G7 ~& ]) a2 J7 i7 ^* e3——5——7——21——8——9——11——13
5 Q$ v1 t5 ~0 a1 D) T4 U, `对此条时钟路径做如下解析:
6 J3 _' f; q U, [! Q对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;
& F' o% Q- V* N+ y. z9 x对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
# A) N7 r* S) ]$ W8 g对于7,设置外部晶振的分频数(假设1分频);
1 i* `0 M' l. d4 W, o2 ?. ]对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
0 l$ D5 e/ H( O- f! T: x: t对于8,设置PLL倍频数(假设9倍频);
8 K" d5 `7 w9 {- r对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);$ _0 k, J4 Z/ ]. A8 _, o. ^* \. d
对于11,设置AHB总线分频数(假设1分频);* x4 g( N2 i% e! H r, x
对于13,时钟到达AHB总线;7 i2 ^3 N. ?% H& U3 c, {0 d
在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:; d" c' j9 \; c7 {5 F- f
3——5——7——21——8——9——11——15——16$ N5 n$ L* e7 v4 k8 s2 n4 O
对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;7 Z1 E" K4 b0 H% U. i; b
对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
/ V8 z: p' h( j0 I8 o- r对于7,设置外部晶振的分频数(假设1分频);
; _% `" v1 W2 Q5 T对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
9 f/ |. W1 C% C; m. D对于8,设置PLL倍频数(假设9倍频);3 C! f( a: j- C" Q! W j
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);) W6 e+ W+ n" p! G. H0 o/ @2 f
对于11,设置AHB总线分频数(假设1分频);
+ v& \, d4 m7 E- y对于15,设置APB2总线分频数(假设1分频);' [8 _& w/ M+ {; X9 n& k9 f% W
对于16,时钟到达APB2总线;3 W e% I2 o! ]
现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):
3 f; `# i( j, f) R' v1) 由3所知晶振输入为8MHz,由5——21知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。
. c! i6 `, B) ]( o0 L9 ^9 K L0 o# U2) 由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。
7 q% j6 {8 l9 i3) 时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。 ]0 s- F" H8 i0 {9 Z- o
4) 时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。8 p' i) J8 N/ t8 o' p7 ^7 G* |$ l
5) 时钟到达APB2总线外设。6 l0 n2 ^2 f6 Q" K0 W
因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:, a/ F$ O; X5 }. o4 _; i- S8 M
void RCC_Configuration(void)
5 ]% v0 O2 n4 u' u. O, ^0 w& v{9 H& v0 W$ U1 r
ErrorStatus HSEStartUpStatus; (1)
+ g9 }8 e5 S& \/ c3 b8 L RCC_DeInit(); (2)
9 a! @+ [2 L N7 T6 g% D RCC_HSEConfig(RCC_HSE_ON); (3)
3 E0 V3 u8 l7 g4 } HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4)
* K5 M) b! i3 K if(HSEStartUpStatus == SUCCESS) (5)
* ~) d9 d' C! j: C' x6 P6 F7 a7 d {
3 Y/ ~9 V0 I' p$ n- z RCC_HCLKConfig(RCC_SYSCLK_Div1); (6)
4 H: r) Q5 z& d RCC_PCLK2Config(RCC_HCLK_Div1); (7)
7 F' P& O" j: y RCC_PCLK1Config(RCC_HCLK_Div2); (8)
. v! ~+ Z" c* i1 H2 o) m) s1 w FLASH_SetLatency(FLASH_Latency_2); (9)
8 }/ v% e8 w7 r2 @! P FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)/ m% P( A1 H$ Q7 o- u+ N0 l
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11)
0 ?' J; X$ K+ P5 e" P5 R RCC_PLLCmd(ENABLE); (12)
/ \! `" B# W; D; B. E while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); (13)
- c/ o2 q N, \+ D& o- r. n* g RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)
! _: O4 `6 `: |6 ]# ^ while(RCC_GetSYSCLKSource() != 0x08); (15)+ C6 d: J. Q0 U
}
' n. r! c0 j$ D9 |0 Z% e2 t8 j |
|