找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

巢课
电巢直播8月计划
查看: 120|回复: 1
打印 上一主题 下一主题

送给新手:STM32的时钟树解析

[复制链接]

169

主题

582

帖子

1242

积分

四级会员(40)

Rank: 4Rank: 4Rank: 4Rank: 4

积分
1242
跳转到指定楼层
1#
发表于 2015-11-12 09:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您!

您需要 登录 才可以下载或查看,没有帐号?注册

x
STM32的时钟树) Z2 h+ d* x! r% O! m$ z- Y: o
         对于广大初次接触STM32的读者朋友(甚至是初次接触ARM器件的读者朋友)来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上。这问题有个关键字叫:时钟树。  Y8 }& r) k( v$ z2 }7 P- h
   众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲来驱动——往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运 作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端8位单片机诸如 51,AVR,PIC等单片机,其也具备自身的一个时钟树系统,但其中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状 态(假设单片机处于正常工作的状态)。比如51单片机使用典型的12MHz晶振作为时钟源,则外设如IO口、定时器、串口等设备的驱动时钟速率便已经是固 定的,用户无法将此时钟速率更改,除非更换晶振。
* u; c$ }! r& `7 ]1 e- u! A+ z         而STM32微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系,本文将来详细解析STM32微控制器的时钟树。图1是STM32微控制器的时钟树,表1是图中各个标号所表示的部件。
' _# N6 j9 J$ o3 P! F4 m& {; P标号            图1标号释义" B9 a. }4 A3 o- T; v+ t0 b" g- y
1     内部低速振荡器(LSI,40Khz)8 c3 W& A5 K( U  a1 v* V2 f( D
2     外部低速振荡器(LSE,32.768Khz)% W" L$ B8 A4 w6 p# L
3    外部高速振荡器(HSE,3-25MHz)
; F# Y/ `; v1 x2 m# w4    内部高速振荡器(HIS,8MHz)
2 L. C, T* u: `( R, p5    PLL输入选择位' C+ ~/ N0 w6 ?" l) T' W+ X1 `( d' l
6    RTC时钟选择位6 [0 h3 Y& Z1 ~+ b* J7 P
7    PLL1分频数寄存器. H; W' L+ ?, p& `; U6 j
8    PLL1倍频寄存器
1 z& ?8 a! F' Q5 x% L2 w' i! a0 R9    系统时钟选择位
5 w8 |5 |$ @$ h& Q& V9 y10            USB分频寄存器
+ E- I) \, x3 A! W3 v11            AHB分频寄存器- x' b! @8 d1 ]' h& ^& D' \
12            APB1分频寄存器
" w) ~2 ^1 {% q13            AHB总线, u2 I. F, h: M. g/ n
14            APB1外设总线- c% E4 z& x$ q$ p
15            APB2分频寄存器1 {, r+ q* W3 I% N& V: i; u9 ^
16       APB2外设总线
! ^' o* U: @9 m  K' ]2 t, V- Y& Y17            ADC预分频寄存器3 ?) B* D" j+ R+ p3 f4 u8 H
18            ADC外设5 @, n4 ?2 j8 x; e# `/ [  S
19            PLL2分频数寄存器
# C/ @* n3 A" O! \20            PLL2倍频寄存器
0 @0 v8 x! ^$ G/ S6 D, l; {, \  e21            PLL时钟源选择寄存器+ f' \; m3 E, u3 r# @( ^; B% W
22            独立看门狗设备8 Q% T* k' O# l# Z- P$ o
23       RTC设备% ?3 F: S0 n6 S" K, [7 H+ u9 _
STM32时钟树.jpg
9 v8 Z" M' s7 Z9 c图1  STM32的时钟树
7 b. ^% N  @5 m, o. _- h" |   在认识这颗时钟树之前,首先要明确“主干”和最终的“分支”。假设使用外部8MHz晶振作为STM32的时钟输入源(这也是最常见的一种做法),则这个 8MHz便是“主干”,而“分支”很显然是最终的外部设备比如通用输入输出设备(GPIO)。这样可以轻易找出第一条时钟的“脉络”:( U1 K# D1 h5 H
3——5——7——21——8——9——11——13: L# t7 x0 C. _. M
对此条时钟路径做如下解析:
3 F* r) ]4 ~6 L对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;" Y- S* A2 Z( |( S/ N
对于5,通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);' o# h/ k* c" U7 t4 g% S
对于7,设置外部晶振的分频数(假设1分频);
5 k' e5 B6 A% p) B! p4 d对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);
( ]. E, y6 d; m对于8,设置PLL倍频数(假设9倍频);! T4 k6 _2 E$ q. d" |
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);3 A0 r) i; B. s  g1 R
对于11,设置AHB总线分频数(假设1分频);, i9 A% }1 s( ^2 P' c! Z$ n
对于13,时钟到达AHB总线;
" j4 A  m0 S& E) @! u0 Y$ n在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的时钟来源于APB2总线,同样在图1中也可以寻获GPIO外设的时钟轨迹:
. w; k+ x: x' S  P7 X8 J! f3——5——7——21——8——9——11——15——16, `. I6 L% Y6 c/ V
对于3,首先是外部的3-25MHz(前文已假设为8MHz)输入;7 R) h( }5 {5 }  {6 _
对于5, 通过PLL选择位预先选择后续PLL分支的输入时钟(假设选择外部晶振);$ y5 Q- `2 I: a1 t/ o; j
对于7,设置外部晶振的分频数(假设1分频);0 P! ?% H0 @/ h- k9 E: w
对于21,选择PLL倍频的时钟源(假设选择经过分频后的外部晶振时钟);% f- V, y. r0 Q
对于8,设置PLL倍频数(假设9倍频);! I" c3 z  `! F5 K& u: H9 m
对于9,选择系统时钟源(假设选择经过PLL倍频所输出的时钟);
3 T8 i" K3 i9 E& B  J- G* e对于11,设置AHB总线分频数(假设1分频);
1 l% H" t5 E) F: P# e! L' C对于15,设置APB2总线分频数(假设1分频);
( P( J4 X2 X1 j2 ?对于16,时钟到达APB2总线;
5 ^- h4 r1 a' d( |& T( `- n现在来计算一下GPIO设备的最大驱动时钟速率(各个条件已在上述要点中假设):
) ~( v4 }# {, `4 ], u4 h1)   由3所知晶振输入为8MHz,由5——21知PLL的时钟源为经过分频后的外部晶振时钟,并且此分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。
+ K. C( X2 V- j. A2)   由8、9知PLL倍频数为9,且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为 8MHz * 9 = 72MHz。$ d1 o) D# n# `6 |" U( z0 ~
3)   时钟到达AHB预分频器,由11知时钟经过AHB预分频器之后的速率仍为72MHz。
, V; S2 A' u, q: O4)   时钟到达APB2预分频器,由15经过APB2预分频器后速率仍为72MHz。
# \5 G+ |. J6 K+ V' s+ q: |: @8 E5)   时钟到达APB2总线外设。, y; |, L. I! ]1 `! g( `3 v
因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:
: C* L# X5 z4 t8 n; ^* D, ]: e4 w5 Wvoid RCC_Configuration(void)
4 t% i; ]; `9 o3 c( n# q{
1 K! D/ G  ?( K& {  m/ Z        ErrorStatus HSEStartUpStatus;                                                                                                     (1)
( x' I  ]: t$ f, W        RCC_DeInit();                                                             (2)4 f" ?) V) X$ _- g! h6 B) i
        RCC_HSEConfig(RCC_HSE_ON);                                                                                                   (3)4 u" H' i" U% ?% ~$ F' L
        HSEStartUpStatus = RCC_WaitForHSEStartUp();                                                                     (4)
1 m: d: B' M, D; T& a. T        if(HSEStartUpStatus == SUCCESS)                                                                                                (5)5 C  U8 d7 T3 A3 w% x& t
        {9 l  c6 {2 j  O% N5 v3 ~9 J
                   RCC_HCLKConfig(RCC_SYSCLK_Div1);                                                                              (6)
  l) L1 w, e2 {$ |! @                     RCC_PCLK2Config(RCC_HCLK_Div1);                                                                               (7)
& Z. ~7 s8 A' j0 b1 @: [                     RCC_PCLK1Config(RCC_HCLK_Div2);                                                                                (8)
) c& x: o( o) s- {( V6 J                    FLASH_SetLatency(FLASH_Latency_2);                                                                             (9)
+ i8 E& s4 C, b5 k  J, |                    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);                                      (10)
1 z" ~( e. C9 ^  Z5 y- m; |                  RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);                                         (11)
. w% b1 P9 x2 `! T9 X                  RCC_PLLCmd(ENABLE);                                      (12)
3 l. k3 H) v& o                  while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);                                                       (13)
6 N5 A% T2 c9 R8 O9 v( P                  RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);                                                               (14)  u0 S. ]/ g# G4 F
                  while(RCC_GetSYSCLKSource() != 0x08);                                                                               (15)+ Q/ J9 s, C. C1 V2 X) f
        }
+ x0 R& E8 D' {7 ?! ~3 h
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 支持!支持! 反对!反对!

245

主题

1028

帖子

1921

积分

四级会员(40)

Rank: 4Rank: 4Rank: 4Rank: 4

积分
1921
2#
发表于 2016-6-4 15:48 | 只看该作者
学习中,谢谢分享
5 t+ {+ L" r( ]$ C3 \$ q
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

巢课

技术风云榜

关于我们|手机版|EDA365 ( 粤ICP备18020198号 )

GMT+8, 2024-11-8 22:44 , Processed in 0.057528 second(s), 32 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表