|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
STM32的时钟树
% x) V6 S1 O* ~3 D7 x9 H; k 对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。" }9 L8 k- p% t* f; A, p
众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运 作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如 51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状 态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固 定的,用户无法将此时钟速率更改,除非更换晶振。
5 [/ B) q6 C4 m1 `) b9 f2 J. K6 w 而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。 b( s* y0 K* w
标号 图1标号释义
. p6 }( X" v8 D2 q& V, V9 l. y$ P, c# S1 内部低速振荡器(LSI,40Khz)
9 ~4 b. I0 v4 f; q2 外部低速振荡器(LSE,32.768Khz)4 Z- L& G6 h0 P) {- `
3 外部高速振荡器(HSE,3-25MHz)6 j5 j+ G @! }+ Q m# D" v1 u
4 内部高速振荡器(HIS,8MHz)3 h D6 \- Y+ b1 P7 }5 H
5 PLL输入选择位
. w8 A o( o7 ]$ e! L+ q6 RTC时钟选择位$ Z ~: A# K9 S! \0 H' s" D/ t8 G
7 PLL1分频数寄存器/ o& z* W- K) n( K
8 PLL1倍频寄存器% W0 i! E' a4 [! g
9 系统时钟选择位
$ @5 l* _3 {6 {$ L10 USB分频寄存器7 }0 s3 e- z1 U+ v7 G- A
11 AHB分频寄存器5 @" p( ~1 P5 ]/ y# e
12 APB1分频寄存器
) M0 v1 |3 `9 Z; D8 c' f& I! _" K13 AHB总线
* M0 p$ X3 _3 t8 N9 M1 F1 M5 G C) n14 APB1外设总线
( V- r$ g6 @' h; Y N15 APB2分频寄存器
" n6 Z& L8 p( l U' x0 o$ p16 APB2外设总线# i7 k9 f# s: l5 E! g
17 ADC预分频寄存器" v7 p5 i; I: \0 k! t" }
18 ADC外设. {# D- _- J4 s. c0 W* `
19 PLL2分频数寄存器) V3 L/ z. C5 C% U3 e+ E2 x( ]
20 PLL2倍频寄存器& H- V+ U& U) z O
21 PLL时钟源选择寄存器) p/ C% C2 ?8 G# J8 T4 s1 }2 m
22 独立看门狗设备
% E# P# m8 t, ]3 e% q/ t% ?23 RTC设备8 t# @# A; s! A) Y" Y
STM32时钟树.jpg* p. g/ M# q. @ r
图1 STM32的时钟树
, X5 V! t! m8 ^1 d 在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个 8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:. ]; }" t! k3 B
3——5——7——21——8——9——11——136 e( k" i) u3 |% x( w
对此条时钟路径做如下解析:
8 j, B7 {* B) U& @7 ~) Z- j0 z6 |5 G对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;
% X# S6 k/ }0 H! Y6 ?对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);, V9 J8 N$ `- R0 V, k% Z' v* R
对于7,设置外部晶振的分频数(假设1分频);
z2 d. H6 Q) n+ R0 \4 L0 {4 Y对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);1 j8 n$ l) i7 d% M/ f/ X9 k, v% Y' e+ R
对于8,设置PLL倍频数(假设9倍频);' f8 X: _- F1 V* f9 J2 I
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
7 s, ^0 p- `4 z# s) M! z, U8 o6 d对于11,设置AHB总线分频数(假设1分频);1 u9 o, [9 x' I
对于13,时钟到达AHB总线;4 g% R" X- z5 K. o ^) D9 @+ l
在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:4 ]& i" H+ u& k7 u* s
3——5——7——21——8——9——11——15——16
- i5 G: Y( I* ? f% O0 W& O对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入; u0 R5 J' @2 Y& t
对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);/ g/ [1 t2 g8 J# N8 N. j
对于7,设置外部晶振的分频数(假设1分频);
4 `# _3 n h: {对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
0 I2 b0 W# h! R& G u对于8,设置PLL倍频数(假设9倍频);" p! k- n7 X% e/ m+ b7 p; F) A0 v
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
! Z7 B9 p; ]. h7 N对于11,设置AHB总线分频数(假设1分频);# s$ t( d3 A$ o
对于15,设置APB2总线分频数(假设1分频);
. _- O: _) l0 a8 E* b对于16,时钟到达APB2总线;* \7 t; ^) X3 c
现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):
9 @9 c( d9 E! ] _+ H1) 由3所知晶振输入为8MHz,由5——21知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。3 R# v0 ^ E5 V8 A- H
2) 由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。4 s& K1 z6 z$ X
3) 时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。8 s0 p) u1 K# L4 K1 T! ?$ r7 w
4) 时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。
8 d( T! F* _ i/ U2 {: L6 b3 M5) 时钟到达APB2总线外设。
( U A/ s9 L% s/ b$ K因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:6 u0 P9 h1 C' d) ~! b
void RCC_Configuration(void)! ^- f- D* @& Z9 K
{
. t f' l) E3 Q' `( w0 ]3 a6 A ErrorStatus HSEStartUpStatus; (1)
) J- _- i3 ^; Y/ P( O RCC_DeInit(); (2)+ N: |( f+ h- C' j; G% Q8 a
RCC_HSEConfig(RCC_HSE_ON); (3)2 c7 g- ?3 C2 o
HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4)) S, a W' h& {5 Z+ T5 }) ]% Q
if(HSEStartUpStatus == SUCCESS) (5)8 ~5 \' R: O& N+ a
{$ ^1 U9 f, U( @& R4 L; F
RCC_HCLKConfig(RCC_SYSCLK_Div1); (6)
+ u. E( N2 X" S, I# z( [" r RCC_PCLK2Config(RCC_HCLK_Div1); (7)3 g5 F% X- p! b% }( D
RCC_PCLK1Config(RCC_HCLK_Div2); (8)( M6 }1 h& n# T% K, _7 V/ h
FLASH_SetLatency(FLASH_Latency_2); (9)
, E% H- O5 ]& {7 [( I. ?2 f0 q5 g5 L FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)& l, e* ^6 I7 ]4 L* b
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11), h* G2 N/ ?, M' Z
RCC_PLLCmd(ENABLE); (12)5 `( Q" r/ O; W0 F7 l
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); (13)
/ b& @2 @5 ?2 s4 e3 |* C/ ]) U8 U w RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)
1 \7 V; b( \7 x5 x* N/ F! ? while(RCC_GetSYSCLKSource() != 0x08); (15)2 x+ G' Q0 T8 p6 O
}
( T, ?" `7 |; b3 f0 j3 A |
|