|
下图是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$ ]5 Q* Q# s2 V2 Q* l, y
- Reset_Handler PROC2 ?" B& P0 t7 P) ~& r5 e1 }
- EXPORT Reset_Handler [WEAK]# m( z* e5 G7 ], b4 _
- IMPORT SystemInit8 r4 J b0 H' B& V3 ^, {
- IMPORT __main
q$ J! W8 u2 J- ^( ~4 ~% [ - LDR R0, =SystemInit% L& w' e2 i' c# K" q% k1 }
- BLX R0# ^. p3 n- }( _8 t
- LDR R0, =__main8 c5 W- q8 n5 v9 s
- BX R0" m. I3 u* u8 o3 l9 V
- ENDP</font>
复制代码
& @ C( ^' A4 j2 } R代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。
- <p style="line-height: 26px;"><font face="-apple-system, " "=""><font style="font-size: 16px" color="#000000">void SystemInit(void)
5 u' q ~8 z$ V: {9 U - {
7 W+ o0 a* O/ Y6 b7 B - /* Reset the RCC clock configuration to the default reset state ------------<i>/" R( T2 X6 q/ v- V2 ^9 G
- /</i> Set HSION bit */: g* _; M/ }+ o8 }& }; H
- 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 */0 k/ [" j! ~. W, }% `' A
- RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
. y2 N" f4 S3 g1 o# ^ - //AHB prescaler 不分频
: }3 d) H2 g* p5 y1 g, N - //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ1 g; N/ p" m- z+ f+ i* ^) b2 M
- //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
; y1 c1 n/ ?6 o. l - //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 */: U1 A& n; e# Q: p/ ?
- 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 */
$ d7 C7 b& }) p$ o* A5 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 */
/ @( _0 R3 ]' P* Y. M - 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 */
3 R0 z! E' g, f; N- d8 ` - 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
- @$ O) b# M. W# z - SystemInit_ExtMemCtl();
6 P- T8 r5 k7 m - #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,! Q0 X/ r! z$ w# X7 T
- AHB/APBx prescalers and Flash settings ----------------------------------*/
% \% q) r& W$ L; v1 G' d# S; q+ ? - 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 Q/ X! R% Q* G+ ~: j; H - #ifdef VECT_TAB_SRAM
5 p: _) b: R1 v4 | - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal SRAM <i>/
3 A, b+ |" C5 l. a - #else
6 a# \- h: I; _6 s- m( e. w1 B - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /</i> Vector Table Relocation in Internal FLASH */
7 W y: b( k1 Y) s$ u( p/ B - #endif5 f1 r" S0 r5 ^1 G+ b' E3 K. x' n! v, a
- }</font></font></p>
复制代码
' O7 u( F: ^7 X* }+ S在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下
" n3 M( i9 Y o9 q$ [ A7 ustatic void SetSysClock(void)7 q- @3 e g% [2 f- B
{
0 r2 y1 S; n1 e8 ?4 b$ ~/*****************************************************************************/) R+ r" R, T! ?6 x# x# S1 ^
/ PLL (clocked by HSE) used as System clock source /2 B& U. s) M' ?* V% U
/*****************************************************************************/
0 J% |# W" X5 Z$ ~ a C__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* Enable HSE */
* U/ P1 \3 b# G2 i, `- PRCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口( C8 O" \8 Y: x, r) y8 @( ?7 n
do, h: o5 \# d7 s
{
$ c5 W8 m6 b8 |/ u6 [1 m: ^HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好
- ]2 l" E: I* x$ v: B6 fStartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
+ @* s5 R* Q7 L7 `" M2 s} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的5 }* J, G2 ]: F' e4 m! }5 i0 K; P4 R
{$ F. T9 G* [7 M9 A5 W1 t& ?
HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环, M i/ u z6 p1 d
}* P( B: r5 f. P% ~# k, ]$ G" g
else
|% l- L" F% T* x{# v$ N6 `* G4 e3 W3 V
HSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环$ F+ `4 ]3 E, a! m7 k7 E$ E
} if (HSEStatus == (uint32_t)0x01). A, t+ a; P% l
{
8 o9 {1 k( o. _% I1 ^% u& d/* HCLK = SYSCLK / 1*/
d. b5 B6 i0 W# h5 bRCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M - /* PCLK2 = HCLK / 2*/
2 j8 ]7 Z6 a2 C# t( j0 G - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M% q7 O/ |% R- s
- ' J/ K4 X; o8 a5 e! O6 p+ Q5 F% N
- /* PCLK1 = HCLK / 4*/5 e* p5 M: |0 h4 x
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M
* N( b: ~ k" t& L% b - ) N! c- i, ]' w+ v4 H9 z8 y5 y# V
- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置7 u# T9 ]8 J- e. k
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |& U+ @4 B+ u9 {8 D% v p$ S0 `
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
复制代码//配置完后RCC-> LLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
i3 c' Z% M3 F' s% J& t3 P//通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M
3 x: }' \9 ~- Z* r" D//通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
2 ]$ m' M1 _4 L0 b: F; R//bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M- ^: \& G+ ~+ a4 q+ Y
//bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M# e3 L! t: z6 t
//bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M - /* Enable the main PLL */8 c" R5 R$ S3 F! r. q! [
- RCC->CR |= RCC_CR_PLLON; //使能PLL* _1 {/ _6 P7 `, g
- * L8 o1 i2 w4 d! S1 _
- /* Wait till the main PLL is ready */
N5 G2 W7 k/ P; \, Z, D- a - while((RCC->CR & RCC_CR_PLLRDY) == 0)
; X4 E# n( t6 C( x1 ~ - {. x* |# l( R2 k0 W) @/ s
- }
# n4 I: }$ G. n% P) j - //到这里RCC->CR已配置完,最终值0x03036783& f3 a& l8 F4 L9 {
- //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启3 ^6 ~* D" z% Z$ H( [6 z3 p
-
% `4 U6 { Y: r0 p% D - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
8 N2 d+ Y U# ~0 f3 z# }" w - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;
" W" l) Y, |+ l: M - 1 n& U# S4 G; A# u' g1 F
- /* Select the main PLL as system clock source */! H. b. X4 g' J* _6 @8 m0 w
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
0 H) e- P: E8 F; n3 T9 m+ f0 W - RCC->CFGR |= RCC_CFGR_SW_PLL;
! ]1 M- ~) F: s s4 G( I; ~ - , [) f2 t, }& Z/ p2 q
- //到这里RCC->CFGR已配置完,最终值是0x0000_940A7 L, ~. x2 b: e) L7 N! W+ X0 S
- //通过查看用户手册,知道,PLL时钟作为系统时钟即120M" J j9 a4 _& ]" H& D& D" O( Y/ ~; p
- //AHB不分频,即HCLK = 120M
: E3 S! w0 @+ i4 g3 u. T8 N- \$ G - //APB1 4分频,即fpclk1 = 120/4=30M4 o) Y/ y2 H+ D' v) ^0 \* @/ _
- //APB2 2分频,即fpclk2 = 120/2=60M2 r; n% W7 f3 G2 {, A
' \1 e) R+ O4 e+ _- /* Wait till the main PLL is used as system clock source */, w o6 p. E+ `' o4 Z0 S
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
' e/ h- @! W* a' h8 j* X$ K8 b - {
' I$ @5 R! C/ R - }
复制代码} H% B. a; S# ~8 _+ T4 Y
else
+ E$ B. b6 Q7 d4 l) t; O6 F1 L7 j{ /* If HSE fails to start-up, the application will have wrong clock
: k+ G2 p1 D1 u+ C( g6 I8 [' {9 fconfiguration. User can add here some code to deal with this error */! m% R" _0 z6 S% F: E) v1 @; e
} }& ]: E$ o7 o s! ~+ l
9 M2 x# Q8 }& w OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。
$ H X! ]5 e; d$ z# C C0 q5 M |