本帖最后由 TLLED 于 2018-12-8 14:42 编辑
* O2 K0 T- [) E
$ E6 B3 l5 P- t" [ 通过第16章节 -- RCC-使用HSE/HSI配置时钟 的学习来重新认识系统总线和外设时钟的配置。 ( |/ ~. d# Y5 g$ }
! x% R% n7 e3 D. I
一、手册中有关时钟配置的内容
/ ]% n" v7 K4 M! W7 o# s8 K
+ N$ J5 @* H2 H& _3 e
% C1 r! |% o# Y$ }5 u5 ]* o' x 1.1、系统时钟框图: 9 X8 {5 Y, n# c9 F
里面显示出STM32芯片所有时钟和有关配置的寄存器位
( v0 f$ N# Z" }3 h7 q. j1 C$ N
2 d: S7 j6 k' ?2 g& I; L% r" N
9 g5 O+ n/ X% h4 c9 H/ d
二、系统启动有关时钟配置代码3 X' F- F9 c* J" u' f g
; b! V1 m' k3 C/ Q6 @8 a
2.1、系统启动分析
7 z" j( ]" {+ ] 上篇介绍启动代码分析,会执行Reset Handler服务处理程序。6 b/ m5 |. h. R
& S$ G7 v; H4 \
里面有执行SystemInit系统初始化程序
1 \+ o8 L1 U0 q
5 S/ v! o1 {6 Y. ]) `
9 w' C2 {3 l0 C5 P. G) S' ^
里面调用SetSysClock函数设置系统时钟1 q2 f/ d# a% S% }* t: u! e* I" |
5 `. \9 y; }0 C w* e( A' m SetSysClock函数配置为72MHz后调用SetSysClockTo72函数! F9 w. V v. `
, j4 f* D( A+ f. g! m* T0 n
0 }1 ]+ Y- q+ {' G3 ^
" n$ Z) y# D4 O# i! f
# s/ [0 s1 P$ u# B9 ?) \ 上面是系统上电启动中,系统初始化和系统时钟配置过程,已经把总线时钟配置为72MHz。
& X$ d; k( T! r/ X' e& L# a! B
/ O+ U: y* C/ l& Q' D/ o/ @0 l8 d- _ 三、时钟配置实例* D8 U2 N( K; Y+ v
: f6 { ?. X6 i3 [
* J1 o' K! J$ U: z. q$ @$ x 3.1、配置HSE时钟 ) g, t) t# @9 ]5 |0 t9 t( S0 h h. c6 j
了解系统时钟配置流程后,来实践使用时钟配置。1 Y2 v* a# V: H+ p
在系统时钟框图中,HSE时钟配置
3 j% K& s: e$ }1 n) ^# j- q
H' u) L! ?1 u
>选择外部时钟OSC32_IN和OSC32_OUT两个引脚上接的8MHz无源晶振 O$ S$ [0 q; I4 u; \5 M
>通过PLLXTPRE来选择HSE时钟不分频还是2分频。9 d. Q. l" m9 L2 U5 A3 R1 _9 \
>通过PLLSRC来选择内部时钟HSI和外部时钟HSE时钟选择。 U3 H9 y% q9 g1 ~( o* Q
>通过PLLMUL来选择倍频设置,如系统总线72MHz,晶振频率8M*倍频9=72MHz
" K$ D3 Y0 ~" ?# ? >通过SW来选择时钟源HSI,PLLCLK和HSE时钟源。
& h( s. d/ K3 j' s0 |, H1 z* l( i. Y9 t( U
7 |1 c, H1 \7 E& u4 \9 B7 {- d
3.2、程序源码. ~ l" v) a; {9 a; g' d
3.2.1、HSE时钟初始化
- O: ]0 g6 S2 ?( c9 ]! o PLL时钟倍频到72MHz$ m+ m" E3 E8 q$ s" A
- static void HSE_SetSysClock(void)) h0 B0 \$ z* @0 J; Z) _
- {
" y3 d( m6 u, I - __IO uint32_t StartUpCounter = 0, HSEStatus = 0;) I2 }# L( @# j# u
- 9 K9 s; l6 G+ \& w
- RCC_DeInit(); //RCC恢复默认状态
* M& ]- q/ s$ }4 q* W# `( ^ - RCC_HSEConfig(RCC_HSE_ON); //打开HSE时钟, U6 X( h4 Y4 L4 V: g; c
- ! w0 s1 J2 n, A8 O4 R/ _ b
- HSEStatus=RCC_WaitForHSEStartUp(); //等待HSE时钟稳定
% @" B5 J1 s- O1 Z - , B6 J% Z9 `) U
- if (HSEStatus == SUCCESS)+ f5 S" z C9 R- Q0 L
- {
3 r4 }' P8 X z8 n6 d: h8 B, L - /* Enable Prefetch Buffer */
, m3 N' z+ v7 l; C( y2 P! @3 f - FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能FLASH预存取缓冲区3 C' x0 n+ A! x
- FLASH_SetLatency(FLASH_Latency_2); , }4 H& ^6 Z/ N/ k) F# j
- " ^1 j& W0 M, e8 e" n& H
- RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB - HCLK=SYSCLK
( j6 F; |- b: K$ N4 h - RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 -- PCLK1=HCLK/2& i h1 ], ? [" i" r! ]
- RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 -- PCLK2=HCLK) x0 B0 K6 b# b4 q, o9 Z
' u: V6 }2 O% p- RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLL倍频
+ X: M m5 X4 P! `% U- y - RCC_PLLCmd(ENABLE); //使能PLL
' s0 k7 Z7 B _4 g5 R - & w7 b4 |* J8 p4 A: e
- while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET) //等待PLL稳定
?- R4 f; X2 S2 Y6 c' g - {( W; c9 Q6 Z+ ]4 y
- }; S4 |4 ?8 A# f
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //PLL稳定后,时钟切换到SYSCLK- L/ ?& s! {1 ?) h6 F- f. {
-
8 N3 [# o" w: Z [( c$ b# D - 1 r8 j, K' M9 {* M9 Y w2 L
- while(RCC_GetSYSCLKSource()!=0x08) //读取时钟切换状态位,确保PLLCLK被选为系统时钟
1 ]1 Q5 s8 Z3 E - {
% `& n3 b* e6 H - } a* `+ a' I; V0 \8 a
- }' `, P8 x0 Y2 Y9 n2 i8 H; t b
- else% S0 G/ G V% q
- {% |+ e5 F$ o" d H0 G/ @5 t
- }
/ B( M' g8 a7 v4 x' o2 ` - }
复制代码 3.2.2、MCO引脚初始化$ r) x4 }2 w$ E" f! @
- //MCO引脚配置* A3 t% ^: U& V J9 e/ ]
- void MCO_GPIO_Config(void)
* [4 N, N$ p3 {; v" @( s1 K - {
* Z$ {# R0 o" a* N4 B1 N: Z5 ` - GPIO_InitTypeDef GPIO_InitStructure;
3 @) @* k0 i e* q4 E, q j - . M0 t) P0 d0 e" u, K- L
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
$ o' u9 |. f! }' L E - + P! v7 ^- a& x$ w5 a0 J
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
* L. v0 H$ [% \8 c - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 3 f/ U2 V6 I) S) M) Z9 a
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz ~6 R' I" _) L/ G8 S9 i! @3 Z
- GPIO_Init(GPIOA, &GPIO_InitStructure); ; l# o" t! ^7 P4 D- x
- }
复制代码 3.2.3、LED引脚初始化% ^3 E/ B1 |2 P( Z' B8 t/ U/ r! P
- # \& Z0 j: {, l( Q7 z
- void LED_GPIO_Config(void); B$ y; ]) q, D
- {1 }5 \) K! m8 H1 h9 X' A' j
- GPIO_InitTypeDef GPIO_InitStructure;, W1 j; o' l& @9 e$ S+ X
- 5 \! j. R; P. Z2 m. I s7 m
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PA端口时钟3 U6 O/ o: X% H9 b! c
- 9 x) c6 m! e, Y, ^" F4 M$ H
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 9 i2 @& o0 p8 ]* e- F9 t
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 1 k( } n- [( L5 A
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz, O) ]+ y; t0 m
- GPIO_Init(GPIOB, &GPIO_InitStructure); : g! k0 v; T W, k5 F
- }
复制代码 3.2.4、主程序2 V2 \9 h: Y! m3 H0 _
- int main(void)- l/ W5 T4 i% f& }* l% t z3 c
- {
7 K. a9 L" P$ E - uint32_t i;8 R, Q6 ]4 @; g5 p, v
- HSE_SetSysClock();6 i/ ]7 h! J+ a8 v+ q. j/ `/ s
- MCO_GPIO_Config();# L9 h. p' J w3 M1 I2 Z; Y4 E
- RCC_MCOConfig(RCC_MCO_SYSCLK); 0 {8 ^/ l1 ]. Y& c! C5 y7 i6 e
- LED_GPIO_Config(); / P1 t- [4 C# ?4 O# i" D9 I
- while (1)( {& o6 x1 H% h: M8 K' O
- {6 x$ s$ w; d6 B3 x3 n- d5 k Z
-
8 C- W, `, w$ h( {# e( F - for(i=0;i<0xffff;i++);
' o, K% m9 g- ~ k - GPIO_SetBits(GPIOB, GPIO_Pin_9);1 }7 h, P) j5 z* P6 S
- for(i=0;i<0xffff;i++);
3 w; }6 q0 j5 W u- s - GPIO_ResetBits(GPIOB, GPIO_Pin_9);% l0 B& U9 L$ G6 B& ^4 G* H8 [7 C
- }9 A& ^; ]& e% L* G& f- x, i0 `
- }
复制代码 4 l6 a9 n# y8 I0 \% X* s" V
四、程序执行结果: P) @* g2 d+ e4 b, b; j3 ^
& U w$ J) g A9 A+ }) `7 u* f$ q9 t
4.1、PLL时钟设置为72MHz,倍频系数设置为9" O. N( l: t+ F: S
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLL倍频
0 N1 e6 b' D- A9 k! q 4.1.1、MCO输出时钟频率72M
4 @$ p0 z+ O; x( X
1 c f- [% A4 S, R& E5 b% N6 H! H6 g 4.1.2、LED引脚输出6 p1 _8 n9 E* e( Q2 n0 h
1 @( A+ R# H2 g9 Y/ S5 r' k
9 M4 _( q% t" _: R4 f 4.2、PLL时钟设置为56MHz,倍频系数设置为7 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7); //PLL倍频 I& g0 h3 t; S- a: }8 W
4.2.1、MCO输出时钟频率56M# Y$ Q8 {% h) S5 `# J, E
- S7 W7 {* }# b, E! A3 J: R 4.2.2、LED引脚输出
" [! I! v5 m- F4 v9 X) m
; a; \8 `- l! c5 ^$ f' h
LED输出宽度比72M时钟时,宽度变宽2 T4 t8 _; J7 r @+ r* A
4.3、PLL时钟设置为88MHz,倍频系数设置为11 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_11); //PLL倍频4 f( W& @2 u% V0 t) _* S
4.3.1、MCO输出时钟频率88M
: K0 N- k( U O7 g! V$ U$ A3 `
& e% [: X K# v) C7 \
4.3.2、LED引脚输出. f: y) ]# j* [$ b0 x+ F
! z3 o: N# B a$ P" L& ?4 v& o) g
LED输出宽度比72M时钟时,宽度变窄# T9 J7 A+ {8 r7 M. J
6 a3 P' V& ~* J7 }4 x 五、结论
5 `4 ^: ~ z/ r @/ z( W/ e9 w' d* ~ c$ [
6 ~# C( F4 ?, e3 J% K1 \+ B: ]& c 使用外部时钟时,通过改变PLL的倍频系统可以改变SYSCLK时钟频率,来降频或超频工作。HSI配置和HSE差不多,就不在列出。
0 Z0 @: ?2 Z4 D" C: T1 O# o* Y; y# g O/ X
/ H5 b: M/ a B& W
! c6 K9 f l9 S9 h: P% K& Z _+ @9 e
7 k; H1 t* B, G4 Y' }1 _, `7 i; w! o8 l- _1 c7 V
# p4 g7 ?/ C, ]% C3 Z j! k
8 C% K: T, e! M6 e! l
|
RCC->CFGR &= (uint32_t)((uint32_t) ~(RCC_CFGR_PLLSRC |RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));% g* {& g( {: l9 s! w
这句话是把第16-21位清零
由软件置’1’或清’0’来选择PLL输入时钟源。只能在关闭PLL时才能写入此位。. `/ s% R' A# [7 C" @8 i
0:HSI振荡器时钟经2分频后作为PLL输入时钟" o1 _; k) L+ D& S( E% A5 a) U
1:HSE时钟作为PLL输入时钟。
以上是手册里说的
这句话是不是把相应的位置1 这样就能配置了