0、前言 RCC-复位和时钟控制器 可以实现配置系统时钟SYSCLK,配置AHB(HCLK)总线时钟,配置外设APB1(PCLK1)和APB2(PCLK2)时钟 库函数的标准配置为PCLK2=HCLK=SYSCLK=PLLCLK=72M,PCLK1=HCLK/2=36M 系统初始化时会调用函数实现时钟配置。 - #ifdef SYSCLK_FREQ_HSE
- h- _8 N4 r: t& ? - uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */% p( O% j! T* K' D! A7 q
- #elif defined SYSCLK_FREQ_24MHz
( Y7 o( k& k" d' b - uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */& t: K* f) P- f: f
- #elif defined SYSCLK_FREQ_36MHz
# _9 C: \2 [1 }7 k5 f( k8 G- H - uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */
/ `( i6 l0 C0 I- ]& Y3 Q! {7 f- i - #elif defined SYSCLK_FREQ_48MHz
/ f# a" n8 m+ m2 y) s" e" ^ - uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */ w- H6 q7 g6 t% d- R* Y
- #elif defined SYSCLK_FREQ_56MHz6 Y3 [7 I3 b4 p. D9 t1 m5 R" t% S
- uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */) Z) i2 s7 c$ ^* f n. P
- #elif defined SYSCLK_FREQ_72MHz( b7 Y" J: I/ g& m# s, A& C7 Q
- uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */
. j/ c% D0 Q5 x$ D( m/ t1 v
复制代码- static void SetSysClock(void); f+ D7 a+ \7 r9 N
- {
/ U' q4 }# x3 {, ~) U m2 Z - #ifdef SYSCLK_FREQ_HSE
1 h# U9 s- c7 F/ Y/ l - SetSysClockToHSE();
/ ~& y# |6 `8 x) i1 G0 ~9 y( j% e - #elif defined SYSCLK_FREQ_24MHz
, s6 b$ y: p3 Y) C - SetSysClockTo24();, [7 \5 R! ^6 k" v3 ?' t3 t0 N
- #elif defined SYSCLK_FREQ_36MHz
7 S! n# b* ?- I. j4 O6 p- _ - SetSysClockTo36();6 F* { ?4 m3 M* @: Y
- #elif defined SYSCLK_FREQ_48MHz- s. U) _, i+ a8 M3 X) j. C
- SetSysClockTo48();4 ?9 w) W( n* N9 `# _3 N0 H
- #elif defined SYSCLK_FREQ_56MHz! v/ R1 C5 J4 O' k' |5 B t3 W
- SetSysClockTo56(); 4 G# _: f1 e) ?, ^
- #elif defined SYSCLK_FREQ_72MHz
. M+ p( T# O( P J - SetSysClockTo72();( h* S \8 b$ ?& `
- #endif0 Y3 q9 Q% u9 Z9 A: f
复制代码
% {8 u" N$ t: M( L- W4 i9 V在system_stm32f10x.c文件中可更改宏定义改变系统时钟频率 - #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)! l6 E& q2 h& C+ z$ h V' D2 f( e
- /* #define SYSCLK_FREQ_HSE HSE_VALUE */3 k. B" `: h, ]
- #define SYSCLK_FREQ_24MHz 240000001 I0 E" U: C* ^# @! k
- #else
* }" ^# d/ C- f- m: H4 @5 Q& q - /* #define SYSCLK_FREQ_HSE HSE_VALUE */0 R2 p- g( H2 F4 y- d. m- n" V
- /* #define SYSCLK_FREQ_24MHz 24000000 */
( \+ A8 C6 A/ h& [* ~ B - /* #define SYSCLK_FREQ_36MHz 36000000 */0 Y9 t6 t' j/ Y; v' ~& G8 m
- /* #define SYSCLK_FREQ_48MHz 48000000 */
0 T! R# q8 s! y7 Q6 R4 u - /* #define SYSCLK_FREQ_56MHz 56000000 */! O/ X, j B8 r, a$ r$ ^
- #define SYSCLK_FREQ_72MHz 72000000
) O. S2 q" o' H$ T6 I3 }/ v - #endif
- Y% j! s; f/ l$ R, W7 k) K
复制代码
* N3 \) q; S( f$ g/ H
9 [9 I8 S% j! a4 n! u; v1、时钟树
2 Z' D, K' [: x t, _( o, F! ]2 d+ f5 V. f% m
* Y G* L ~* V+ U主要时钟! F8 a+ p s" I3 E' Z' C7 k( F
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] 8 w: a7 K* O8 [$ ?: @
其他时钟- 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配置# B1 X% u9 H4 }
7 f ~: k6 h0 f+ h* [( E9 ^6 T: Q7 M2、时钟配置9 s+ ?8 F( {( H) ^) V0 p, f( C3 `
2 N% a) c0 _/ B; G
相关库函数8 I4 K4 {) U4 L; I8 X! _' |
配置函数- S6 O0 B1 F6 b- c/ S
- /*
1 b" u( Z* F/ @, r - 将RCC外设初始化为复位状态5 y5 o: \5 @/ \% x4 S, A# u
- */# s/ J' j2 h4 }
- void RCC_DeInit(void);
4 S4 g @ N; O2 y2 w& F - /*: _2 O; L$ f: _1 W* P
- 使能HSE,可选参数RCC_HSE_OFF,RCC_HSE_ON,RCC_HSE_Bypass + J8 w; ~7 i( r& G2 T6 T" W7 o- F
- */3 |+ u5 ? s, O- R, j& y. {3 G; ] R1 ~
- void RCC_HSEConfig(uint32_t RCC_HSE); ' I; \7 z/ F: Y' A+ a" y
- /*
/ n4 Z5 ~- M5 C+ N - 等待时钟源启动稳定,返回SUCCESS,ERROR
+ w) D" p0 f! y: X - */ a5 j2 }1 t3 h1 L g
- ErrorStatus RCC_WaitForHSEStartUp(void);
% S8 H3 ^# Y4 u' S$ b - /*
! e! P3 p/ s1 z" r* @* \9 V9 | - 配置PLL时钟源和PLL倍频因子* m" i. C8 |" h' E* ?
- RCC_RLLSource:RCC_PLLSource_HSE_Div1,RCC_PLLSource_HSE_Div2,RCC_PLLSource_HSI_Div2: b. `* f$ q1 [: {; s
- RCC_PLLMul:RCC_PLLMul_2 [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]! O. o4 E0 N I/ t1 M* z7 g A
- */# w/ G+ F4 Y; n6 j0 o T6 O) d
- void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);
. ^- A$ Z' \' Q' y! {* z4 Z: H - /*# s- q. X y; q2 s
- 配置系统时钟,可选参数RCC_SYSCLKSource_HSI,RCC_SYSCLKSource_HSE,RCC_SYSCLKSource_PLLCLK; m' q' u, t. Z+ d; Z3 e" C O
- */
+ V, B/ @4 r O0 T0 m5 `+ s6 ~ - void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
. W; n, }; \" X9 G) a - /*+ c6 X4 B5 o, I1 d# k, v4 |
- 配置HCLK,可选参数RCC_SYSCLK_Div1 [1,2,4,8,16,64,128,256,512]
+ a9 H# w) V- v. H! Q$ ^& F N+ n - */" \0 k: M1 }4 m
- void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
! t" o& G) M0 ~2 H2 b; V" @ - /*
4 [4 b( C, @4 v$ i) E8 h - 配置PCLK1,可选参数RCC_HCLK_Div1 [1,2,4,8,16]. x" F" H5 Q) ? [/ y9 ^; t" L6 _4 \
- */
7 N4 A$ Q* Q% U7 [7 K# ] - void RCC_PCLK1Config(uint32_t RCC_HCLK);
5 t8 J$ [% H% V _ - /*: l3 x) R" K+ M# S
- 配置PCLK2,可选参数RCC_HCLK_Div1 [1,2,4,8,16]" V% A8 x8 @% G3 ^; F
- */) w; A0 i4 i8 K6 v/ `* H* o9 Q, k
- void RCC_PCLK2Config(uint32_t RCC_HCLK); D9 X& S9 Z8 h5 l, S
复制代码
) |% ?; z( }. ~3 C( H3 I
9 Z$ Z( Y, E" T1 {# _使用HSE配置系统时钟- 1、开启HSE ,并等待 HSE 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
4 Q/ z" j' p7 \8 r! u1 s2 ?4 _
) w9 Y2 g8 ?1 ?+ g' h( Q使用HSI配置系统时钟- 1、开启HSI ,并等待 HSI 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
3 B5 _; @5 m/ u: `
- /* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:PCLK2, APB1总线时钟:PCLK1
( `0 E6 T: T( N; s& P% Y# O3 D; p - * PCLK2 = HCLK = SYSCLK
. h2 p; C I# E! n. q- M( { - * PCLK1 = HCLK/2,最高只能是36M* F4 b/ T, n ]3 g6 @
- * 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]
, t3 U3 ^! }$ l' ~) G2 L - * 举例:HSI_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:4MHZ * 9 = 72MHZ6 B% T1 @( D+ f6 V. R" c( [
- * HSI_SetSysClock(RCC_PLLMul_16); 则设置系统时钟为:4MHZ * 16 = 64MHZ, z3 p: c2 V B B# x2 j6 C
- *% t1 W. ]$ N6 H# L, ]
- * HSI作为时钟来源,经过PLL倍频作为系统时钟,这是在HSE故障的时候才使用的方法
8 `" A# c& A" v3 x1 i: |% G6 ^8 v, D - * HSI会因为温度等原因会有漂移,不稳定,一般不会用HSI作为时钟来源,除非是迫不得已的情况
/ h" q" [( o+ u+ E9 }/ t - * 如果HSI要作为PLL时钟的来源的话,必须二分频之后才可以,即HSI/2,而PLL倍频因子最大只能是16. D8 c& l A4 g' H1 }# ]
- * 所以当使用HSI的时候,SYSCLK最大只能是4M*16=64M7 U. o" S e L: b
- */
6 ]6 V. ~9 e: O8 \% J+ ~" x6 X - void HSI_SetSysClock(uint32_t pllmul)
: s# w. T! x* v5 ?" g/ w - {: k: Q: J* x! I4 H6 N5 S
- __IO uint32_t HSIStartUpStatus = 0;8 ]- M( A! G+ ^# H$ ~
- // 把RCC外设初始化成复位状态,这句是必须的
( W' G! G' p4 a! K. l2 K' S - RCC_DeInit();
4 j9 I m3 [2 z5 J/ T8 l
6 g5 b, x* K' v9 [: F8 P1 g: S4 |- //使能HSI/ t# E3 G: _- r6 s5 A7 [
- RCC_HSICmd(ENABLE);
o4 M2 G/ i, I+ }7 B5 ]. a - // 等待 HSI 就绪9 }( E3 V6 C" \5 U1 Y4 a7 t
- HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;, t7 P' k5 ~, D1 C. q9 X2 y
- // 只有 HSI就绪之后则继续往下执行
5 \3 ~' q# @ O - if (HSIStartUpStatus == RCC_CR_HSIRDY)
( Z# e+ a, ~ q4 ^/ M; C4 D1 x+ E/ \ - {
- `4 j* e8 a% l' V+ C! k - //----------------------------------------------------------------------//+ B' f- f' \4 ]9 l, s# k/ }, @
- // 使能FLASH 预存取缓冲区! |- r" R6 m6 R; |: @" V9 p3 [
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);3 ^$ z, K# y- s. t: L( l
- // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
$ y: X+ h% ]2 w# T9 c" u9 D& t" R - // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,; B& ^5 J1 Y. ?9 d2 G
- // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了1 R# U8 P5 h/ I- d3 Y
- // 0:0 < SYSCLK <= 24M
$ m$ F1 q" q7 @. P$ v% ? - // 1:24< SYSCLK <= 48M
( d5 S; _* j0 F# b- u' `: [ - // 2:48< SYSCLK <= 72M+ v# d1 o4 V# d" W3 f3 ~5 m9 l
- FLASH_SetLatency(FLASH_Latency_2);
3 Y4 @& ^" L- O$ p9 C' V% G9 c - //----------------------------------------------------------------------//
" s, y2 b" Q1 s3 \* Y8 J$ g3 o7 _ - // AHB预分频因子设置为1分频,HCLK = SYSCLK
5 i. P; R/ M: F8 A% P- ^ - RCC_HCLKConfig(RCC_SYSCLK_Div1);
& J m& e) w: M$ V# ]( l - // APB2预分频因子设置为1分频,PCLK2 = HCLK
% ?+ y3 C4 n% ?4 h3 h( |: a6 e; L - RCC_PCLK2Config(RCC_HCLK_Div1);
3 F: D6 q- ~; n' B; R - // APB1预分频因子设置为1分频,PCLK1 = HCLK/2
* A6 V. U( w4 q3 b% B6 m& r - RCC_PCLK1Config(RCC_HCLK_Div2);: C6 t3 b: N& H) A, X
- //-----------------设置各种频率主要就是在这里设置-------------------//
8 j4 Y) P6 R6 V2 Q0 I - // 设置PLL时钟来源为HSE,设置PLL倍频因子% j# q6 \9 j* @2 m
- // PLLCLK = 4MHz * pllmul
) K: m6 \! f7 s) p- ?6 Y - RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);/ y7 _% s0 @2 u& f/ C5 @
- //------------------------------------------------------------------//$ Q- n# E( k. F O0 S1 U
- // 开启PLL9 J9 D) z/ ^0 ~# b+ w6 E
- RCC_PLLCmd(ENABLE);
) F3 R3 S4 B3 R2 J7 z4 ~& | - // 等待 PLL稳定
. [/ b7 x; R4 V$ c7 f- ]; t - while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)! b# p$ R5 @4 ]+ \) J2 M' _. d
- {1 |8 o* A) [# f* I) u2 Z' E
- }% B( r' ]' W8 d2 g2 A' L- `6 V
- // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
% @' [7 l$ X! B" D+ h* K3 S - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
: R# u/ G0 F+ w8 V3 M2 }$ i: A4 T - // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
7 t7 {/ v0 S$ O- k5 W0 f - while (RCC_GetSYSCLKSource() != 0x08)
. N$ Q8 u+ j. u1 q" l6 x y5 g! T) h - {, H) L& g; O W2 w/ U
- }
6 D |* H+ r; T - }
' a1 L; ~1 T: d7 F - else
" C5 M/ y$ m; U I - {9 V# }3 p3 e0 d% Y+ V
- // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
6 \! G q" A2 n - // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,# z9 G' e u/ N7 i. i
- // HSI是内部的高速时钟,8MHZ* e7 P( C% o) f- e
- while (1)
# P/ q P! W9 I+ w - {
2 V# r$ Z, |! f6 h6 g - }
$ K0 U2 V3 C! T% v, y - }
/ k) g* ~! F7 U7 Y; }* A - }
复制代码 9 x1 v/ P8 ]7 H
$ m$ e$ s/ G5 e4 F) [7 k( n$ yMCO输出MCO GPIO初始化 - /*# n4 g; T, U* \' O ^: D
- * 初始化MCO引脚PA8 r' U# ?6 Y: {$ b+ Y
- * 在F1系列中MCO引脚只有一个,即PA8,在F4系列中,MCO引脚会有两个( X+ w0 b c# N/ L
- */
- Y$ k3 i% F6 O w - void MCO_GPIO_Config(void)
2 r: \& C% f& }0 ~5 M8 H2 r# J. a - {
1 e* R( Z R) Z3 D; B* b - GPIO_InitTypeDef GPIO_InitStructure;
$ H8 t# n9 U3 f: N* b; L' W - // 开启GPIOA的时钟( W& G' p5 F9 f
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
* }" y+ E- {6 F4 I - // 选择GPIO8引脚- @- a6 p8 q# C- Z, W* }% N: z
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
1 ]7 q& |( w* H8 V5 |/ Y/ a# ? - //设置为复用功能推挽输出
) a0 C' L3 r3 l5 {3 q! r6 Y5 P - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;% F/ @" j4 |: U* f; \0 A
- //设置IO的翻转速率为50M
; n8 I5 ^( o. {0 V, Y6 i) m+ F - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 T! t x1 [8 ~: |' o
- // 初始化GPIOA84 I# U& Q% n1 C% Z( ^) M! m
- GPIO_Init(GPIOA, &GPIO_InitStructure);" B3 u( F* q( _ |& V
- }
; d+ n& t. L; D1 a$ B! l4 x+ ]. T- w
复制代码
9 o6 ^* E. J$ V2 E+ ]输出 - // MCO 引脚初始化
8 e" S$ o% m! y) |9 r, g0 K8 O3 H+ s% k - MCO_GPIO_Config();
9 M6 Y0 D* u1 Q% L3 v: g - // 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号,2 t, I N/ {0 R: ^# m. d) W
- // 我们可以把PLLCLK/2作为MCO引脚的时钟来检测系统时钟是否配置准确) r. i: N" I$ r; _
- // MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK1 p" U" C6 c9 w1 r) x* A
- //RCC_MCOConfig(RCC_MCO_HSE);
. T/ q: [3 M9 |9 ~ n% H& s - //RCC_MCOConfig(RCC_MCO_HSI);
9 d0 i6 I/ s$ g" P$ |: [" R - //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
, A% k) G! k- }, Z6 }+ B7 x - RCC_MCOConfig(RCC_MCO_SYSCLK);
复制代码
! E- |- {: J5 r
) |( M- d$ C X- r1 fSystick系统定时器简介SysTick——系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。 因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。 相关库函数( _4 V, c. D( C* D/ N" ^+ y
8 i2 N$ @ u% G' X* S; {; z; R! b0 H
( f3 z/ E. J7 @) Q& E: o4 @! H& K! {5 f" x
* F3 [' S8 j$ o( B4 i' }0 s |