|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
STM32的时钟树
1 l; Y4 t' _; t) ^1 S$ T 对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。) v" J( E. V, O: p1 y: ?9 k* N( x
众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运 作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如 51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状 态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固 定的,用户无法将此时钟速率更改,除非更换晶振。8 Z2 `& ^4 H( u. ]
而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。
. i9 |2 u! o, d$ Z+ H" o O' Q7 R标号 图1标号释义
4 r$ i& k) ~( D) X* a3 t+ L S2 u1 内部低速振荡器(LSI,40Khz)4 n; B: z" F7 ~& d2 T- R
2 外部低速振荡器(LSE,32.768Khz)2 w _- D9 w1 E" k. D
3 外部高速振荡器(HSE,3-25MHz)! b3 O5 e+ |; ?, N+ N$ z
4 内部高速振荡器(HIS,8MHz)/ W1 I2 ^# X# m* H& L4 i
5 PLL输入选择位( \% U1 {& c K1 m. U
6 RTC时钟选择位, p8 Q/ Y0 C: h r4 ^1 c8 \& u6 }
7 PLL1分频数寄存器
2 q. C$ R+ T4 t8 PLL1倍频寄存器
& T- j& M+ l% a6 k9 系统时钟选择位
% n+ H% K7 o1 \10 USB分频寄存器
* e$ r- t# S. _9 k11 AHB分频寄存器; Y& w2 @, r* \# V0 m
12 APB1分频寄存器1 z/ B+ I0 K( V4 W% w( @
13 AHB总线* S* ~3 Z- T$ B' s
14 APB1外设总线, w' R5 u ^3 A; x F: i4 Y
15 APB2分频寄存器* \# s& @$ E5 w
16 APB2外设总线# { S# V, t" o$ c& q
17 ADC预分频寄存器/ w; x8 N7 e( y8 k& r0 k7 ?
18 ADC外设
3 w& P2 o# {& X$ H7 l# W' h: G19 PLL2分频数寄存器
. |1 [ S: [, y: {2 E" R, c0 b20 PLL2倍频寄存器, Z1 j4 e% O c, c; g, t
21 PLL时钟源选择寄存器
z7 M5 U! v6 T% Y. k- ^( i22 独立看门狗设备
' }+ p. K' p. B! Y0 q# t- l! u/ W. J) y/ \2 m23 RTC设备
) B! y8 T R4 t0 W6 W r% z8 lSTM32时钟树.jpg
) [8 Q' C% W# {, ^+ c0 [# \图1 STM32的时钟树
$ V+ `. P6 i0 u! \# n: E, S: V 在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个 8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:+ G+ }, ]2 |( y5 S, c$ l \
3——5——7——21——8——9——11——13
* M+ v( M1 E, O' q' x对此条时钟路径做如下解析:( @4 {7 W" G# v. _; E
对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;
5 X% Z8 C5 }0 z3 J8 b9 E对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
" f8 s0 h( R* n* _ e: l对于7,设置外部晶振的分频数(假设1分频);
& _' @3 Q5 G& ~4 l$ u0 H% x/ |( @3 o对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
$ U; E2 X$ v( m; {: w* E, c对于8,设置PLL倍频数(假设9倍频);& e, O! C+ t& t' _. \3 K) G0 W
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
+ h: X+ v9 G/ y- J, S2 |- n1 V对于11,设置AHB总线分频数(假设1分频);
2 T# l# I- h# Y对于13,时钟到达AHB总线;" G5 o- ^+ C7 z) w
在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:
1 w1 n: b2 ?7 r# f3——5——7——21——8——9——11——15——16
4 o3 o4 x# s- E7 I$ f+ Z9 I对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;; M$ Q3 ~5 Y! x7 k7 `5 M/ N) d- n
对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);
3 ~3 r' k4 E' \对于7,设置外部晶振的分频数(假设1分频);
0 a: Z7 w0 d$ `& ]3 ?9 ?对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
0 A" [1 q# W, z( v# V* {! V6 W% L对于8,设置PLL倍频数(假设9倍频); t* [& T' S5 h8 Q. |, @4 k
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
~0 K9 h8 f1 z" I% O对于11,设置AHB总线分频数(假设1分频); M0 M, d6 e5 z5 P( a
对于15,设置APB2总线分频数(假设1分频);
7 P2 `# A/ B7 ^8 Y6 V6 L对于16,时钟到达APB2总线;
- u( Q' r( y$ u现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):
- K$ I% O/ t+ a0 K8 P+ D# a9 e6 c2 o1) 由3所知晶振输入为8MHz,由5——21知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。
1 |0 \7 q, o M/ G, S' N2) 由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。
4 b8 X' P3 x0 X0 h/ r$ P0 o3) 时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。$ J* h: Y* C1 ]
4) 时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。
+ y; G6 v2 T* \! o! F; m7 G3 u( Z4 L5) 时钟到达APB2总线外设。, y I: l8 f6 I# R/ z" O
因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:
+ d7 S$ s9 q/ b! h+ e" c% Z! B5 q& Lvoid RCC_Configuration(void)/ g2 j6 x0 n x0 U/ Q+ g! c: H2 q) Y
{+ b% C* U- Z$ A
ErrorStatus HSEStartUpStatus; (1)4 f# q: A$ M, ?; N6 R, M
RCC_DeInit(); (2)% ?/ A4 d8 n# r$ b c, Q
RCC_HSEConfig(RCC_HSE_ON); (3)0 F- q8 J* ?, @& |) L4 p
HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4); K6 n c: [6 s5 g8 c+ C: T
if(HSEStartUpStatus == SUCCESS) (5)) m# `; u2 q K
{) M, L6 F9 |# S. m% h; { f0 F
RCC_HCLKConfig(RCC_SYSCLK_Div1); (6)
4 U) V' w, G- V) [2 }8 p9 ?* p9 H RCC_PCLK2Config(RCC_HCLK_Div1); (7)
) p" o5 P5 z. d( J0 u RCC_PCLK1Config(RCC_HCLK_Div2); (8)
5 A6 v& K/ j& R" ?. o" Y FLASH_SetLatency(FLASH_Latency_2); (9)
5 J4 D4 o7 L8 _: Y+ K5 g FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)
0 v- i/ ~5 S$ t RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11) T5 U) ?: f6 ~/ | f
RCC_PLLCmd(ENABLE); (12)
' Q5 _( X+ A* U' H3 I while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); (13); }7 ~1 K4 Q5 [0 @, @6 }9 Y8 `, r
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)
3 d) G+ r' C2 d while(RCC_GetSYSCLKSource() != 0x08); (15)
% {+ h3 \+ g0 l" _ }
7 D' d9 |6 h, l& H% B6 |5 @ |
|