有个项目要用到STM32F207Vx单片机,找到网上的例子照猫画虎的写了几个例子,比如ADC,可是到了ADC多通道转换的时候就有点傻眼了,这里面的时钟跑的到底是多少M呢?单片机外挂的时钟是25M,由于该单片机时钟系统较为复杂,有内部高/低、外部高/低 、PLL锁相环时钟,又有AHB总线时钟、APB1/2时钟,而例子中很少讲到系统时钟的默认配置是怎么配置呢?那么就发点时间研究下这个单片机内部的复杂时钟系统吧。 下图是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。 然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。
1 r L/ M5 p3 X T+ T% w; p: P. a
6 ?& I: e2 n' h# z- M+ N" G! r

然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中 
- ; Reset handlerReset_Handler PROC
/ ]% B# G* C# F3 G - EXPORT Reset_Handler [WEAK]
5 Z; M: L& Q2 c2 p( i - IMPORT SystemInit
1 h4 V2 s" Y& `9 A% v - IMPORT __main% |- o! [ U) l' p$ Z
- LDR R0, =SystemInit3 a4 H6 Q" A2 I% Q
- BLX R0
% t: @7 g, u* Z5 Q9 d) o$ D3 K8 |1 \ - LDR R0, =__main4 f% y# O, r: N; d
- BX R09 O6 A& b) D" O% w) J
- ENDP
复制代码
0 q7 k* W* d/ Q
代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。 
& e# z+ v$ C0 P: _5 A
- void SystemInit(void) W( E' c5 E r" w0 L! F
- {4 B' M, ~: L& f4 {* G1 L- E/ i
- /* Reset the RCC clock configuration to the default reset state ------------*/
; w1 X, x* Y# _. f3 a7 x; l - /* Set HSION bit */
! E! ^/ S! T0 P, S, j& M) x - RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ
* P5 b' a4 J- U - / `/ Y7 ~: E+ E- x. s, U
- /* Reset CFGR register */# q Q: g( }7 c. e: F% D9 s. g
- RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
- C, S8 k3 [+ I, r! `2 R, [ - //AHB prescaler 不分频5 k! N$ {, t; l: ]" R. z
- //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ
0 D( o* m, A& ?6 B. e - //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
7 K/ R% p: c2 I3 P& G - //MCO1和MCO2时钟输出等配置可参考用户手册Page95
7 i( K; v% L$ ~, q& h& e* \. a - K! M: K- @1 H) x4 z
- /* Reset HSEON, CSSON and PLLON bits */' G8 w. O' Z0 i4 @- A: b
- RCC->CR &= (uint32_t)0xFEF6FFFF;
" b( _' E P2 A6 ` - 1 F) l* i% O# d# w3 O6 M3 B0 x
- /* Reset PLLCFGR register */% j3 m8 s/ v! R% ^0 o
- RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010( l/ \/ B. N8 ?5 |0 z' C
# O/ g/ U1 z( O$ {% V6 S4 |- /* Reset HSEBYP bit */. M$ a% K7 {0 k+ B3 v
- RCC->CR &= (uint32_t)0xFFFBFFFF; //对bit18 HSEBYP 设置为0,外部高速时钟被禁止
7 v& o5 n, B9 v/ b- ~ - 4 ?: d' e4 ?( _0 @
- /* Disable all interrupts */! X1 E3 ~3 D; J/ m. l2 h
- RCC->CIR = 0x00000000; //所有时钟中断都被禁止 k1 M( X0 ~5 b
- * O7 N6 x- J |% @
- #ifdef DATA_IN_ExtSRAM
4 \- q3 U- }/ |7 _5 S7 L - SystemInit_ExtMemCtl(); 6 M8 @# {/ o. e8 Y* g3 \8 l' x
- #endif /* DATA_IN_ExtSRAM */
0 L' R @( s5 h. `7 n. w - 3 U3 L6 u, |. ]* H) R G
- /* Configure the System clock source, PLL Multiplier and Divider factors, - w o0 n" Y2 }# S) w
- AHB/APBx prescalers and Flash settings ----------------------------------*/3 R3 s. v, z( m: O- z
- SetSysClock();
7 f/ |4 F( u, [& U - . C3 H$ J8 K; Y6 P$ t
- /* Configure the Vector Table location add offset address ------------------*/
0 ]0 W/ K5 ]5 w8 B; a; C! R9 n - #ifdef VECT_TAB_SRAM+ {" @8 v/ r( ~
- SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */' z% ]3 c$ E$ x4 N
- #else' V, ?2 \$ @& H- t/ n
- SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
1 Y8 n' D5 ?$ y% d - #endif
7 t. `/ k- O9 f! U. p - }
复制代码
, ~* R' `# D- y. ]# a8 r7 T3 z
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下 - static void SetSysClock(void)6 ^3 p- e: F4 @7 _& Q
- {
! ~( i, s6 M# B+ Q - /******************************************************************************/! Y. V- Z* Q" \/ X* |; M+ i% k
- /* PLL (clocked by HSE) used as System clock source */, K2 z) {# e+ {4 }
- /******************************************************************************/6 a5 V3 a9 E6 }# g: @2 a5 N8 L
- __IO uint32_t StartUpCounter = 0, HSEStatus = 0;6 a$ w) I1 p W J" ~8 S' j! U3 ^5 B
- M, [" H$ W* B6 {
- /* Enable HSE */
( @- L) ?8 {8 B' `; A+ R* F - RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ; z* |: Q" ~) N- e4 R
-
# P9 r' i: G! B- a2 n- ]/ E( B7 z: {0 \ - /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口3 `( t$ N; x) t
- do
0 H) E# }, A4 }% V2 S. m - {
0 {4 B) k6 k! X5 [ z% W - HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好: z3 c( A/ g- ~ i! s' ~" T
- StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
$ ?* D( L* N3 D - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));$ y; }" p+ v7 W; g; V- |" ]
2 n& K" a, a) E) R, C7 l- if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的
, L2 I. z$ U2 C - {6 g) Q/ U2 k" }5 A. K/ s
- HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环3 b# k( r) l# ?1 Z. {
- }
: G4 }3 P+ e, V0 n - else
- B$ r- v& c- t+ y - {
) ^* ~$ k6 \% f5 B/ ]4 ~ - HSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环
' h/ m, g% J* g7 ?& J' C0 \' G, ]: x - }
$ u. f/ C0 _( i: D" F: w6 m
5 x0 @9 B" h- g7 C4 Y- if (HSEStatus == (uint32_t)0x01)
% {# p, o% `/ G+ O - {
: m$ O: w2 W! s& N9 F" ^ - /* HCLK = SYSCLK / 1*/+ j1 _8 ]7 m6 N/ k5 C, g" N( m
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M
/ l+ C$ Y6 W/ k* o9 \; ?3 a" F0 | -
; p2 g0 |- H% w. i - /* PCLK2 = HCLK / 2*/
$ J7 o! z) F! P, W. ]8 C$ Y D - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
' p7 i6 J. E; Y' t# | - : l7 s0 `6 m! ^) J+ F
- /* PCLK1 = HCLK / 4*/
& P4 c$ h5 F) v2 C) m - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M
8 }5 u1 b7 w* o7 L8 }: H7 k - ; o( ?9 `" s2 N
- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置! C/ ~9 J# ], }* {# t: m) E6 O& L9 e
- RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |$ X s) f6 T2 @6 Z) m4 k; q
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);: s6 s/ z, F; w; j" k8 n4 J
- //配置完后RCC->PLLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
1 |7 M% ^% R: r* E) b$ s - //通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M: ^7 |$ z( M1 T6 ~
- //通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN1 c* M7 p6 S) ^8 }, K& M
- //bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M. y, j3 E: S4 [. `4 ^
- //bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M
\# r; R* Y& ^6 z9 X$ b& ? - //bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M
9 S, c6 y* d$ s( M+ M" Z" h1 }& } -
) m7 v$ g; ~% p+ v- r8 @ - /* Enable the main PLL */. X h5 {# s8 n
- RCC->CR |= RCC_CR_PLLON; //使能PLL
! x; B' S, t& {/ S4 {. A: E% g# g - / j& Y9 I8 r i i U
- /* Wait till the main PLL is ready */
$ F! k9 }: Z( R A- I+ ?" H: E; h9 l - while((RCC->CR & RCC_CR_PLLRDY) == 0)
. T1 |& I7 Z5 Y5 } - {
6 z9 i. v: F0 u/ M% s2 F# x$ u - }
4 z- u$ Q8 A V$ P$ L9 J - //到这里RCC->CR已配置完,最终值0x03036783& |9 s9 m. _) i7 @
- //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启9 b* o% B* } P2 ~* d b# F* X
-
) D! r- w) R& B, b! a - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */) i6 S. f& N% V- a# R* v. s6 e
- FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;
' `/ ^5 ~# w2 z& l L3 T/ d0 y3 k1 ? - - p2 M+ @0 {7 M: |
- /* Select the main PLL as system clock source */
2 q) U4 C0 s3 V' R - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
5 c s M* {- S1 i - RCC->CFGR |= RCC_CFGR_SW_PLL;2 t' s, S+ h, ^& B6 s
-
e. `- A- _1 v! w d% I$ l& Q - //到这里RCC->CFGR已配置完,最终值是0x0000_940A" e8 X, h& R! ~! D
- //通过查看用户手册,知道,PLL时钟作为系统时钟即120M% Z Q/ C: M% y$ u
- //AHB不分频,即HCLK = 120M
) G4 o8 g' z5 m. C5 F6 Z5 u3 p7 C - //APB1 4分频,即fpclk1 = 120/4=30M
5 }3 L! D- M, _3 G - //APB2 2分频,即fpclk2 = 120/2=60M
' M+ r* F6 z/ y: O - $ G$ p" @3 U/ _" h
- /* Wait till the main PLL is used as system clock source */0 A# o1 X2 ?" R! g, L/ v# K: g, f' t
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);# t8 O% E; X, F* }9 j
- {
) {+ R' i8 m' J4 ? - }1 ]$ ^8 `) W: ?
- }
+ U5 l* A7 k; h B+ u5 Z - else/ N4 |) J. `( Z
- { /* If HSE fails to start-up, the application will have wrong clock; G/ Z! S. h. x
- configuration. User can add here some code to deal with this error */0 Q9 {( J2 }* T4 ]$ a9 {( W
- }. Q3 w/ Q1 v \
- 3 ~. [5 v9 ?: k5 j! k _" Q. C
- }
复制代码- r6 n, k7 |% N8 E" x


OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。 " k- H! i9 ^6 B
' A; s3 x. s9 O- U. ^
' f5 J: E+ l* E- f) T3 I
2 ^4 f' J% [4 Z5 g! V# I
|