|
下图是STM32F2系列的时钟树结构图: 1、内部高速时钟HSI、外部高速时钟HSE和PLL时钟PLLCLK时钟都接到了SW开关处,通过SW选择哪一路作为SYSCLK,SYSCLK经过AHB分频器进行分频得到HCLK,APB1和APB2是挂在总线AHB上的,通过APB1和APB2分频得出fpclk1和fpclk2。 2、PLL输入时钟源主要是靠外部高速时钟和内部高速时钟作为时钟源,通过PLLCFGR寄存器的bit22来选择具体哪一路作为时钟源。选择好了时钟源进入/M分频器,也就是PLLM进行分频,送入VCO,在通过xN,进行倍频,也就是PLLN 1)通过/P进行分频(PLLP)得到PLLCLK;(2)通过/Q分频(PLLQ),得到PLL48CK。 然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。 然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中 - <font color="#000000">; Reset handler) \# G: O6 U' L/ F& e
- Reset_Handler PROC) q6 X) _/ B! S4 c( {9 q) }: ] _
- EXPORT Reset_Handler [WEAK]( u) _; W5 Z6 U9 U+ s4 c( i
- IMPORT SystemInit
( `6 Z& `. Z' }2 k - IMPORT __main
0 o( M0 r% |( K) n# E+ f7 \ - LDR R0, =SystemInit+ K3 S' y+ b9 F% |) Z
- BLX R0
" P+ T, P+ ]9 [: x# E) n) Z4 y - LDR R0, =__main
/ F8 n8 I7 _5 E x - BX R04 Z& H* U) x9 c1 t
- ENDP</font>
复制代码
j0 l) n! K3 u" _3 k代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。
- <p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">void SystemInit(void)
6 V& p( V: r! I' ]9 F1 _2 @3 [& N - {
4 q/ `) m! _. o1 j+ u# `( G* B - /* Reset the RCC clock configuration to the default reset state ------------<i>/
; m' Q$ c" g+ Q2 V - /</i> Set HSION bit */
0 B7 F G0 y5 o/ }) O - RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Reset CFGR register */
/ X, s( e# ]% W, \( X - RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
2 k1 B! |1 V& L, W - //AHB prescaler 不分频) h- R' r1 z \$ c
- //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ% T/ W R- B/ x2 r7 U8 I9 K5 J
- //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ9 k' H; ^ n* i) q. x
- //MCO1和MCO2时钟输出等配置可参考用户手册Page95</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Reset HSEON, CSSON and PLLON bits */; Z& y6 o4 E) r6 r0 ~6 \- k( t+ h
- RCC->CR &= (uint32_t)0xFEF6FFFF;</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Reset PLLCFGR register */
" p% h1 \, N! O; y$ x' d - RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Reset HSEBYP bit */$ T( W+ ]& q4 V# c( @: F
- RCC->CR &= (uint32_t)0xFFFBFFFF; //对bit18 HSEBYP 设置为0,外部高速时钟被禁止</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Disable all interrupts */
! S+ y2 @4 p L - RCC->CIR = 0x00000000; //所有时钟中断都被禁止</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">#ifdef DATA_IN_ExtSRAM
6 Y# \! l2 i5 ]7 h$ d - SystemInit_ExtMemCtl();" g8 I( n5 g0 P
- #endif /* DATA_IN_ExtSRAM */</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Configure the System clock source, PLL Multiplier and Divider factors,$ y8 ^6 u- \6 Y" k1 K& _
- AHB/APBx prescalers and Flash settings ----------------------------------*/
. Z( A7 n' E& Q& ^: x9 |1 _ - SetSysClock();</font></font></p><p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">/* Configure the Vector Table location add offset address ------------------<i>/
2 A* r% g4 B# R$ C& c! U- l2 R - #ifdef VECT_TAB_SRAM
' |& ]; ?: P8 n5 M. j - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal SRAM <i>/0 t1 M. M4 h+ v: W2 Q% ~
- #else
, ]: k* J! r% T% c- k5 G% ]2 h5 x - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal FLASH */
8 g) p: Z X* i0 @. m/ B - #endif
1 d$ C" j, R0 c# a' P( v6 _( S- k5 m' P - }</font></font></p>
复制代码 4 e0 q+ B3 }4 W& k
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下 6 H9 p& G% N2 }- X$ F
static void SetSysClock(void)
- b, A% I; ^2 V2 u5 I{# @1 Y4 i' N9 k1 t
/*****************************************************************************/! ^: {! g5 G, `6 {" q6 Z
/ PLL (clocked by HSE) used as System clock source /) C4 i) d v' X3 ?( A( s) o
/*****************************************************************************/
; F" r( s' U3 T1 n9 O__IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* Enable HSE */! s& |" p3 F0 `
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口
0 u, t3 w" a; Y s. w/ C7 @/ U- Wdo
* ^- M J% }: N4 ], _{
: v8 u9 {9 m% @5 H% s8 f; U0 fHSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好+ S0 f1 P; Q8 o' E
StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
( H( C! P& S" {$ A1 L9 ^} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的
( z: [2 y. h5 R' x$ v. `% L9 l{/ @, E2 v2 T" V0 V
HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环
: p% s* B) I M8 ?2 r# D. \* h B C}
1 V8 Y; G, Z, _+ ~, A% s) Uelse
: n' N `7 A% i9 @/ p- |6 W* V{
- L: D7 g9 d, o% R1 K) X- tHSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环
" T4 ?7 ^& N! Q2 e} if (HSEStatus == (uint32_t)0x01)
- q$ ^! F, G' O' E- X; L6 V# [{
* i2 ^; V* S5 o- X+ y/* HCLK = SYSCLK / 1*/. u' s9 J! {1 c# g
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M - /* PCLK2 = HCLK / 2*/
- O' ^' Q+ k/ \9 J( q' z$ d - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
( L/ v1 F _/ I. @7 J - $ ~- x! j( u* ^. {
- /* PCLK1 = HCLK / 4*/
! x1 f, @# g, t8 R - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M
' O6 g- M: T* s/ Q1 e9 r
8 }% Y+ N( E1 E- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
7 V2 U; d6 i5 ^& r - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |+ ~: s* ?2 A. h" h2 p$ w* ^5 l1 o
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码//配置完后RCC-> LLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
" d; s& g$ y5 W6 G1 c$ H- L//通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M
5 e" z& D- S+ C2 H, W( P//通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN# S3 [" y5 L+ J4 @% U
//bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M4 X% O0 ?1 k) R0 [2 Y
//bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M' Y) o+ I* [; E) {" }
//bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M - /* Enable the main PLL *// y% w3 O+ l* P
- RCC->CR |= RCC_CR_PLLON; //使能PLL' p, L3 O5 W2 }5 ^( Q3 f9 x
/ j- _2 s8 \3 Q, u" }- /* Wait till the main PLL is ready */
7 f* K) g6 ?' f- s+ _ - while((RCC->CR & RCC_CR_PLLRDY) == 0)
7 i/ A" q& i7 H' ` - {3 m0 e3 V1 i4 c
- }
! a5 q( Q( A* P$ d$ l2 B* U - //到这里RCC->CR已配置完,最终值0x03036783( @7 F( T3 Z; O% \; `( a/ u) Q2 j# u
- //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启; a3 O" d6 Q8 H3 L
- 7 T( ]& K6 W' A
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
! J+ z, L+ d: S6 Y2 H J* X& K8 H - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;/ P0 \2 N, q/ c' w
- : j# x+ X. O( g7 D5 f' V! T i6 e* M
- /* Select the main PLL as system clock source */
! e; w7 \, j* s$ A& u: `* K - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));) r3 Z& E+ W Y5 U
- RCC->CFGR |= RCC_CFGR_SW_PLL;- p7 X$ i, u2 E" L, ~: u7 l
- - v8 e0 C1 a/ m
- //到这里RCC->CFGR已配置完,最终值是0x0000_940A) z( [ p( G& f& z
- //通过查看用户手册,知道,PLL时钟作为系统时钟即120M5 b! z* J, _! y! L. e1 K# _- W) w6 K
- //AHB不分频,即HCLK = 120M$ q. `4 A& r/ j$ I- V! g f& }
- //APB1 4分频,即fpclk1 = 120/4=30M
: K3 W/ ?4 Z0 s6 {% m$ c - //APB2 2分频,即fpclk2 = 120/2=60M& Q2 y) {% I8 u) M
( I' r" e2 n9 ~" f0 k- /* Wait till the main PLL is used as system clock source */% ] E. _. t W: r3 f0 o" H
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);3 j" n) _4 C7 R, ]4 _3 z$ `4 e# J
- {* `* q$ t. m* f- t
- }
复制代码}" Q a/ ]4 h& D
else) g+ j- e, X% \2 }' A6 l( t, E9 u
{ /* If HSE fails to start-up, the application will have wrong clock6 a- Z/ E; z/ r# O0 v9 x/ m- W
configuration. User can add here some code to deal with this error */. c G# B2 ?8 y' K% }
} }# s4 ]& B' \8 V4 `" |# ~+ h8 o
- u$ ^$ s1 H G- N. N* v
OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。
- R* D: M8 L% Q, J% I% r |