|
有个项目要用到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。 然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。 , f* L$ y# A8 [2 M6 a9 |
5 @& D* @. @4 }, P; D

然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中 
- ; Reset handlerReset_Handler PROC
% G5 s9 D3 q0 {4 ]6 h5 y5 o - EXPORT Reset_Handler [WEAK]
( F f# z6 O4 W* e9 r$ x - IMPORT SystemInit! x5 E) O; n( X/ U
- IMPORT __main
b/ b o' \& E0 ^ - LDR R0, =SystemInit- _2 n; Q- m$ o9 }' Y9 w% B$ j2 A
- BLX R0+ T# s$ g& w0 i3 [7 o' @
- LDR R0, =__main, q5 D' P: i" c/ ^
- BX R0 t$ o% L( e, I& w5 D8 x
- ENDP
复制代码
& r1 r7 t1 O7 y+ W; \
代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。 
% z. s! O7 U& H4 I: {. \' T
- void SystemInit(void)# P* s$ j( J% i; p# Y c! m6 W
- {
% N& e% x! O7 D9 |/ M: f3 i$ q - /* Reset the RCC clock configuration to the default reset state ------------*/
' x1 r# P r; ?0 n' E. U- [4 H+ C - /* Set HSION bit */
# {1 G8 f' Z l6 D7 N - RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ) W6 w& M6 p. h# a [% G, m7 b
0 ~9 w* `1 v% I8 j* A9 ~- /* Reset CFGR register */
- V& L2 D$ E) ]2 C2 { - RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
$ ^8 ?2 m) g/ L B - //AHB prescaler 不分频! y% P% [! X* ~& q }5 I( P
- //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ4 z: s5 _" ]9 x) v* ]) k7 O
- //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
& P9 @, J( E& P1 s; t; ^4 y - //MCO1和MCO2时钟输出等配置可参考用户手册Page95- R% C# A! a7 g
- 7 y! y) W( k3 P9 ]) ~3 u( i
- /* Reset HSEON, CSSON and PLLON bits */
' F+ Z. y5 \( O. p7 K# q a* f% h - RCC->CR &= (uint32_t)0xFEF6FFFF;
- W n8 A6 \- Z8 ] r$ \+ p5 @% H
$ n0 d' I% G2 O! Z8 Z4 C- /* Reset PLLCFGR register */: C( r& l6 L* d; I5 T
- RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010! o+ J, D9 A8 w4 e
' A: Q6 }3 i& M% Z- /* Reset HSEBYP bit */
: O2 l: k3 ~0 R$ w+ P% ]( ^ - RCC->CR &= (uint32_t)0xFFFBFFFF; //对bit18 HSEBYP 设置为0,外部高速时钟被禁止9 `3 L- M& }8 U M# c
+ i" o5 B S4 ^- /* Disable all interrupts */
) k- N: q, l! W - RCC->CIR = 0x00000000; //所有时钟中断都被禁止
3 \1 q* D1 [( h" T. l
* h: R0 c' x7 [* t0 u) s- #ifdef DATA_IN_ExtSRAM! C9 J6 t9 Y7 s/ i* }
- SystemInit_ExtMemCtl(); ; @* Q$ J U( j. N2 ?$ ]5 t
- #endif /* DATA_IN_ExtSRAM */
7 L! D2 [0 ~+ ~- H- o3 l -
7 k. e! \( w) z. R( t' T; v) J% _ - /* Configure the System clock source, PLL Multiplier and Divider factors, * K& z* J3 j9 k T4 H+ P+ a6 E
- AHB/APBx prescalers and Flash settings ----------------------------------*/
: p& u5 B6 l. ~* X! O/ e - SetSysClock();9 ?. n5 y% U) \8 o1 A% Y
* N$ }. I1 Q$ ?& Y! Z! B: {- /* Configure the Vector Table location add offset address ------------------*/! S" U( _! P! E3 F3 k, ^
- #ifdef VECT_TAB_SRAM& k8 w$ y1 N4 B% p+ w, K8 E7 }
- SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */) m" H) e" Q; d& Z* F
- #else5 f6 m1 W' G9 \0 [# {9 K
- SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */! V% x6 T$ a. A" S& N. l0 ]
- #endif
# J* I) r/ H7 [6 s$ { f - }
复制代码
0 r" B8 b J3 R' p
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下 - static void SetSysClock(void)
4 g7 |7 l' }6 P/ L4 T* U - {$ k+ w3 n, _+ L+ Y* P
- /******************************************************************************/
: z; N+ @% y8 R - /* PLL (clocked by HSE) used as System clock source */3 ^- m& J5 A) e9 k+ z1 X
- /******************************************************************************/
0 k: `1 t+ Z Q; P - __IO uint32_t StartUpCounter = 0, HSEStatus = 0;6 r7 o: k+ [' O' i5 m
- * G9 a( v- f( k7 h# q3 }, f
- /* Enable HSE */
; o, ~; ~ `4 {% p) K# F - RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ
" t4 `7 G; [ u( k -
3 k, S% e: | |1 b8 W1 ?9 i3 F - /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口
6 f. N# }" i2 T& k - do
6 u$ k3 R; q- `# E5 a - {7 P, H' U. n4 U& a& O
- HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好. l4 z3 P! O$ h& E8 N0 R
- StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
6 G# T ^8 r0 m* @2 W2 c- _+ B - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
: K/ o" h4 p* D \$ x& z - i- P z3 [3 l, [
- if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的
; J- R* |2 _4 M! [) O6 f% { - {; E! Q, a# z# V
- HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环0 b5 H# ]& V, x' P1 Q/ K
- }
! G5 Q8 E0 x0 ?: J$ K7 ^! @. A% G( v - else1 R" _2 t! |* {
- {
9 n/ g9 y3 Q5 j - HSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环+ l" g5 s# |4 _2 j( ?; b$ K$ e
- }6 ]1 u, [5 i0 [) \
- ~" g* ]! p6 G1 P! w& D- if (HSEStatus == (uint32_t)0x01)
% r2 J2 O) s9 f) N8 w - {
! i- r% A. r' X" q4 | - /* HCLK = SYSCLK / 1*/" z- ?( S" x2 o' q( z9 Z: ~
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M
1 s: b- w U# o- |% t( R -
/ M1 Q) p. m8 d1 @) ] - /* PCLK2 = HCLK / 2*/1 @# E7 }: }) p4 u: U
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
. D: I$ w2 V, f& x4 t -
+ y& k& Q4 U) _, U% {/ ^ - /* PCLK1 = HCLK / 4*/
- s7 z" q6 H$ j - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M
" J$ `0 b- f, B. g" N - & T, z: H1 \- S4 e; `
- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
, R8 o( G. U+ l1 Z' ~! u, P - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |8 t4 E* I z4 {& @. A
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
9 M/ l* U! [$ R, \' N - //配置完后RCC->PLLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能- r6 g! v; m* l5 u6 x
- //通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M8 A- m1 ?, G* T7 B7 c! f! V4 ]; z
- //通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN; ?" b2 R+ x, X+ o" |4 P. B
- //bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M; Y7 z) P. t- ~
- //bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M* _# |% u9 ]- h
- //bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M! `/ J; m( s. f; J
- 4 M1 \8 Z; {) Q
- /* Enable the main PLL */
0 `# |7 y) X4 s- p* S* ] - RCC->CR |= RCC_CR_PLLON; //使能PLL( w& r3 p0 U* ^. C; p$ `9 j. K
- - |) G }9 [' \! T5 O. s2 V6 X
- /* Wait till the main PLL is ready */) [" k5 O4 v- y$ Y9 s% |; q% l
- while((RCC->CR & RCC_CR_PLLRDY) == 0)9 l) `6 h( x' w' N5 c
- {, q0 \# D/ d2 \' P% q' i
- }
4 B7 c4 O0 ~" B f! }+ X - //到这里RCC->CR已配置完,最终值0x03036783
! O: z; b8 }: x6 e# i0 Z' N - //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启 B5 T' m" W3 x$ V9 t# w. J
- 6 [$ k8 _0 d8 ^+ U& v
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
& c+ V$ }9 Y, x - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;; u8 ]4 h5 W6 b# ~5 @7 y
A1 h8 ?+ m$ A- /* Select the main PLL as system clock source */2 C, ^" G; f2 J0 ]; g/ S
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));. ~( T) H3 v% F: S
- RCC->CFGR |= RCC_CFGR_SW_PLL;
( C& s" q% s! t9 v* y* K' X - ! o3 Y5 e- C# H8 A+ F7 H- n
- //到这里RCC->CFGR已配置完,最终值是0x0000_940A- V9 K7 ]; ^9 ~! B
- //通过查看用户手册,知道,PLL时钟作为系统时钟即120M
7 p8 v* z8 Q+ o G, n4 d' B - //AHB不分频,即HCLK = 120M4 z* z7 Q- y3 J: V# x& c7 [6 f
- //APB1 4分频,即fpclk1 = 120/4=30M8 g4 ~" j- J, U1 p
- //APB2 2分频,即fpclk2 = 120/2=60M" a, K3 f# n- h; j3 q9 f* Y4 F
- 0 G2 s D; _& t" w' Y9 j8 @6 L! [( \
- /* Wait till the main PLL is used as system clock source */, Y0 ?" _& {- s: T* W
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);2 \5 [8 V+ @3 F
- {" \4 B) `; l# c. t
- }1 |; y8 q' w6 [
- }9 n; o Y7 @, H# h% |0 ?# \$ J
- else
6 ]. w# q& T$ h - { /* If HSE fails to start-up, the application will have wrong clock: C" c& d& k8 b& l' `5 \8 I: X7 Z
- configuration. User can add here some code to deal with this error */! m9 g7 V0 }# i6 V5 |: S7 @5 g0 N B
- }
/ T2 Q- G. V+ A8 _' l
3 P+ A7 Z; L, Q8 }; J5 o6 J- }
复制代码
/ ?: o; X; E t3 t


OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。 ) Q% c! s2 N* c8 X; b0 s, V
" H. L/ N9 L7 k
6 s6 L% g; Q5 x
7 A9 |+ ^: M. v3 p6 o
|