下图是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,进行倍频,也就是PLLN1)通过/P进行分频(PLLP)得到PLLCLK;(2)通过/Q分频(PLLQ),得到PLL48CK。 然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。 然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中 - <font color="#000000">; Reset handler
9 F3 ? G% u& \; V1 \4 p! r - Reset_Handler PROC) g1 D4 z6 c4 @, Z9 U% f
- EXPORT Reset_Handler [WEAK]- d: N3 c9 z6 \
- IMPORT SystemInit
m1 A! M1 ^& S4 O o# K) ? - IMPORT __main% n( H5 x" N# a7 O3 n2 \
- LDR R0, =SystemInit
: X9 d$ a0 |7 i) V. v, }9 n - BLX R0
. u! N9 G% s5 F! B2 W' B - LDR R0, =__main0 U Z- } a+ O( F, b' i8 B) R+ h
- BX R01 S3 S6 h% K3 n8 S+ _
- ENDP</font>
复制代码
/ o7 E+ s5 e) M- X7 f# t5 P代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。
- <p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">void SystemInit(void)
T' ^" a1 M$ _% H& Q% F, F+ Z - {( d& e& G: z( X2 c. C/ X1 ~! Q5 |: r
- /* Reset the RCC clock configuration to the default reset state ------------<i>/4 v" b/ }% g" J5 C% ?8 m
- /</i> Set HSION bit */: a5 E% _4 X. [& U; _
- 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 */
; e m7 Y# I" ?3 k - RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
v% j/ @0 z; r( L - //AHB prescaler 不分频
% Q# w6 @2 H* H) g) Q" j9 D - //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ. c3 P' ?3 N/ k; q' Y
- //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
4 P2 K2 J5 b7 O& Q& o- }# D - //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 */" D: |3 h E }2 t5 w9 w
- 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 */
( ]. R; W: ~1 Z8 G2 E* i+ O - 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 */! f6 o+ K+ W- x
- 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 */
5 l! l2 s- @: _* h: `2 S3 t - 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# j: b/ [8 [ v2 t( _
- SystemInit_ExtMemCtl();# L% S1 j- R# s
- #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,
9 C* n0 K' t5 R! m - AHB/APBx prescalers and Flash settings ----------------------------------*/8 W2 ?4 r$ o2 t
- 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>/. Z8 f) Y' M6 Q( r. @# U
- #ifdef VECT_TAB_SRAM
; C1 ^( i* C H - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal SRAM <i>/ j+ S+ }5 P: s3 g. ? @
- #else
3 C1 H: f6 `! j$ K7 p1 L - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal FLASH */
; v. K( M) l0 |) Q9 M - #endif
% @4 F# O( u+ P: m - }</font></font></p>
复制代码 ) y& V: m& C; U+ m2 D
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下
, l4 ^- y& p; {- _static void SetSysClock(void); ~4 V% Q+ s7 B. u, ]
{9 e- \; {4 |9 m: O9 D
/*****************************************************************************/
$ e+ k$ |$ e2 w: T/ PLL (clocked by HSE) used as System clock source /8 g3 c1 |- n& n2 @; {
/*****************************************************************************/
8 U8 f# z" C) c" B X; C__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* Enable HSE */- ]" J# F% s0 g! e
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口4 N/ t5 l# L) M" Z7 V$ A3 i
do6 U4 Y9 E' f% R) H. z7 q v
{
9 ^$ e) t5 P3 ], `9 tHSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好
$ E6 i/ u+ n9 f0 C" lStartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
% Y( f2 l1 j' m; u, T, a5 w} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的2 i# @( B# I5 }; |) S
{& @ `! i& n+ t& a6 Y
HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环
, t6 A5 ^$ K4 R" [}0 d1 U0 F0 z' K/ X* r: t" h
else) i+ I/ K; N. K# b3 I# ` ?5 M; s
{
$ p7 Q, K% t4 M# iHSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环3 q' v4 S0 L0 j
} if (HSEStatus == (uint32_t)0x01)
& _( A1 p+ P' L E3 e5 }- q{, L8 T: f& K4 F' J- M: ?. v
/* HCLK = SYSCLK / 1*/4 f/ r2 s& l. @; @; E% ~
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M - /* PCLK2 = HCLK / 2*/
2 p) H( \/ B" {5 e - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M2 ~: p) T% Y( D H, s9 u# F% a2 I
- 8 [: {3 j! {( j3 P7 {
- /* PCLK1 = HCLK / 4*/* n. O% \3 L9 j# F) W
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M+ r5 Z2 E9 \/ F3 H- }# j
- & O6 @$ J2 u4 ^* y
- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置2 Q, l3 T# G2 ~5 O% H1 K
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |) d2 r% Z" K$ J. s$ x5 R& S. W$ B3 S
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码//配置完后RCC->LLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
/ ^2 A' B$ A* c7 g+ T G//通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M
8 Q: H' F% P0 c: {" R. D//通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
% H4 f0 ]% P2 V! I& J//bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M1 X, ?3 w+ r- ^% n* T( l* H
//bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M. U2 K( w1 Z9 v
//bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M - /* Enable the main PLL */
& b1 |, B- w; N - RCC->CR |= RCC_CR_PLLON; //使能PLL
/ F7 N! E& K* g4 ? - V# i$ l- W) _- z: a
- /* Wait till the main PLL is ready */* t0 G7 p; }3 N8 b) K$ e1 }
- while((RCC->CR & RCC_CR_PLLRDY) == 0)
4 S, X B8 t& g9 W# s J - {
9 w9 Z _9 o; e' e" t& U - }
) S2 s. Y8 T/ i8 [! R% i! \$ z - //到这里RCC->CR已配置完,最终值0x03036783
4 V* p9 I& i- J6 c7 _/ O H/ M0 j - //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启
' h3 o5 M k* D# c! k% r' B -
8 x3 D5 g# E1 a - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
( ]. K+ t5 e5 J! s* D8 z: i$ t - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;) B( R; D( @2 @4 Y5 A7 X
' H7 V2 k+ V0 W& B- /* Select the main PLL as system clock source */
1 Q+ \$ @1 e1 ] c } - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
0 a( s9 D! q* t! q* e0 W - RCC->CFGR |= RCC_CFGR_SW_PLL;
5 e% L4 J8 R' }( p -
& b- I! f0 U& ]& p9 x! O- r - //到这里RCC->CFGR已配置完,最终值是0x0000_940A: @3 Z, V& p# l7 v) f- R
- //通过查看用户手册,知道,PLL时钟作为系统时钟即120M6 d3 ?% a: x. J) G, d
- //AHB不分频,即HCLK = 120M
; Z8 o' X6 M% A- L" D3 F) f- [$ z( ^ - //APB1 4分频,即fpclk1 = 120/4=30M
* b( O/ t. ?4 z3 I7 ^ - //APB2 2分频,即fpclk2 = 120/2=60M
! N' x# ]9 n' ]: Y% f - 0 ~2 H1 w7 U) r
- /* Wait till the main PLL is used as system clock source */* ~+ ]) Z: P1 e1 I. t& l
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
: B5 G4 y2 q9 \! P: Z; ?' U - {
8 O" ?3 Z* Z2 H+ j) K# ^ - }
复制代码}7 _* S+ ~" z8 J- N
else) |& A2 ~6 H4 x7 u
{ /* If HSE fails to start-up, the application will have wrong clock
2 j: f1 t* n: R" H. ~7 ?% E) w- jconfiguration. User can add here some code to deal with this error */
( u5 _% A, y4 [' Y6 L% A. v} }
) W0 i4 g' z$ ^ K+ U! |2 Q& P( z! q
OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。
4 F$ T( ^' u) W! o* c# u4 p& P |