本帖最后由 TLLED 于 2018-12-8 14:42 编辑 3 i% g8 O. Y2 d; |! H; ]
M$ g# o; `0 ~/ h* z8 s/ X 通过第16章节 -- RCC-使用HSE/HSI配置时钟 的学习来重新认识系统总线和外设时钟的配置。 4 b" l$ o. h; T: Y- G/ B
* S" u* L: T- ?9 K; f* M 一、手册中有关时钟配置的内容
' K! ~; R! M- R" R) b# U- M5 J6 X3 `, A1 ~& |
1 N4 Q& }7 Z0 F4 x4 ?" e. [# o! ?. N 1.1、系统时钟框图: 9 [7 t( m3 h7 _) Q1 C9 b
里面显示出STM32芯片所有时钟和有关配置的寄存器位
: n. X9 ^. B0 \; l
: {' U2 N" u8 \1 E4 i2 I8 E( W " F/ s% k: V0 D3 I( u. k- z- Q* @
二、系统启动有关时钟配置代码
n% h6 ]' L8 N, s( s3 L4 w5 W$ G2 i; R: g p: v
2.1、系统启动分析* y3 D% K( S" @5 A7 v
上篇介绍启动代码分析,会执行Reset Handler服务处理程序。
7 d \% T) z) s, m* X: F7 L P
5 y! L0 {& I" A& l3 Y2 k
里面有执行SystemInit系统初始化程序
) C0 Z5 c3 M9 n" J
& t1 `* u, L3 O% Y) c" d
0 `2 \, G6 K l- u) C: @, {* p
里面调用SetSysClock函数设置系统时钟
3 x" ~0 D( Y1 ?9 s
/ f( M# V% d' \0 Z SetSysClock函数配置为72MHz后调用SetSysClockTo72函数) c( _5 K* c8 e2 u2 S8 e' u
# F1 K8 A* Z" I+ E8 @9 R, Q
" M% r4 [: B: b. E- C
2 c' ~' Q+ Y& S, L$ ?$ j/ S
5 Q! O6 ]9 Q6 N# V) `: L 上面是系统上电启动中,系统初始化和系统时钟配置过程,已经把总线时钟配置为72MHz。
; D2 `; i- u; X2 t* L ) K4 _0 i, X) m
三、时钟配置实例
+ F p, C: a+ U8 a& s: g
6 E m5 b; m. w6 M8 F) N1 F, N$ ~' A
3.1、配置HSE时钟
# s7 S0 p/ {" ~6 F 了解系统时钟配置流程后,来实践使用时钟配置。
5 S# ^3 ^2 k, V' }4 } 在系统时钟框图中,HSE时钟配置
* Z, h" |% M& m7 A
8 t* c. {# `/ F$ h
>选择外部时钟OSC32_IN和OSC32_OUT两个引脚上接的8MHz无源晶振
5 q1 x# D6 R# ?$ J' } >通过PLLXTPRE来选择HSE时钟不分频还是2分频。
q1 s% o- Z4 w% Y >通过PLLSRC来选择内部时钟HSI和外部时钟HSE时钟选择。. `! e8 V( \) M# Y
>通过PLLMUL来选择倍频设置,如系统总线72MHz,晶振频率8M*倍频9=72MHz) S4 R# \9 U P
>通过SW来选择时钟源HSI,PLLCLK和HSE时钟源。
( v, s" v0 G5 S" t6 ]+ \% n% \* P
' M- k8 {. e( F6 S! x
4 C, }% _1 z4 C2 K 3.2、程序源码# N; [# g4 `. Y" |! h) [7 `( S
3.2.1、HSE时钟初始化' L+ d m9 [/ x/ Y8 o' ?" C
PLL时钟倍频到72MHz) |1 ^& f# B& V' B
- static void HSE_SetSysClock(void)2 H8 f% [% _1 T/ p
- {2 J, I9 ~) w, x/ Y; r& w, E% P
- __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
! z' t5 i2 s' J" C# B - ! }; J- g7 R/ I2 g) C. D
- RCC_DeInit(); //RCC恢复默认状态
4 k$ d) U3 ~- y; G - RCC_HSEConfig(RCC_HSE_ON); //打开HSE时钟
% o3 _" r" D0 g7 G0 ]0 C7 }* m - 6 p6 U2 P6 M0 q$ u3 _; P9 k
- HSEStatus=RCC_WaitForHSEStartUp(); //等待HSE时钟稳定
) M7 J# E. g! C0 }; q/ ~. B1 F/ r: W - 6 U& L5 i/ B% n4 ^' q
- if (HSEStatus == SUCCESS)8 ~3 }" m* O0 u+ J
- {
* G0 `# z* L i9 y7 ], O - /* Enable Prefetch Buffer */( T* [( {) u2 V
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //使能FLASH预存取缓冲区
. E' l) r0 O. R3 | - FLASH_SetLatency(FLASH_Latency_2); 7 k2 Y" W/ U/ |! B, i
, o: K1 Y7 ^5 V/ e; [$ y- RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB - HCLK=SYSCLK) ]( l7 k( p) B4 O
- RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 -- PCLK1=HCLK/2 m& f& f+ i8 l5 R9 C
- RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 -- PCLK2=HCLK/ k# `; R4 @& h5 b s; J2 O
" D$ m: d. k* M% m& \- RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLL倍频
0 q3 c) o2 z6 d, ^3 A. r9 r% H - RCC_PLLCmd(ENABLE); //使能PLL
5 w0 W% Z2 f8 J+ Y6 ?& @ ?: _ - - z+ ]' a' \' `, |( w
- while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET) //等待PLL稳定% H5 W) l) a8 D" r
- {0 s- @# W/ O- w
- }
/ O {" B8 K6 S M7 y, S( S9 V - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //PLL稳定后,时钟切换到SYSCLK# B: t9 M; `8 m, g
-
4 A( K& D( x& n+ ]- I -
' Y, l: ~' E! f* Y# ?* L5 X& d - while(RCC_GetSYSCLKSource()!=0x08) //读取时钟切换状态位,确保PLLCLK被选为系统时钟, R9 ?* A; N) t4 d! u
- {
. O4 ]' o7 N' D1 v) O# v& i - }! y. b t2 h0 Y1 Z3 n5 m+ \8 ~; \
- }
" h& N! Z& i" v6 K7 t" A3 P% L+ E - else W3 p X8 y2 O: D5 \
- {
8 o% v7 z' {8 K - }- J- V$ g! @( }
- }
复制代码 3.2.2、MCO引脚初始化
7 r) D1 U8 O8 G5 d: `- //MCO引脚配置
y* T A2 c3 _2 ?* Y( v& } - void MCO_GPIO_Config(void)6 Y! y- \0 |- v% {
- {. q8 S G8 E2 z! t4 K
- GPIO_InitTypeDef GPIO_InitStructure;7 _, j3 a" r. {
-
9 p% y! l5 l: \! W( Q8 t+ c9 h9 r - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟' }! V& n7 B; k, q; L' q
-
- u$ m( L ~8 ?+ L: Z- y( [ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; 1 I& q" c7 G7 W A- z( R. ^0 P
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; . A. W8 }- u* b$ V. a: e
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz6 J* e: [: x. z) z
- GPIO_Init(GPIOA, &GPIO_InitStructure);
. d3 {% m9 q) H2 I, L2 i& Y) w$ j - }
复制代码 3.2.3、LED引脚初始化
" R. S" t% a6 U$ b9 `$ C- ( x2 w) q6 @9 V' H0 E% e
- void LED_GPIO_Config(void)2 \$ c! B( ]- Z' R/ Q! z1 ]6 H
- {
5 m& U& Y* y1 v" J - GPIO_InitTypeDef GPIO_InitStructure;# }! I: k& z2 Q% E
-
' |3 V& z# t. x& e& t# d: K - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PA端口时钟
' o4 a7 v, k' u -
; g! {* q% D Y# l: l$ n) A - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
8 n: i* a0 t# }- T4 M6 G - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; * o$ M9 ?# u$ |6 c) ^8 U1 ?. L
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
B: \0 W* y5 @ - GPIO_Init(GPIOB, &GPIO_InitStructure); w3 [1 N/ l% ?& ^ O' G) n6 {0 o. J$ d
- }
复制代码 3.2.4、主程序
" |0 @3 V+ i4 r/ F- int main(void)
, k0 ?# c5 r. ~+ Y4 [/ V. p5 c/ H - {5 K" z0 g9 ]# G) F2 ^* A! c
- uint32_t i;5 A# E0 @, K& g6 H' ?& D/ L8 w/ k
- HSE_SetSysClock();4 w. U& O3 S+ [
- MCO_GPIO_Config();
% V" p4 R3 G& b* X f* c3 i - RCC_MCOConfig(RCC_MCO_SYSCLK); . }. W* _& B% _* R3 q1 k2 E
- LED_GPIO_Config();
4 K2 S; h7 p% g" I - while (1)5 z* P3 z; I0 C
- {
, x- @# \& w# v+ i -
$ n4 G; \- e; D* D - for(i=0;i<0xffff;i++);, V9 H( |8 k% l9 ~' v( r
- GPIO_SetBits(GPIOB, GPIO_Pin_9);
: e/ a) [1 h% E2 r - for(i=0;i<0xffff;i++);
7 s4 c/ ~. R9 M% H - GPIO_ResetBits(GPIOB, GPIO_Pin_9);6 x3 k! ~3 |7 o* q% j C
- }
! e* `. G9 P( V. D1 ?) ]( c - }
复制代码
+ O X' ^4 o b, u' \' I6 I 四、程序执行结果
, g! W# h6 l, z9 Q# l* c7 R; F6 \! b6 v1 }" V
- y# R6 s* p g7 F/ c
4.1、PLL时钟设置为72MHz,倍频系数设置为9( Y$ K/ e h( B& X, O
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLL倍频
: M4 ^9 e/ g4 a9 ?; z 4.1.1、MCO输出时钟频率72M7 C. `9 ]2 E' A- U! N7 F# O
+ V1 P: L, x% |2 X @% s2 O 4.1.2、LED引脚输出
* v! P' e; E ]
" U; g; |: d, ~. w: R* [
# U- S& A! B. j3 T R 4.2、PLL时钟设置为56MHz,倍频系数设置为7 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7); //PLL倍频
# m6 Y& ~" n. w4 B+ x) c; k 4.2.1、MCO输出时钟频率56M
! Q/ `+ ~3 b* N2 K5 W- m j3 W3 K" [
# U |# u* r7 ~ 4.2.2、LED引脚输出
- g! }6 F+ y; t- C, [5 q( m) f m
9 U; A9 d% I1 r LED输出宽度比72M时钟时,宽度变宽3 g A# f; o0 [6 v; S. W' ?
4.3、PLL时钟设置为88MHz,倍频系数设置为11 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_11); //PLL倍频
) }* v% Z7 N( G% e# r J: v" p% u 4.3.1、MCO输出时钟频率88M
# i" l- F% o* W l7 ~" t# C
2 }* @! L0 M) ?
4.3.2、LED引脚输出
! ^! w3 H" i* X
5 {' u! T! l( U
LED输出宽度比72M时钟时,宽度变窄
! v9 D. ^! c& P9 v( A* O9 ^$ y6 n* t- S1 K
五、结论
( a' x i0 e% { X# {3 b8 h6 ]5 r9 Q& r9 O% {
0 L1 D. i2 K7 n5 u# S
使用外部时钟时,通过改变PLL的倍频系统可以改变SYSCLK时钟频率,来降频或超频工作。HSI配置和HSE差不多,就不在列出。0 G, w- K4 k$ G# b, l2 D
5 P& Q- g( o% w& ~ I+ }6 D# W
1 B( S0 b4 X+ ]# h* g, k" b
: u/ q7 [' T; p
$ M1 Y; U( l1 P
) P6 t* ^3 P& o1 c: }! B5 i6 N9 R& k7 ? L9 @8 p' S- F2 Q
$ D- t% Z. S- `$ ~% F9 f
& g, A4 z2 `- Y& u8 h; S |
RCC->CFGR &= (uint32_t)((uint32_t) ~(RCC_CFGR_PLLSRC |RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));% x* V, `3 S1 ]+ n0 G1 S1 v, j$ H
这句话是把第16-21位清零
由软件置’1’或清’0’来选择PLL输入时钟源。只能在关闭PLL时才能写入此位。
0:HSI振荡器时钟经2分频后作为PLL输入时钟9 o5 X) s$ O& f: I* b9 N
1:HSE时钟作为PLL输入时钟。
以上是手册里说的
这句话是不是把相应的位置1 这样就能配置了