下图是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
& A# p6 g$ ]7 x: | @ - Reset_Handler PROC
) H7 J/ P# M. c - EXPORT Reset_Handler [WEAK]
$ [' W+ e. G$ w% U! F( ` - IMPORT SystemInit$ \2 o6 t1 g9 o% o
- IMPORT __main
1 \+ Y6 R( y* Q2 T - LDR R0, =SystemInit
' P4 u* g4 r" Z: c - BLX R0
- _' B: {, ^2 V - LDR R0, =__main# h( i& ?1 B% `9 e1 ^0 Y
- BX R0: a' a0 T- I- j) U% \" g, `
- ENDP</font>
复制代码1 b) Y* H2 Q! I
代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。 - <p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">void SystemInit(void)
& N9 \6 g& L9 p( K1 k - {
5 ~9 ^" T1 p7 ]' i* I1 X& y$ p* k - /* Reset the RCC clock configuration to the default reset state ------------<i>/
0 [; P( C( N0 e: x; @ - /</i> Set HSION bit */
! U- H( s6 |8 o+ e - 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 */
4 S8 g; k3 i) `( N$ y, r - RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
& m" g# g3 y5 k7 C2 ~6 L) P. V6 | - //AHB prescaler 不分频
$ O3 [/ y+ F( n - //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ
( ^* F' r* f/ _, c; _3 N0 {0 ~ - //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
1 t z; J" Z) v2 C5 g: k) b: S# T - //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 */8 S5 F& i$ A1 r, C
- 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 */3 Z/ R( O6 l+ B _* X( U0 R
- 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 */
8 Y$ u; K. Y* k* n! j - 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 */0 c, b' _- q' A
- 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_ExtSRAM9 k, G; H! o+ \( r" l" P5 o+ o2 O3 w
- SystemInit_ExtMemCtl();- Y2 M- X2 H/ J" b& R3 N
- #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,
! f4 ^; s3 t+ I - AHB/APBx prescalers and Flash settings ----------------------------------*/8 e( v6 z* e8 S" B
- 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>/4 C0 Q5 M9 Q) V1 c$ {8 s
- #ifdef VECT_TAB_SRAM& f) I1 r: C+ o! T3 t2 q
- SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal SRAM <i>/
5 t/ y. C, K: H - #else
$ R- V0 y, T8 I5 D& X - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal FLASH */
% q6 C/ o1 r9 C5 \# o( a1 ?7 Q - #endif- @: J' I; ?* k# Z1 I
- }</font></font></p>
复制代码 6 i$ F$ `: a0 r* {3 r ?) |
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下
9 M) F* Q+ t0 O, t* G" c4 @static void SetSysClock(void)3 l/ Z* }5 V- R
{6 r' K. O: v$ V% ?- `( ]& Y& L0 @/ G3 ^
/*****************************************************************************/6 y' r- z" B8 c1 ]
/ PLL (clocked by HSE) used as System clock source /
1 x5 i! ]2 L0 ?# n" e% u- Y/*****************************************************************************/
* j; P# \0 `2 a L7 h0 ^' E+ z# I__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* Enable HSE */5 `; g/ f1 v/ J. r- N) {
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口
7 P: N9 [2 E: O7 t, hdo
: L4 ?$ j, s% T b{
1 l; ?3 }9 e7 FHSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好' I# O) ]" ]3 [4 T" n2 g
StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时6 d9 _7 O' [7 A9 w3 g3 X
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的% ?+ G! t' |4 M: j7 c- ]; ~; q
{/ \7 `5 ~/ v0 q
HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环
9 ]$ F" C" _+ X6 T- J$ o}% l) [- j9 I7 R8 _: A
else* [; M/ u6 Z5 |. j1 n
{
2 S4 x4 ? `5 O+ Q& kHSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环
. c4 }& J5 H& _7 e& M# n& r} if (HSEStatus == (uint32_t)0x01)2 L; \8 C/ c6 q" @+ G- c
{
/ N0 R1 I& c( I- V9 h+ d1 w5 _- O/* HCLK = SYSCLK / 1*/1 ^: M3 [/ E% t, R
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M - /* PCLK2 = HCLK / 2*/
/ N' J) p y1 M( {, E - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
4 o- G+ W- w2 Q6 F' N' }+ ]: D - % @- \5 B9 F6 a0 G9 ?6 z
- /* PCLK1 = HCLK / 4*/7 D2 L3 z- _ V3 u4 B1 \
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M- g- C+ _6 i9 o) k" t- [6 s
^4 r/ @( p% |! l$ w c! c- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
- n6 ]2 F5 h) m7 T; L - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |3 k& f' c/ _; b
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码//配置完后RCC-> LLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能$ C1 a P* U0 y4 o. }5 }/ f* J
//通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M- |7 ?5 e5 _! u+ t# }
//通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
" }# ^+ l! ?9 I' K2 P# j//bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M
1 [+ q# } Z) j//bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M
9 F3 J# g6 d: g* G5 V3 C3 }8 ~//bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M - /* Enable the main PLL */
. O0 K" C8 n: P$ u" r- N" S - RCC->CR |= RCC_CR_PLLON; //使能PLL, l5 }3 p: e8 L, i* p- d
$ g7 w/ C" n/ D# |0 Z- /* Wait till the main PLL is ready */3 J1 V9 _) H# r- k& P8 K, p Y1 p' s
- while((RCC->CR & RCC_CR_PLLRDY) == 0): |/ n& A2 ?4 g" |; E9 f
- {
" y8 I# `6 q+ n7 A" G - }! G: o/ U7 r6 [2 y
- //到这里RCC->CR已配置完,最终值0x03036783
8 z( T4 N' G1 } - //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启2 J+ e+ g0 g; o* P
-
* ]; n3 R9 ~7 i. |1 t: } - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
7 R9 z/ S3 D! R W! r - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;
! W0 J9 P* C) L( i - g* Z7 C, d( M: c
- /* Select the main PLL as system clock source */
$ Z) E% Z0 u$ J1 R: t$ p9 l* U - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+ [9 c- L6 ~. d8 M- H2 D - RCC->CFGR |= RCC_CFGR_SW_PLL;
8 Q; }- Y, v1 R5 d" X5 \ -
9 _% _8 D0 @* y y" _* O - //到这里RCC->CFGR已配置完,最终值是0x0000_940A
9 u& r/ }. L) ~2 b/ t4 Z& i1 L - //通过查看用户手册,知道,PLL时钟作为系统时钟即120M
& `" V7 ^& S- G& c1 h8 H+ ^& I - //AHB不分频,即HCLK = 120M7 u0 x# P) [: X. X1 u
- //APB1 4分频,即fpclk1 = 120/4=30M
/ p3 }4 j+ r7 {( {: y0 ?. T8 b - //APB2 2分频,即fpclk2 = 120/2=60M
+ Q/ \* M; N; j8 I4 `9 E! K - x! r) T0 y @ _0 l
- /* Wait till the main PLL is used as system clock source */
( O/ e9 x* r8 P( o: ]9 L% h# u - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
1 b1 P/ J" }/ m; w" C C - {( `1 y. Z; o8 v! F4 M$ E
- }
复制代码} b$ r: J2 Y0 L! k
else
& k% h( G* B7 B- I2 A: m9 s3 l{ /* If HSE fails to start-up, the application will have wrong clock; J% Z3 L, _+ J- Q$ p X, t
configuration. User can add here some code to deal with this error */
1 B" A; {0 ]# l/ @) S ?} }- I7 o8 W1 M% g
* V, f: Y! b4 F5 X5 P" @ ] OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。 7 n4 a* |6 @( G) v) D: X
|