|
有个项目要用到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。 然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。
9 p! @8 r5 [ i' m
# z* t4 U+ w) j, Y1 k6 x

然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中 
- ; Reset handlerReset_Handler PROC
0 b9 l% I, a% f: c4 u; E - EXPORT Reset_Handler [WEAK]( v& g4 O. T( W" L/ k* @0 \
- IMPORT SystemInit- j7 d# |2 [1 t1 J+ I' ?
- IMPORT __main
$ P) d. r, `+ D- Q+ |, [ - LDR R0, =SystemInit& g& U) W" q# f' O& a2 q ~5 Q4 }
- BLX R0* J) ?* e6 {0 d- }5 b* j& W
- LDR R0, =__main9 P9 P5 y6 S4 T5 T: P3 ^
- BX R0 K9 F" w2 {3 W1 K3 S( q! i
- ENDP
复制代码 $ m) D# l+ l/ D4 g. X

代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。 
2 [/ Y- Q* c9 {+ H- G' Q+ ]! G
- void SystemInit(void)$ o# P) I) ?# p& R0 a6 p$ b
- {* E% @- `" U' J E% o# [
- /* Reset the RCC clock configuration to the default reset state ------------*/
3 H( T# c1 U1 l1 L - /* Set HSION bit */
: Z. D8 r* ~+ x; p - RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ
) K0 i+ R! Y8 g* C7 j0 T7 J - 6 R$ y( U$ t$ e( C6 A' w6 `
- /* Reset CFGR register */
/ _- i( h- P( J9 M1 r3 K; D - RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ8 M* e/ e& F% h' n$ q+ o
- //AHB prescaler 不分频: L0 ]7 Y+ o4 q! @
- //APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ
0 `! q Y( ]) @+ Y G4 M: m - //APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
$ D8 c- \" G* K/ T f - //MCO1和MCO2时钟输出等配置可参考用户手册Page958 G5 i9 t, t& f/ f
! p( t% E0 t& y7 |) k' h+ T, r- /* Reset HSEON, CSSON and PLLON bits */
+ ]8 `" |0 v7 ?3 | y - RCC->CR &= (uint32_t)0xFEF6FFFF;
, R+ e" i/ s& B+ K9 C) F0 `
* \. h, Z" u0 K: ]0 M5 W- /* Reset PLLCFGR register */' p1 q8 M) A& N
- RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010% q. q' }# B* B* k0 U0 f- k
) X" _& u* K2 k/ K- /* Reset HSEBYP bit */
9 P7 x% `3 O/ _: ~* i w - RCC->CR &= (uint32_t)0xFFFBFFFF; //对bit18 HSEBYP 设置为0,外部高速时钟被禁止; Q! Y8 G2 J+ P) F: q7 j) s! i
- 9 D3 f# `* v% W& b; O
- /* Disable all interrupts */8 X( B% s" E9 U; R6 n4 G
- RCC->CIR = 0x00000000; //所有时钟中断都被禁止
- m, \) P5 o% ~. s/ i - % A2 h) ]( j/ y
- #ifdef DATA_IN_ExtSRAM
( q& Q. ]* _: E! L - SystemInit_ExtMemCtl(); 3 e# F s; f0 n2 c7 D
- #endif /* DATA_IN_ExtSRAM */- T6 U2 x6 {* J$ S
-
1 L7 z# X, q0 y0 ~3 r - /* Configure the System clock source, PLL Multiplier and Divider factors,
- l: J2 c7 [# F0 l! H - AHB/APBx prescalers and Flash settings ----------------------------------*/ J, l1 O1 S+ g" _/ ?- X
- SetSysClock();# S" A+ Q0 ^, l: Z
# B( T3 Z* }! { v$ s- /* Configure the Vector Table location add offset address ------------------*/; U! |, W6 Q9 E0 q- r
- #ifdef VECT_TAB_SRAM+ y. S9 Y/ ^8 A5 Q! { }5 x; v7 n
- SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
" g. t5 x. B$ W1 e - #else
5 X) ^) s3 l8 X - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */4 X' s4 R i( U/ P0 H5 C
- #endif
/ ?( x% C( z% w: s - }
复制代码
" g' l1 ?) q, A- z
在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下 - static void SetSysClock(void)' @) G `/ n$ N; w: F, C
- {
2 R% q- Z2 V& J+ |1 o8 r% I - /******************************************************************************/
U* l' B5 ?5 e y& `) s! h - /* PLL (clocked by HSE) used as System clock source */
" h/ |. Y; g+ P. F V - /******************************************************************************// D. r% W5 z" k6 H2 i) }; e
- __IO uint32_t StartUpCounter = 0, HSEStatus = 0;, O4 }8 r3 j, P9 N* y
-
$ b) n O z7 o - /* Enable HSE */7 m$ @# o3 u- A
- RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ
, n3 s+ J' M+ ~) ~1 K' r: ?" k -
" G9 r" u5 ~" L0 Z$ H - /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口
# h- K1 h* U: L9 c% _ b) r - do; E$ i7 \5 ?: x2 x2 H) R
- {7 Z3 n4 P) a* O+ q
- HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好
* N" E6 i2 U- G4 u% M - StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
6 r7 y/ \7 ^7 w. F+ c) V - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
* I% T2 H5 c1 w6 r' C! ?; x5 z - 1 S! e0 x J# \$ W4 C
- if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的" U; u% S1 r0 r, t1 P7 Y
- {
- e' B) w$ x+ K - HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环
1 C/ G/ `) F& G/ Y7 }$ |! n - }
' z/ w: g" H2 {% G! Y* X m - else
6 R; s4 o& s4 M- {% ^- d7 U - {! ^: z# r# P, w9 w" P, K/ y
- HSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环2 b2 Y0 e' s$ n. c+ P
- }0 ~: ]) t) d- I: J( W2 v% L
- : R4 H3 r% _* p( Y( l3 A1 \
- if (HSEStatus == (uint32_t)0x01)
8 w# Z/ Z1 O/ J6 R6 `3 |' ]' Y, N. _ - {
7 F2 l# T( `3 Z& @3 E. | - /* HCLK = SYSCLK / 1*/1 x D! A% B7 W' ~
- RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M
% v G( [! @) s+ d# p* R5 y2 G - " F: C, [6 Y' L- P' v, n
- /* PCLK2 = HCLK / 2*/) N; K+ m; K1 G5 Y
- RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M
3 a* J, ^5 ], b1 ^$ C u - 1 x3 T! ^8 J/ F; h
- /* PCLK1 = HCLK / 4*/+ m! C, ^, g! \# ]4 B
- RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M
$ t; {' `7 c6 ]. o2 V, S3 Q - 8 `' a" Y: m3 J1 D
- /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
$ s# N# {" o2 z5 t - RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |7 \4 T9 Q3 @5 J* |" Y; ~' {' ^
- (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
0 G$ E* s4 w/ r, l* z - //配置完后RCC->PLLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能$ J/ s. S" z8 K( U
- //通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M3 }7 [( h' [0 G& A) b
- //通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
7 S. u/ i$ _+ _* R - //bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M- d$ v, p; k* t$ @0 d. k8 S
- //bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M
X; g- [) Q9 F' @: T& | - //bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M
* c* K4 p# E. J( |* C7 U; \ - " H$ e) H- u9 E+ Y& M- {
- /* Enable the main PLL */
( Y6 e$ v# B8 R4 A; p - RCC->CR |= RCC_CR_PLLON; //使能PLL
6 C. Y( Y c2 r6 I
* q6 K- [8 r6 n; x# u; j- /* Wait till the main PLL is ready */4 g& v2 y' Y7 ? i, X$ y
- while((RCC->CR & RCC_CR_PLLRDY) == 0)# |! @7 }7 F9 |# D5 H/ z) _
- {# S" C* H# E/ Z o( ~; w
- }
, Q7 o+ z' }! t0 i - //到这里RCC->CR已配置完,最终值0x03036783
4 K8 f- g$ o4 s - //通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启7 ?* M: K8 L8 P+ K1 V& u, x) ?- {4 e
- + r e0 Q1 X! K+ f5 _
- /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
/ b% I" i' X6 d8 H6 R& W - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;
2 E4 ~( k" \. A" @* F3 \; L
$ p8 h, i1 O; W- /* Select the main PLL as system clock source */2 s0 t% u7 B) z! n& ~0 o1 b/ O
- RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));8 A( P0 m. T5 ~8 g
- RCC->CFGR |= RCC_CFGR_SW_PLL;, n6 Z# \: u4 E+ z3 \3 K1 m
- & o( V# C- ?) G$ @
- //到这里RCC->CFGR已配置完,最终值是0x0000_940A
$ g$ m) J1 d# D" q: N6 q, X - //通过查看用户手册,知道,PLL时钟作为系统时钟即120M
" {* ]% y ^0 Y" b; z& @ - //AHB不分频,即HCLK = 120M
! e8 J& ^. j& D: [& a& C - //APB1 4分频,即fpclk1 = 120/4=30M
1 ]9 |/ m5 Y; w! |* I% e# a - //APB2 2分频,即fpclk2 = 120/2=60M
z8 j5 i+ g6 ?2 l" u( l - 9 e8 x! v1 D& g0 ^, M
- /* Wait till the main PLL is used as system clock source */0 s, |( @5 j9 }7 x
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);; C; O6 D& c: @2 j3 |
- {/ ]' L# ^: V/ }& }8 r$ q" h
- }% T& {5 L+ Z( h/ E; ~
- }4 F( P+ G' ^/ L6 ], j1 o: j
- else9 |) q; G4 w: b7 u' c
- { /* If HSE fails to start-up, the application will have wrong clock
a' Y. P5 ]; y# q - configuration. User can add here some code to deal with this error */- R+ K2 ^5 C9 P# p6 J3 _5 W4 C, y
- }
9 H/ ~& M8 c% |
# |- h7 k+ `9 X5 z T8 O- }
复制代码
l' M; b3 I, H% O9 R2 c; Y& `5 v


OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。 总结: 1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。 2、APB1出来是30M,也就是FPCLK1。 3、APB2出来是60M,也就是FPCLK2。 & r, N% j& f% \( w" ^2 [
, c: I8 S% M% T& N! C/ f
$ }/ h! ?$ y# v) f7 `, E6 T7 E8 O5 Q
|