0、前言 RCC-复位和时钟控制器 可以实现配置系统时钟SYSCLK,配置AHB(HCLK)总线时钟,配置外设APB1(PCLK1)和APB2(PCLK2)时钟 库函数的标准配置为PCLK2=HCLK=SYSCLK=PLLCLK=72M,PCLK1=HCLK/2=36M 系统初始化时会调用函数实现时钟配置。 - #ifdef SYSCLK_FREQ_HSE+ ]# C9 C8 E/ U6 ~. L6 D
- uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!< System Clock Frequency (Core Clock) */# r+ p1 L% T$ o( Y! c* a
- #elif defined SYSCLK_FREQ_24MHz" [; ^4 v- t& G6 `
- uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz; /*!< System Clock Frequency (Core Clock) */5 F& C$ w+ v/ s" w- G
- #elif defined SYSCLK_FREQ_36MHz
* Y c7 e! p: |8 {+ B1 e - uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz; /*!< System Clock Frequency (Core Clock) */! n; ?+ z: T+ l! @. @3 C% T
- #elif defined SYSCLK_FREQ_48MHz
, Q) {" E9 f" q; X0 C - uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz; /*!< System Clock Frequency (Core Clock) */6 z3 ~! E [" b7 w7 p
- #elif defined SYSCLK_FREQ_56MHz; I1 y9 `. h% b1 d
- uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz; /*!< System Clock Frequency (Core Clock) */1 a6 a- D8 V1 `/ d, W
- #elif defined SYSCLK_FREQ_72MHz
/ b+ E/ X: o% [; \! O; u2 _ - uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */
. V; R3 n @1 n& N
复制代码- static void SetSysClock(void)
2 N$ a4 j: H; Y r - {
# Z; [& K7 C4 k# J - #ifdef SYSCLK_FREQ_HSE
$ t1 V, r X3 w& e - SetSysClockToHSE();5 a, R7 y, ~" N- e- Y5 c9 |4 Y9 S
- #elif defined SYSCLK_FREQ_24MHz
: h9 m4 N- \( N1 i - SetSysClockTo24();; R% H1 T! a, {+ Y2 T
- #elif defined SYSCLK_FREQ_36MHz
" K9 E+ A) E- q - SetSysClockTo36();
2 e& A0 c0 j& Z- i - #elif defined SYSCLK_FREQ_48MHz1 ]) s) j( X8 X# i% H- K
- SetSysClockTo48();! _0 q2 E$ }5 ]/ C2 G5 O
- #elif defined SYSCLK_FREQ_56MHz* V6 r( V+ D# c, p( D& v+ I K
- SetSysClockTo56(); : a/ E1 \4 R4 g& M6 V3 u
- #elif defined SYSCLK_FREQ_72MHz
0 Q" |# Z6 f! g' F1 m - SetSysClockTo72();
+ Q8 y( }& j& }7 Y6 _ - #endif
! _ x* w. H \- ]1 H" }
复制代码
- c$ Z/ H4 G: Z: T5 W在system_stm32f10x.c文件中可更改宏定义改变系统时钟频率 - #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)% R9 E) y9 M6 n) w0 m$ J
- /* #define SYSCLK_FREQ_HSE HSE_VALUE */
9 v, ]0 M% L5 ?7 ?! v - #define SYSCLK_FREQ_24MHz 24000000* s _3 v6 y- C$ y! S4 S4 B
- #else- U2 a, q5 z( a& W& d, Y4 o
- /* #define SYSCLK_FREQ_HSE HSE_VALUE */
8 Y0 Q6 S6 c6 @ - /* #define SYSCLK_FREQ_24MHz 24000000 */
/ E0 w" m! G( ^& D - /* #define SYSCLK_FREQ_36MHz 36000000 */" \+ u: h4 V) }1 X3 w
- /* #define SYSCLK_FREQ_48MHz 48000000 */9 j- T/ |! G3 u( }
- /* #define SYSCLK_FREQ_56MHz 56000000 */
: ^7 [: b) d+ A' P( ^% Z - #define SYSCLK_FREQ_72MHz 72000000; ]0 y0 s( R C- ]
- #endif1 Y$ p" F- ]6 z; Z& t
复制代码 ) | V+ z3 B% U; }% P* g( y
# |0 B; K6 H9 y% k$ z, v- S1、时钟树
4 w* V" a7 T( L# E6 v+ P1 a3 K( q7 E, a( y
; }9 f7 i' W/ i' k' I主要时钟
! @& n- X5 ~2 \/ SHSE:高速外部时钟,可由有源晶振或无源晶振提供,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]
! V8 X# v& l8 L 其他时钟- 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配置" A: W+ D8 i+ Y7 O
$ k" c' _ T5 N2 M; ~
2、时钟配置
8 u) b& F! s. j$ A. l: M6 V; q! Q
& L1 c; Z0 w: A! z相关库函数
, a# b/ q- j4 h配置函数 [; r7 i" f8 X; X6 B( `4 N
- /*# l9 t0 Q; e/ t% f( Z0 y
- 将RCC外设初始化为复位状态9 m9 ?8 s j# _1 D+ {
- */
7 a4 P) ?/ _) Y* I3 c - void RCC_DeInit(void); + }3 Z& c. q7 F4 G0 t" G
- /*
* r. f' L0 ]# X - 使能HSE,可选参数RCC_HSE_OFF,RCC_HSE_ON,RCC_HSE_Bypass 0 u, x! x% D: s' V7 c5 K
- */! O6 u5 \6 `0 K' o( Z
- void RCC_HSEConfig(uint32_t RCC_HSE); - m) _1 h4 o6 Q# g( L
- /*7 S. l6 _. A( T5 o/ j
- 等待时钟源启动稳定,返回SUCCESS,ERROR
8 l1 r, V8 `, w2 j* `/ Y - */9 ]8 C b4 Z8 O4 l) h6 h1 L1 x
- ErrorStatus RCC_WaitForHSEStartUp(void);
% Q! J2 ~2 Y1 \0 s* K2 `9 S - /*
# C0 Q2 ^! L9 O3 {) a - 配置PLL时钟源和PLL倍频因子
) }2 P$ F7 k) ^/ W8 U - RCC_RLLSource:RCC_PLLSource_HSE_Div1,RCC_PLLSource_HSE_Div2,RCC_PLLSource_HSI_Div24 ]/ W1 Y# v$ T; i. E' |/ P2 Z3 H g
- RCC_PLLMul:RCC_PLLMul_2 [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
' }5 ]/ u; Z+ ]1 s, X - */
3 f' D/ p3 x7 d3 s% A- M - void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);. o4 V: |9 o; U, f
- /*
' I5 d0 g. K4 Z9 W+ l# g) ^' t( X - 配置系统时钟,可选参数RCC_SYSCLKSource_HSI,RCC_SYSCLKSource_HSE,RCC_SYSCLKSource_PLLCLK) y' T, s. [% p% W% \) b4 W9 S
- */: s& ^) b+ s% k6 ~
- void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);5 N3 \# ?) e0 x/ T! A/ }
- /*# I8 s. j; ?6 P0 R0 A* W% A
- 配置HCLK,可选参数RCC_SYSCLK_Div1 [1,2,4,8,16,64,128,256,512]
( |3 K) O. _: j/ `9 G$ r, c - */5 G, ^1 v0 @# l4 C8 Z
- void RCC_HCLKConfig(uint32_t RCC_SYSCLK);$ K: x& R. r m9 l9 R" h
- /*
) C" x! N, X& c# l8 ?) o - 配置PCLK1,可选参数RCC_HCLK_Div1 [1,2,4,8,16]
7 ]9 ~" x* @' ?0 m - */
& }- @- U; l) _ - void RCC_PCLK1Config(uint32_t RCC_HCLK);2 k5 L6 n* ]1 T3 M
- /*
/ E+ q1 \- u# Q, w- u. k - 配置PCLK2,可选参数RCC_HCLK_Div1 [1,2,4,8,16]0 G) r) e$ H5 Z- T5 C Z
- */: Y* }) H/ z% N6 R" G; {
- void RCC_PCLK2Config(uint32_t RCC_HCLK);- b- |2 |% x) f4 ~
复制代码
. X1 N' A+ c% |# q% m. y& m9 Y. B$ G \) ]
使用HSE配置系统时钟- 1、开启HSE ,并等待 HSE 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟6 H2 k! d( ~( p: ^8 d3 T7 e
- R8 @3 x; h6 J' S7 ~6 |使用HSI配置系统时钟- 1、开启HSI ,并等待 HSI 稳定
- 2、设置 AHB、APB2、APB1的预分频因子
- 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL稳定
- 5、把PLLCK切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟' g" S8 m; g. K z% |
- /* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:PCLK2, APB1总线时钟:PCLK16 W( ]7 D2 A) o' Y
- * PCLK2 = HCLK = SYSCLK
1 e3 {; y. }0 _" U9 s - * PCLK1 = HCLK/2,最高只能是36M, s# D1 R* f& ]0 f" h
- * 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16], {) N F% o$ P; c2 i( a
- * 举例:HSI_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:4MHZ * 9 = 72MHZ/ Q7 F: _1 \# E0 f" f
- * HSI_SetSysClock(RCC_PLLMul_16); 则设置系统时钟为:4MHZ * 16 = 64MHZ4 v9 G7 t1 p! @4 ?2 S" h
- *
" S. a5 |, h2 [" E4 Z/ Q - * HSI作为时钟来源,经过PLL倍频作为系统时钟,这是在HSE故障的时候才使用的方法3 V, B k, Y/ E' w F
- * HSI会因为温度等原因会有漂移,不稳定,一般不会用HSI作为时钟来源,除非是迫不得已的情况% |+ U5 E) N; Z4 b' L5 h
- * 如果HSI要作为PLL时钟的来源的话,必须二分频之后才可以,即HSI/2,而PLL倍频因子最大只能是16
* p: y! L! O7 j2 F F0 P - * 所以当使用HSI的时候,SYSCLK最大只能是4M*16=64M
5 f" z/ Q( z+ c! Z, S j - */8 N" K; ^; w3 P# O) e
- void HSI_SetSysClock(uint32_t pllmul)
% f1 _, W' f2 T" D8 T5 x- ^; c - {8 n: F0 W) Z' `- P3 g' \
- __IO uint32_t HSIStartUpStatus = 0;# z9 b! c$ _/ i N$ s
- // 把RCC外设初始化成复位状态,这句是必须的
2 R# u+ Z; U/ T# p5 r% a - RCC_DeInit();
; \. ~8 X( F$ R0 C8 R S# _- K
0 K4 T& \3 ^; N- //使能HSI R. Q6 E- {/ I* a: \
- RCC_HSICmd(ENABLE);
+ ]1 }( l5 h- g9 Q/ Q - // 等待 HSI 就绪' J7 [4 \! T7 B! x3 x/ V8 M
- HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;, `" }# b8 q: n8 H9 W- e8 h+ J
- // 只有 HSI就绪之后则继续往下执行
! C- s5 S u9 c4 P3 T- i) Y- z& W - if (HSIStartUpStatus == RCC_CR_HSIRDY)8 V, t; ^0 `3 u5 N4 ]
- {, _5 H& P4 o7 Z0 Y# J
- //----------------------------------------------------------------------//5 b+ O1 S4 D3 X/ D5 X4 h% Y
- // 使能FLASH 预存取缓冲区
6 l& z% q3 J, ]( s* F5 R. T - FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
) K0 W k9 k, j9 @ - // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
- F5 a3 ?& H* f- L" ~ - // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
% z: A- y" f$ R/ [' N$ u! g( i9 G - // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了: F3 h* t9 ? C7 D7 n9 S& |; m8 s
- // 0:0 < SYSCLK <= 24M$ w4 F. S( H# @
- // 1:24< SYSCLK <= 48M
* t- h$ y5 n7 \1 d: W$ Z - // 2:48< SYSCLK <= 72M
! h1 i( e4 `& J5 [7 Y - FLASH_SetLatency(FLASH_Latency_2);
' N! U& x8 u2 \+ l - //----------------------------------------------------------------------//
8 ]' m5 P( Z, S! Q% Q4 B f& ? - // AHB预分频因子设置为1分频,HCLK = SYSCLK% ?0 i. A: K3 `0 \% G
- RCC_HCLKConfig(RCC_SYSCLK_Div1);
* L% z& ~( d: W, A0 G6 w - // APB2预分频因子设置为1分频,PCLK2 = HCLK
. B6 W- E" F$ a$ I# [6 N - RCC_PCLK2Config(RCC_HCLK_Div1);
( d7 m4 r$ _$ F) C; \; B - // APB1预分频因子设置为1分频,PCLK1 = HCLK/2$ j v2 @* I) L5 o9 Z; v
- RCC_PCLK1Config(RCC_HCLK_Div2);0 {/ G8 Q1 y1 g9 Z* S
- //-----------------设置各种频率主要就是在这里设置-------------------//3 [6 m& R0 J" [2 ~/ F. O% `7 f
- // 设置PLL时钟来源为HSE,设置PLL倍频因子
0 ^/ {/ I# f4 A2 v5 a - // PLLCLK = 4MHz * pllmul
( b4 Q- c& E7 ^/ q - RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);4 L9 w/ J5 ^! _/ h7 ~6 X* @
- //------------------------------------------------------------------//
7 G' [- t5 e4 k" D - // 开启PLL
3 C( O* C: @4 C7 W% p" O - RCC_PLLCmd(ENABLE);
2 Y" I; C+ U. {7 L3 U - // 等待 PLL稳定- c( P( ^! b' i4 w6 K
- while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)# \( T& _7 ]1 N( i% w% ] w' x0 @
- {$ }" D n2 a( y2 i) [7 u
- }7 @2 j5 T% d8 A2 |+ d+ x' d( Z7 C
- // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK: F& u* d7 U; w% @
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);# L$ c# m9 v) \ c- f. g
- // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
% H( H/ }! s; d0 [; A2 E$ Q2 h" N - while (RCC_GetSYSCLKSource() != 0x08)
* A: I6 M& ~& s# K! p8 m6 ~ - {
4 ]. Y# g- T# g5 y1 n2 Q! ` - }
1 J8 ]+ t$ z& k9 O - }
* E* w( z4 ? D - else
9 _( e7 V% Y8 }8 E) ] - {& W- B, b! ], ^ w5 a
- // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理$ O: J9 Q+ L2 w2 V6 P, Y
- // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,
0 r9 r: _7 T H - // HSI是内部的高速时钟,8MHZ
9 z/ v8 {3 X p' c$ i - while (1)# o! j6 [, Z# C+ e
- {" ^8 N. ^0 C) U, g6 a3 ~
- }) \1 `7 I& R% ?6 _, z$ z9 D* Y3 I/ x
- }
$ f# f7 i! n1 b3 a( s8 F( j - }
复制代码 $ h1 ~; S/ X! x# G" [+ |! a
2 L9 {' Z6 @1 B7 i) y% b3 T7 T2 QMCO输出MCO GPIO初始化 - /*" j- ?6 c4 C/ Z2 T9 b% Q. Q
- * 初始化MCO引脚PA85 N6 u+ Y5 d' ^; _1 g$ L
- * 在F1系列中MCO引脚只有一个,即PA8,在F4系列中,MCO引脚会有两个 A! N- ]* g- `' g! C7 @
- */
2 }/ o: O" i; O& w- y. s* T5 N, g - void MCO_GPIO_Config(void)( z5 A, g: W8 Z f( }, p& ?% l
- {
# F1 e0 Q n1 n+ r! } z. l - GPIO_InitTypeDef GPIO_InitStructure;
+ G4 n& |/ y3 S4 v& u0 |: {8 r4 k# |6 t - // 开启GPIOA的时钟
+ M; W7 s; ~0 P6 G* N$ y7 U1 q a' ^ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);2 ~0 @* w4 P0 x# x. e3 B" b2 D
- // 选择GPIO8引脚
" ?7 s: e9 x1 s0 L, }2 Z - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
1 T) [$ ?/ X2 B - //设置为复用功能推挽输出
, C" v4 {4 _9 w& T6 q( T+ x - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;; v g5 s6 r* M+ G% ~) c0 t1 o( e
- //设置IO的翻转速率为50M* N, }$ U- n4 v0 u
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 a! f% ?: J4 ^/ ?3 d$ y3 x
- // 初始化GPIOA82 c' p5 G% ]$ q# f/ y# L5 {
- GPIO_Init(GPIOA, &GPIO_InitStructure);; i5 R, [, i. X5 f! u" H" ?3 J; ?
- }% p8 O/ ]5 h6 A: ]) g& ~# L
复制代码 ' W$ i! V( l& m f" V- Z
输出 - // MCO 引脚初始化5 w; a0 x9 \& H: l
- MCO_GPIO_Config();
6 s# V, H1 t0 x- j$ ^- F - // 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号,
& h) D8 h/ P# V: M5 z+ h - // 我们可以把PLLCLK/2作为MCO引脚的时钟来检测系统时钟是否配置准确
& @7 l, I. u0 P6 |1 B4 L - // MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK e" m) i, A6 z5 {
- //RCC_MCOConfig(RCC_MCO_HSE);3 m: q6 F! _, _/ |7 P
- //RCC_MCOConfig(RCC_MCO_HSI);$ O: Y( V% }* i! k. I
- //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
( c) ? T" f! O1 ~( Z0 q! y8 N# E - RCC_MCOConfig(RCC_MCO_SYSCLK);
复制代码
; ~! U4 S+ d! W/ y. W6 X2 W( R
Systick系统定时器简介SysTick——系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。 因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。 相关库函数
; `3 U4 g) _8 m3 b* D
% [# g: h& o' p( O$ c+ ^/ T
" S+ x8 I* ~% P! f8 N
7 Q) j j, w( g( x |