0、前言 RCC-复位和时钟控制器 可以实现配置系统时钟SYSCLK,配置AHB(HCLK)总线时钟,配置外设APB1(PCLK1)和APB2(PCLK2)时钟 库函数的标准配置为PCLK2=HCLK=SYSCLK=PLLCLK=72M,PCLK1=HCLK/2=36M 系统初始化时会调用函数实现时钟配置。 - #ifdef SYSCLK_FREQ_HSE
- ?0 o& q7 {7 n7 T8 X) Z1 f: i4 p - uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */
3 Q; f0 h- J, I' [* t3 T - #elif defined SYSCLK_FREQ_24MHz
" k! j5 Q3 E7 } [, ?, H& Y4 \ - uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */
! E4 F5 f2 q3 q- w! h - #elif defined SYSCLK_FREQ_36MHz
. h0 e, T2 L. a v) i - uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */
: ?8 M' H8 A# G' l7 X) p# N1 w; A - #elif defined SYSCLK_FREQ_48MHz/ C1 N! r6 T2 ^! U3 `+ K
- uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */2 {5 Y+ W" ^5 s( C
- #elif defined SYSCLK_FREQ_56MHz
) L* i6 c- O% w7 p x; F5 D - uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */
% x7 W* ?8 n- ~! g2 x% U3 X - #elif defined SYSCLK_FREQ_72MHz
# E) j1 @. ^# I0 n$ @$ X5 U: c7 d. | - uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */9 T, t/ S' I: w- o* _" v( O
复制代码- static void SetSysClock(void)
: M1 }+ P7 G+ w" z$ B - {
9 y' G& z* T$ y" D, F - #ifdef SYSCLK_FREQ_HSE
' B) r+ _, p8 u4 j2 R) J/ D - SetSysClockToHSE();; o% I4 J9 Y' C; y9 _# _8 U
- #elif defined SYSCLK_FREQ_24MHz/ }0 Z9 ~3 u: ^2 t3 ?: A3 R- Z
- SetSysClockTo24();2 Y8 ?% ? Z5 Y/ H B0 ]
- #elif defined SYSCLK_FREQ_36MHz
6 W \ t& X( D - SetSysClockTo36();/ I1 m' ?3 _. r, J7 }! [
- #elif defined SYSCLK_FREQ_48MHz' b$ x; p8 P0 g6 U( v/ [$ e2 [
- SetSysClockTo48();9 F; g6 q( {/ H7 D+ m$ A2 j
- #elif defined SYSCLK_FREQ_56MHz
F3 A, p3 _8 h% i( Z6 z7 f4 O) z - SetSysClockTo56();
4 A9 X* m( _! l' V7 k+ q+ A - #elif defined SYSCLK_FREQ_72MHz
) N" A& L" y [2 f! S$ i' W - SetSysClockTo72();
/ x! W& e1 c8 P. r2 _5 Z - #endif/ a: b4 K1 w) y# j( L" B: g
复制代码
& a& K' c; j* W在system_stm32f10x.c文件中可更改宏定义改变系统时钟频率 - #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
0 b2 ?! D/ T# h - /* #define SYSCLK_FREQ_HSE HSE_VALUE */& ^. m) K A& R1 g; s
- #define SYSCLK_FREQ_24MHz 24000000
6 K: f+ a) k! G# B# B9 a - #else
@, \1 T: m1 b- v - /* #define SYSCLK_FREQ_HSE HSE_VALUE */
! c6 j3 m0 T8 D - /* #define SYSCLK_FREQ_24MHz 24000000 */
; f" Y' L* P# i8 {0 l - /* #define SYSCLK_FREQ_36MHz 36000000 */ n7 L& N4 O4 I) O- x
- /* #define SYSCLK_FREQ_48MHz 48000000 */- `, Z' Y( t2 w4 d( Y
- /* #define SYSCLK_FREQ_56MHz 56000000 */
( `# c4 B( [. k8 A1 L" H- u" \ - #define SYSCLK_FREQ_72MHz 72000000
0 T5 P# X# y6 N: w2 \" k0 x+ { - #endif
: t! I/ G/ n5 q! {$ O
复制代码
( r0 e d; M' ] m/ Q
% ]% F& ^, K1 L) n6 C- L5 k1、时钟树
" k @5 X* Q# R9 K/ j5 h
( T. j' n9 d" H( u* |
" p$ r+ f+ y$ R1 M% w8 H
主要时钟
9 q1 F- Q6 J( {2 @HSE:高速外部时钟,可由有源晶振或无源晶振提供,4-16MHz PLL以HSE为来源时可设置不分频或2分频 PLL:锁相环时钟源,可配置来自HSE或HSI/2 PLLCLK:锁相环时钟,可设置倍频[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] SYSCLK:系统时钟 HCLK:AHB总线时钟,系统时钟经AHB预分频得到,分频因子[1,2,4,8,16,64,128,256,512] PCLK1:APB1总线时钟,由HCLK通过低速APB1预分频得到,分频因子[1,2,4,8,16] PCLK2:APB2总线时钟,由HCLK通过高速APB2预分频得到,分频因子[1,2,4,8,16] , o6 h: a8 Z; n
其他时钟- USB时钟:由PLLCLK通过USB预分频器得到,分频因子[1,1.5]
- Cortex系统时钟:由HCLK8分频得到,用来驱动内核的系统定时器SysTick
- ADC时钟:由PCLK2经ADC预分频得到,分频因子[2,4,6,8]
- RTC时钟:由HSE/128或LSE或LSI得到
- MCO时钟:输出时钟,可由PLLCLK/2,HSI,HSE,SYSCLK配置
$ p, ]( I, J$ f1 R* u; Y, x9 N - O2 E; `; m, t4 L6 Q' i" K7 a: i
2、时钟配置
- B3 ]8 @5 g4 x9 C- E; c0 E- ?# ]$ q7 c
相关库函数
' m1 k/ o( w9 V" s配置函数9 m+ x$ z6 C. z+ D9 j/ b
- /*/ q9 W; y9 C* u4 m
- 将RCC外设初始化为复位状态. e% m0 G, b% Z* E9 n
- */
: h1 b. X4 U. R( x( f+ | - void RCC_DeInit(void); / ]5 l- N. c" e% a0 ^
- /*
# C8 H# B9 [ c+ ~2 O! b - 使能HSE,可选参数RCC_HSE_OFF,RCC_HSE_ON,RCC_HSE_Bypass " k/ g3 N+ H0 E6 Z: q3 U
- */
2 I7 j% x$ [. N/ ]0 U$ @! C - void RCC_HSEConfig(uint32_t RCC_HSE); . Y5 I* y. a+ Q) V" j1 b6 k3 t
- /*
- _' d$ [' ?9 `2 P- g* R - 等待时钟源启动稳定,返回SUCCESS,ERROR
8 k1 @4 a) S- e, @2 k - */
. X9 x4 I. t; K! _ - ErrorStatus RCC_WaitForHSEStartUp(void);
+ X1 V0 U" a! i$ t, {# Y, A# { - /*: R: Z0 {; M, F$ c6 D: e
- 配置PLL时钟源和PLL倍频因子* \0 S& k' `" ~
- RCC_RLLSource:RCC_PLLSource_HSE_Div1,RCC_PLLSource_HSE_Div2,RCC_PLLSource_HSI_Div2% w6 `2 M% r. Y$ X/ K
- RCC_PLLMul:RCC_PLLMul_2 [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
" c! W! B e3 B; v% }# | - */2 c4 M* e% {! w4 P
- void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);
6 l- S3 D, e1 X9 U& e0 h; w- ] - /*- K6 o8 Z8 H% ^6 J
- 配置系统时钟,可选参数RCC_SYSCLKSource_HSI,RCC_SYSCLKSource_HSE,RCC_SYSCLKSource_PLLCLK* G( c3 O8 ?5 b+ w- w+ W
- */: r" `/ N5 m, P8 o; u
- void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);3 [1 E: S. w7 E: W
- /*, v$ E: \, I2 z P
- 配置HCLK,可选参数RCC_SYSCLK_Div1 [1,2,4,8,16,64,128,256,512]1 g, n7 x$ n0 K" l: a
- */
3 R8 q4 t- E ~4 K( p" U& H - void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
1 s! z6 k9 D& m; T - /*
: V( Y5 H1 G: e9 L4 x0 i' r' | - 配置PCLK1,可选参数RCC_HCLK_Div1 [1,2,4,8,16]- {0 Q6 Q$ l) [: ~) t4 k
- */5 c. R& C& E+ q' U K9 s
- void RCC_PCLK1Config(uint32_t RCC_HCLK);' D) ^; U% |3 m, G1 r
- /*" y0 _9 E. U8 q
- 配置PCLK2,可选参数RCC_HCLK_Div1 [1,2,4,8,16]
3 w, Z& x( W% K" W - */( s9 ?1 e! x1 A" l
- void RCC_PCLK2Config(uint32_t RCC_HCLK);" u K( n: m5 z t8 A% ~$ o- E# n
复制代码
, h' u6 H, Q1 s" u, U8 K( u$ x0 s
使用HSE配置系统时钟- 1、开启HSE ,并等待 HSE 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟6 K3 Z% e% c8 w5 n$ g
' `1 K* ~& m2 w/ R. X5 ^使用HSI配置系统时钟- 1、开启HSI ,并等待 HSI 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟. Y3 E" {" _6 j5 L% K
- /* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:PCLK2, APB1总线时钟:PCLK1
1 J# a R+ ]* [7 p. o8 G: A6 w - * PCLK2 = HCLK = SYSCLK+ x# L/ N6 k4 r' r" Z
- * PCLK1 = HCLK/2,最高只能是36M; A/ a* y, m9 ]% O$ Q
- * 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]( W/ v: V* R) R M' j
- * 举例:HSI_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:4MHZ * 9 = 72MHZ
& F6 q0 Q" V& D - * HSI_SetSysClock(RCC_PLLMul_16); 则设置系统时钟为:4MHZ * 16 = 64MHZ
R* N; w9 D: _9 u) {/ z - *4 n6 _6 T1 ~* h/ B3 i: s% s
- * HSI作为时钟来源,经过PLL倍频作为系统时钟,这是在HSE故障的时候才使用的方法
) T8 \. ?1 z, T; D - * HSI会因为温度等原因会有漂移,不稳定,一般不会用HSI作为时钟来源,除非是迫不得已的情况
) c* p) Q- \" ^& o R9 B - * 如果HSI要作为PLL时钟的来源的话,必须二分频之后才可以,即HSI/2,而PLL倍频因子最大只能是169 s; [9 |" \+ b7 y
- * 所以当使用HSI的时候,SYSCLK最大只能是4M*16=64M
' N& Z- @) p, b% ~( X - */
/ c o" o+ O7 Q/ R& x# I/ W - void HSI_SetSysClock(uint32_t pllmul)3 \+ Y( U) x3 e3 N# r: n
- {
+ I6 e" {3 F8 |7 n7 T0 V# V - __IO uint32_t HSIStartUpStatus = 0;
$ q2 r- ]' \# Q7 ] - // 把RCC外设初始化成复位状态,这句是必须的$ V# U- u- r& p- X
- RCC_DeInit();- j2 A, S/ Q. I& l: x+ ~3 \4 _7 ^
6 I0 J1 q- y J/ a/ S; k6 P- //使能HSI" ? G$ Z# u6 f+ t$ b! B' P
- RCC_HSICmd(ENABLE);
' S5 l+ B. J1 D. Y J* ~ - // 等待 HSI 就绪
, Y/ l* m8 b" [- H$ j - HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;9 z( e$ M! y% T" Y# U2 k
- // 只有 HSI就绪之后则继续往下执行- k5 R& k; L1 I! T4 D/ N
- if (HSIStartUpStatus == RCC_CR_HSIRDY), u; {/ a9 J" j" V
- {
+ y- ?& d% A9 x2 ?: m+ S - //----------------------------------------------------------------------//
% |" P7 }! E* W; F - // 使能FLASH 预存取缓冲区. H5 c) }+ |% ]/ ?* T
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);+ Y e; v3 J" B
- // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
% G! j7 p# H) q3 ^( x) \' k - // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
8 D# U ?% M) S$ B3 H( b! S - // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了7 l0 a' }3 c" H+ I/ f+ d! [- c$ |
- // 0:0 < SYSCLK <= 24M
7 A2 I+ T+ Z% H/ Q( ?: O) m8 \( C4 F - // 1:24< SYSCLK <= 48M, P. W9 X" G! `7 Y6 D
- // 2:48< SYSCLK <= 72M
! ?+ O# d* t0 d# { - FLASH_SetLatency(FLASH_Latency_2);
/ P9 s& C# c' w, ]* `0 { - //----------------------------------------------------------------------//
d# s, s1 ] H6 u - // AHB预分频因子设置为1分频,HCLK = SYSCLK
. z" t- |6 j$ U' a- s8 m - RCC_HCLKConfig(RCC_SYSCLK_Div1);4 z' @1 X' z6 D+ K7 {! K
- // APB2预分频因子设置为1分频,PCLK2 = HCLK7 {- o3 R: H8 {$ }8 r& @
- RCC_PCLK2Config(RCC_HCLK_Div1);
2 D4 P* R4 G( y6 Y6 G - // APB1预分频因子设置为1分频,PCLK1 = HCLK/2
- \4 ], k+ b5 [1 d: b - RCC_PCLK1Config(RCC_HCLK_Div2);
% \ u0 ~' ~+ Q( q+ r2 d - //-----------------设置各种频率主要就是在这里设置-------------------//
' F' w$ C* `: {& t) D+ n6 | - // 设置PLL时钟来源为HSE,设置PLL倍频因子( ?7 z& D3 d6 y& ~/ F) C* o4 H
- // PLLCLK = 4MHz * pllmul
% z. j s4 T: U) l( c - RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);+ I \" q: p% x( G, F/ L, h
- //------------------------------------------------------------------//, f& \9 Y7 t! F
- // 开启PLL
* x5 e' A9 G, e' q - RCC_PLLCmd(ENABLE);
% v6 i: q5 B' S - // 等待 PLL稳定
. k% [4 f# |8 I+ Q0 o: O* s3 W# ?2 M$ }8 w - while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
! C7 u2 [) D0 ?3 c - { x# Z& d. D* D3 N
- }! U: Y2 W8 g) ^7 ~
- // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
8 D( r( j& |* u" D) x' w! q - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
; S' j6 m8 c1 w9 B" r3 z5 l7 U - // 读取时钟切换状态位,确保PLLCLK被选为系统时钟* L2 v- g: ?% L/ n
- while (RCC_GetSYSCLKSource() != 0x08)% ?8 P) ]& v& g) @ h
- {
5 W5 d' f/ |; `8 M - }3 ~' ^8 `( [, h7 h
- }4 c( Z- j F; ~* X5 H9 }3 e
- else! p% F3 r" \" X4 e, ^
- {
: I- i& s( R& f6 @ - // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
( r' r. w% c1 y5 g$ _ - // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,
+ R- h. N: G+ h1 ~0 O - // HSI是内部的高速时钟,8MHZ! F8 k6 ]% ?3 Z7 F2 C# J( J
- while (1)
8 |6 ~* Z9 g$ [7 v, x1 n6 S - {7 [, }. n& r" J" a
- }+ y- f1 G6 I; Y9 r, m4 p8 `, L
- }9 y+ [3 t: n7 h, q
- }
复制代码 - l0 U/ O) F! F4 e: R# V9 J0 u
8 J; j: l! X& C, e
MCO输出MCO GPIO初始化 - /* k' m! g$ ~$ N5 F. C; X
- * 初始化MCO引脚PA8
' @* q0 `4 W1 d - * 在F1系列中MCO引脚只有一个,即PA8,在F4系列中,MCO引脚会有两个) H. O$ [# C+ ~$ N
- */
0 D+ K: [' V& q' a V& @! T/ U - void MCO_GPIO_Config(void)! E0 s |: B3 P6 }; y! ~0 h' R4 q
- {# ?" u; i' f7 S0 X6 |
- GPIO_InitTypeDef GPIO_InitStructure;
1 Y8 b7 d5 W( z7 |; R! y, L - // 开启GPIOA的时钟
! P! A( B4 Z, p$ ^5 ^$ z7 | - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
& u) M7 m- E- {4 o - // 选择GPIO8引脚$ R* F/ C4 H8 ?# E) u/ _
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;; s8 g" Y" @9 Y9 J3 t1 Z w
- //设置为复用功能推挽输出( |* x6 {3 W( d6 h3 |# P6 |
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
2 f4 o7 Q* H1 u$ u3 j3 f - //设置IO的翻转速率为50M, _- m- v% v) P( j4 k- H
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
: [. Z8 T; B# c0 a - // 初始化GPIOA8
7 N1 a" d. ?4 |9 }( Y4 j. o; Z - GPIO_Init(GPIOA, &GPIO_InitStructure);
6 ?- B. c a6 M - }2 d# w7 ?1 l# Y, N, p
复制代码
* T$ s$ z' Z: u& e输出 - // MCO 引脚初始化1 r, ^7 D2 v- v/ Q- L5 n
- MCO_GPIO_Config();8 ?2 y, S ~/ |3 y1 S
- // 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号,# u. I% B8 l) ^3 m
- // 我们可以把PLLCLK/2作为MCO引脚的时钟来检测系统时钟是否配置准确! n. y+ t6 P$ ^" E1 v* K% R* d
- // MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK
) h0 G# u7 i5 y q- d - //RCC_MCOConfig(RCC_MCO_HSE);
# R2 |4 V" e* N$ d- t4 J - //RCC_MCOConfig(RCC_MCO_HSI);
% a: g, |8 b. d0 \' Z7 ~6 m - //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);6 X% D) p4 W0 |8 n% v
- RCC_MCOConfig(RCC_MCO_SYSCLK);
复制代码
1 |$ `: v% y6 u6 S7 P. t2 }7 u' N. l S. y* G* `
Systick系统定时器简介SysTick——系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。 因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。 相关库函数
1 U* ]+ \5 P% f% {, ^+ Q5 b8 y3 e# ?9 U/ `
9 l! z( S1 F: C2 J& `+ _9 F Q* [, _! G3 D7 @
|