1 什么是时钟
- r$ ~. C( [# O: c. ]/ M
, c. f+ {! t2 d( x: E8 M时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。 # z, N+ |: b# M$ L5 m
为什么 STM32 要有多个时钟源呢? 6 ?7 O9 `' F- {
STM32本身十分复杂,外设非常多 但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费 并且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。所以便有了STM32的时钟系统和时钟树 + f* F J6 D, _: F* }
总括:- STM32时钟系统主要的目的就是给相对独立的外设模块提供时钟,也是为了降低整个芯片的耗能。
- 系统时钟,是处理器运行时间基准(每一条机器指令一个时钟周期)
- 时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。
- 一个单片机内提供多个不同的系统时钟,可以适应更多的应用场合。
- 不同的功能模块会有不同的时钟上限,因此提供不同的时钟,也能在一个单片机内放置更多的功能模块。$ `" u) B) J/ i! q7 `, S, t
对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗 - STM32为了低功耗,他将所有的外设时钟都设置为disable(不使能),用到什么外设,只要打开对应外设的时钟就可以, 其他的没用到的可以还是disable(不使能),这样耗能就会减少。 这就是为什么不管你配置什么功能都需要先打开对应的时钟的原因
, f$ J$ ~4 t4 P3 E6 K( Q
/ i0 j& q! e9 W: l- _" KSTM32的时钟系统框图
4 ? ?# ~$ i3 G$ E0 v* H; K- `! ~
& {2 m6 L, h8 D& m1 X) G- @
/ W# G( l0 e- P) b" y% k* K* A6 o, l6 ? a D$ X) m& W
乍一看很吓人,但其实很好理解,我们看系统时钟SYSCLK 的左边 系统时钟有很多种选择,而左边的部分就是设置系统时钟使用那个时钟源, 系统时钟SYSCLK 的右边,则是系统时钟通过AHB预分频器,给相对应的外设设置相对应的时钟频率
5 j( S! \! Q& C0 G y从左到右可以简单理解为 各个时钟源--->系统时钟来源的设置--->各个外设时钟的设置 时钟系统1各个时钟源 (左边的部分)STM32 有4个独立时钟源:HSI、HSE、LSI、LSE。
9 X0 [$ F1 a. c①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
4 O8 q1 O3 {! X0 f/ c7 [1 a②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。3 y8 A9 c: D2 U
③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。
1 G9 `6 ]( x& ?$ Z0 ?④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。 其中LSI是作为IWDGCLK(独立看门狗)时钟源和RTC时钟源 而独立使用 而HSI高速内部时钟 HSE高速外部时钟 PLL锁相环时钟 这三个经过分频或者倍频 作为系统时钟来使用
/ t4 P& A0 s4 R. hPLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。 通过倍频之后作为系统时钟的时钟源
. O% n1 n7 f" u% ~2 [( h举个例子:Keil编写程序是默认的时钟为72Mhz,其实是这么来的:外部晶振(HSE)提供的8MHz(与电路板上的晶振的相关)通过PLLXTPRE分频器后,进入PLLSRC选择开关,进而通过PLLMUL锁相环进行倍频(x9)后,为系统提供72MHz的系统时钟(SYSCLK)。之后是AHB预分频器对时钟信号进行分频,然后为低速外设提供时钟。 或者内部RC振荡器(HSI) 为8MHz /2 为4MHz 进入PLLSRC选择开关,通过PLLMUL锁相环进行倍频(x18)后 为72MHz PS: 网上有很多人说是5个时钟源,这种说法有点问题,学习之后就会发现PLL并不是自己产生的时钟源,而是通过其他三个时钟源倍频得到的时钟
1 s" F' g) H4 ]7 I2 系统时钟SYSCLK$ W2 S v; Z# |
1 U! N1 y, ]8 k. V- c4 u
系统时钟SYSCLK可来源于三个时钟源:) a6 x& `' u, P/ S; q, n
①、HSI振荡器时钟' o8 Y4 o. {7 \ s! b% K
②、HSE振荡器时钟
* w/ e+ T* H3 r, q8 O* G③、PLL时钟
$ M p2 Z3 E# w1 t' r最大为72Mhz
U ?. V0 Q% h7 J4 J
4 N% j6 G3 m3 K3 USB时钟
- M3 K+ \) s( j0 [/ A7 B
- {4 l5 K$ u b- G7 u* L
STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取(唯一的),,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz 3 |; {; x, {* K# P
* b% ^6 w0 }3 e1 G/ w4 把时钟信号输出到外部
5 T! X3 C+ U+ _
6 ]7 z8 Z7 M4 l) e1 H- G1 ?( c6 {& s" S9 r5 {) ~' y7 F, [
STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。可以把时钟信号输出供外部使用
5 c0 x" z; ~7 J& m9 N5 S5 系统时钟通过AHB分频器给外设提供时钟(右边的部分) 重点
2 _% I1 x, j1 [2 f/ ^; t" t! Q& A4 X" Q7 k3 `, w! Y
从左到右可以简单理解为 系统时钟--->AHB分频器--->各个外设分频倍频器 ---> 外设时钟的设置 3 \2 x* Q6 n9 x# v8 G7 @
右边部分为:系统时钟SYSCLK通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用: & F" Q# l8 {6 z9 b+ v! i
- K2 _( C& p9 h: r6 I& ^4 V! Y
①内核总线:送给AHB总线、内核、内存和DMA使用的HCLK时钟。
! \ |" G' S7 @6 V' X# z* {8 s
: ?3 E. y- e+ `: _8 c ②Tick定时器:通过8分频后送给Cortex的系统定时器时钟。
. ]& w$ y( Z0 T5 d/ K% P8 b. |; W6 z( i& p" K+ f5 l9 a- X/ L9 W |1 t
③I2S总线:直接送给Cortex的空闲运行时钟FCLK。 ' _( ?, @! ?6 I6 Z8 @
/ O6 W; x* N( ]5 u9 i2 F, K; D) N ④APB1外设:送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给通用定时器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2-7使用。 6 q9 y7 |' E5 P/ \
( ]" P* G* ]2 P! H3 }4 ?
⑤APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。 * t7 x1 V" O9 `- L& V, W
另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。
% u$ y* Q. w# x6 N) g3 n( O! M) v7 e6 S: m需要注意的是,如果 APB 预分频器分频系数是 1,则定时器时钟频率 (TIMxCLK) 为 PCLKx。否则,定 时器时钟频率将为 APB 域的频率的两倍:TIMxCLK = 2xPCLKx。
$ d9 ^6 G& q5 F, i, `APB1和APB2的对应外设F1系列# V! a4 x2 J( Q9 _) f4 C8 |
* t. H8 A2 `4 _: K
) b% N5 ?! ^) T m8 x/ F r1 [4 n
APB1上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、USART2、USART3、UART4、UART5、SPI2、SP3等; 4 P( w8 O* y! ]$ b: n+ Z2 }
而APB2上面连接的是高速外设,包括UART1、SPI1、Timer1、ADC1、ADC2、ADC3、所有的普通I/O口(PA-PE)、第二功能I/O(AFIO)口等。 : p) V e+ y* m/ o ?3 {8 Y$ J' @
F4系列
6 g/ O( ^2 e' ?) n
这个和F1系列类似,我们就举几个特殊的
. ^9 z4 ~4 g! Z( U+ A% ]+ s2 p APB2总线:高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11 UTART1,USART6 APB1总线:通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7 UTART2~UTART5 F4系列的系统时钟频率最高能到168M
0 [5 a/ p9 k. E/ t K, L) u具体 可以在 stm32f10x_rcc.h 和stm32f40x_rcc.h 中查看 或者通过 STM32参考手册搜索“系统架构”或者“系统结构” 查看外设挂在哪个时钟下,
, u% J1 t# {. }% x2 DRCC相关寄存器:
$ H* H6 C0 p+ R7 q1 j3 N
$ \: x$ H, _7 d9 S这里我们以F1系列为例
. ]+ u" _3 N5 {. R+ M+ Q4 W6 Y6 |* M: q& H
1 ^0 c, C# j' B; x- RCC 寄存器结构,RCC_TypeDeff,在文件“stm32f10x.h”中定义如下:
' ^* u5 ~) q9 k1 ]4 z -
' r) Z9 T( p; a; L# ~ {8 Q - 1059行->1081行。:
/ m7 u) p I4 m1 f- y2 g - typedef struct - `1 Y0 j$ N* Z3 X' ]& R, S# Z
- { 6 h" l. s X0 N
- vu32 CR; //HSI,HSE,CSS,PLL等的使能 / c4 k6 Y# |. Q, P
- vu32 CFGR; //PLL等的时钟源选择以及分频系数设定 % ]0 ~7 X0 f# W8 }& h: u: e" a& U; M
- vu32 CIR; // 清除/使能 时钟就绪中断 $ Z9 w/ i" R! N, q) ^) a
- vu32 APB2RSTR; //APB2线上外设复位寄存器
2 U9 y3 P: t7 o4 g4 T - vu32 APB1RSTR; //APB1线上外设复位寄存器 / A2 q6 `! H: ]$ _& b4 X0 t
- vu32 AHBENR; //DMA,SDIO等时钟使能 ; R6 W! d& J! i, I) a# w
- vu32 APB2ENR; //APB2线上外设时钟使能 7 v; O, U4 Y: j
- vu32 APB1ENR; //APB1线上外设时钟使能
# C4 b8 R5 N) e9 d. Q h - vu32 BDCR; //备份域控制寄存器
6 [8 k' E( f3 W- C - vu32 CSR; 5 w4 ~4 H' {& f" z
- } RCC_TypeDef;
复制代码 " N9 Y5 I& t$ `9 ~, I7 A8 K
. ?1 V' ~) C0 m3 o- v7 x
" i7 Y, c$ R2 M
可以对上上面的时钟框图和RCC寄存器来学习,对STM32的时钟系统有个大概的了解 其实也就是我们上面介绍的流程,理解了自然也就能写出来
" u% U) o( v% e2 D9 Y2 T7 a6 Q9 s# nRCC初始化:这里我们使用HSE(外部时钟),正常使用的时候也都是使用外部时钟 0 \) Z8 S& s: f# v& E& K4 R
使用HSE时钟,程序设置时钟参数流程: . K7 W+ e/ A$ P7 Q4 K: [# _% x7 e9 s0 ?
1、将RCC寄存器重新设置为默认值 RCC_DeInit;
, I; y) h6 F- [7 B2、打开外部高速时钟晶振HSE RCC_HSEConfig(RCC_HSE_ON);
4 |* E# _/ K6 o# s% O; F( i4 w3、等待外部高速时钟晶振工作 HSEStartUpStatus = RCC_WaitForHSEStartUp();) L8 w3 R! O; Q
4、设置AHB时钟 RCC_HCLKConfig;
" c L9 W. L9 D) V; |; i6 T5、设置高速AHB时钟 RCC_PCLK2Config;
6 S" P; M2 B$ z; V$ a, F. V9 @6、设置低速速AHB时钟 RCC_PCLK1Config;
7 M$ L* F8 v# g2 N2 a( n7、设置PLL RCC_PLLConfig;
2 C0 x0 D" {1 E8、打开PLL RCC_PLLCmd(ENABLE);
5 L, d+ ] I! z* B6 ]2 r/ l* _9、等待PLL工作 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
P8 X0 J6 P2 M+ V6 f! u0 \; b10、设置系统时钟 RCC_SYSCLKConfig;
$ l* z! Z0 w; Z m3 y; Y- C11、判断是否PLL是系统时钟 while(RCC_GetSYSCLKSource() != 0x08)
! h2 G5 Y; Z2 v0 ?12、打开要使用的外设时钟 RCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd() 代码实现:对RCC的配置函数(使用外部8MHz晶振) 系统时钟72MHz,APH 72MHz,APB2 72MHz,APB1 32MHz,USB 48MHz TIMCLK=72M
5 l' I- W; r6 O. K' `/ Y1 _) v2 C- 3 {8 Z$ a# u& V* _" @' F
- void RCC_Configuration(void)
5 v: G* c9 R# G; |0 Q - {6 k1 w$ n# v; E+ z( M# O! R( j( C. x& q
- //----------使用外部RC晶振-----------% U" h. M1 V$ M/ M
- RCC_DeInit(); //初始化为缺省值7 I+ c9 M2 @2 e1 L( m3 z
- RCC_HSEConfig(RCC_HSE_ON); //使能外部的高速时钟 8 Q8 L! X0 K3 Q" |9 l
- while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等待外部高速时钟使能就绪
+ F9 \5 C D- c4 k; D: K. R4 d - 4 X' U, Z' d0 j* u) s
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer
( f2 }6 T, y1 k - FLASH_SetLatency(FLASH_Latency_2); //Flash 2 wait state
$ K' I2 z+ P- |/ _7 G, P+ H - * x9 `, a2 }1 a2 m1 y$ u9 U
- RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK = SYSCLK! o) H R* j: L4 I* d+ c
- RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK2 = HCLK
; u/ C. c) o- k) B3 X - RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK1 = HCLK/2- ~, r; h! P) V5 h' s( S( Z
- RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
, J, d4 n. |6 y0 U3 ^1 ] - RCC_PLLCmd(ENABLE); //Enable PLLCLK
* G3 H; A. }4 k) `* ]" ^ -
( b. a, e$ m: ~; W2 @ - while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready- V- P9 L1 |. }$ `# L
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock7 B) |. F* {- H
- while(RCC_GetSYSCLKSource()!=0x08); //Wait till PLL is used as system clock source% G% \; Z* d/ G( g4 J
-
7 c% t" R' b& k( g; l - //---------打开相应外设时钟--------------------9 H% y: Y0 {8 q* f- ]4 h3 M
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能APB2外设的GPIOA的时钟 & w! b0 e9 M7 `7 z. X2 _
- }
复制代码 & A) e5 o* z9 |/ x V5 Y* A
也就是我们时钟树框图从左到右的配置, 时钟监视系统(CSS)
: o' G: A$ m _; c7 T# J0 E: Z4 F% ]/ V$ V* H# d
) ^: e# i' z' ~) h% H4 JSTM32还提供了一个时钟监视系统(CSS),用于监视高速外部时钟(HSE)的工作状态。倘若HSE失效,会自动切换(高速内部时钟)HSI作为系统时钟的输入,保证系统的正常运行。 $ {. K2 u5 c w6 U8 A# f
7 o" E! ~3 I$ f5 i
3 `/ j) H. m8 }& k; n6 T8 C* R$ C5 R) l
|