EDA365电子工程师网
标题:
送给新手:STM32的时钟树解析
[打印本页]
作者:
liao821
时间:
2015-11-12 09:56
标题:
送给新手:STM32的时钟树解析
STM32的时钟树
' M X- T$ u+ J
对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。
/ b1 X0 i5 a! g1 x% u. o! @
众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运 作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如 51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状 态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固 定的,用户无法将此时钟速率更改,除非更换晶振。
! O1 ]( u8 U) r6 f4 n0 \
而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。
! K& u( p3 c# P* M3 C0 e5 W
标号 图1标号释义
4 ~* D, _5 Q" ^7 ]
1 内部低速振荡器(LSI,40Khz)
v+ r% |6 @$ e; `4 y5 O" W+ E6 \1 k
2 外部低速振荡器(LSE,32.768Khz)
1 G1 H, r; W, ^% g
3 外部高速振荡器(HSE,3-25MHz)
7 l- B, T" F0 s# X% C: j, G
4 内部高速振荡器(HIS,8MHz)
+ Y$ t P9 N$ h p/ J' s7 N% y; T
5 PLL输入选择位
% g2 L; F- X. u; Y
6 RTC时钟选择位
% E0 W4 V- O8 G, a9 [. R% N
7 PLL1分频数寄存器
1 S, t3 N- |. w; I
8 PLL1倍频寄存器
& [; }! g) I( q. ]! m u
9 系统时钟选择位
, q* O% g6 \/ Q& e/ f
10 USB分频寄存器
0 i. j9 G% B3 L. P* X
11 AHB分频寄存器
5 ? _0 r; ] e* Z
12 APB1分频寄存器
" ]' E* P. z! z. ]" `
13 AHB总线
2 Z0 l6 B. z3 q/ T! V
14 APB1外设总线
5 `$ }% B, @- G- h) H. G( D2 _
15 APB2分频寄存器
! m' m/ k5 b2 ^+ {2 _% L
16 APB2外设总线
9 n2 s0 r8 `8 [5 t0 @4 d
17 ADC预分频寄存器
- J! s; y U6 k4 J# S
18 ADC外设
# ?7 f. ~: {; i7 _# U4 t* {5 w
19 PLL2分频数寄存器
2 R! Z$ t3 Z D2 P- O# q
20 PLL2倍频寄存器
! Y; y6 d+ }9 S; t U
21 PLL时钟源选择寄存器
9 y1 r3 g( }% ~
22 独立看门狗设备
, W6 |" i" E- E% ~
23 RTC设备
: e1 O q/ ~, n1 X7 L
STM32时钟树.jpg
5 r7 G/ }) W$ Y. |
图1 STM32的时钟树
$ G# ~3 Q# U7 Q" }* w0 |6 F* N7 r
在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个 8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:
$ h5 f% Z$ ]4 ?# [' I- n: Z
3——5——7——21——8——9——11——13
- _) K" ]6 Y0 K! W; O
对此条时钟路径做如下解析:
; @/ e! A# [8 v* o1 [9 [
对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;
# k5 R- @ w! }2 T
对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
. Y$ \# @6 z# g! M6 w( C( i |
对于7,设置外部晶振的分频数(假设1分频);
$ f3 L4 \- _( x
对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
4 B5 h0 X% Y( o& E: ?
对于8,设置PLL倍频数(假设9倍频);
1 X2 f- R4 Q9 T3 L+ J# [' c
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
& T% b, k$ E/ X
对于11,设置AHB总线分频数(假设1分频);
7 \$ }( K& ]5 M2 j) i5 q
对于13,时钟到达AHB总线;
4 R" Y$ ]- V9 Y9 j
在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:
9 B" }4 `' r q" P0 g) T
3——5——7——21——8——9——11——15——16
+ \- W0 p2 ?! c) [3 W, W" U
对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;
' O( Y9 k U6 m1 x# W
对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
' z$ J0 \! B3 K5 U( C$ X# }
对于7,设置外部晶振的分频数(假设1分频);
1 e* G4 B: @5 n
对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
+ y& l9 A6 @, G; E% h& |! G* W+ K
对于8,设置PLL倍频数(假设9倍频);
9 m9 \- D+ n7 ?" G0 B2 X
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
% `3 E! ^' N& V, t4 f
对于11,设置AHB总线分频数(假设1分频);
& ^7 n, Z: d6 } s' v: x
对于15,设置APB2总线分频数(假设1分频);
# t( r# i3 F4 q% C0 E0 t
对于16,时钟到达APB2总线;
% s |1 D/ g. S. N& M/ T, ?3 ~
现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):
1 v) f/ h; U# G% S
1) 由3所知晶振输入为8MHz,由5——21知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。
5 ~1 j; n9 b$ ?* w( ~+ b( Q- f
2) 由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。
1 z0 y( t/ b0 k6 U6 Z
3) 时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。
6 ]+ \- `7 u9 ? L6 b
4) 时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。
( `. m' m1 D, k" p8 b- S1 ~
5) 时钟到达APB2总线外设。
6 R; @) A, }0 y# [; a
因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:
- d4 e! o) O$ w$ s+ L" r8 e) c
void RCC_Configuration(void)
* J: e0 `$ o- ^# [0 k
{
- c$ I* L3 M) {; j3 L! q
ErrorStatus HSEStartUpStatus; (1)
( g/ _6 q+ O; T: R, w2 z
RCC_DeInit(); (2)
0 Z2 \; r- h1 S; y5 W: C2 a
RCC_HSEConfig(RCC_HSE_ON); (3)
) F8 ~. G2 _) U A- {% s
HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4)
2 v9 y" v8 P8 [' J @- A8 b
if(HSEStartUpStatus == SUCCESS) (5)
! k: y, q& j8 Q5 E( i5 R) o( o
{
5 p1 [0 Q1 b, E. i% r: _) n. Y
RCC_HCLKConfig(RCC_SYSCLK_Div1); (6)
) G( S0 z( L% f& B
RCC_PCLK2Config(RCC_HCLK_Div1); (7)
) r5 F! h; n8 D' ?: ^
RCC_PCLK1Config(RCC_HCLK_Div2); (8)
1 S! p5 I; T6 Z4 q
FLASH_SetLatency(FLASH_Latency_2); (9)
, z6 `8 { f; n- i# A
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)
. x* G2 p/ B: R( I
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11)
. j, s# a5 Y& s( W# o6 d
RCC_PLLCmd(ENABLE); (12)
- X" c% T# i4 w0 y2 _- a) R
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); (13)
8 L8 J4 h3 Q; {3 a4 u
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)
: j: F7 l: o n- \/ t6 E" Q
while(RCC_GetSYSCLKSource() != 0x08); (15)
; r1 ^& R8 y' W7 }. K: v
}
: H- ], g& w; |4 P P3 n# Q
作者:
Haiting32451
时间:
2016-6-4 15:48
学习中,谢谢分享
& ~2 w8 n4 G+ N" B
欢迎光临 EDA365电子工程师网 (http://bbs.elecnest.cn/)
Powered by Discuz! X3.2