1、时钟树框图8 g, Y; |2 ?2 I3 ^, b
系统时钟的选择在启动时执行,即程序在上电复位时,先运行启动文件startup_stm32f0xx.s,该汇编文件中先进行了系统初始化,在系统初始化的时候进行系统时钟(SYSCLK)的选择与配置,当系统初始化完成后,才运行main函数。在复位后,内部8MHz的RC振荡器(HSI)被选为默认的CPU时钟。用户也可以选择4-32MHz的外部时钟(HSE)作为系统时钟,选择HSE作为系统时钟时,标准库函数需要用户自己进行修改,HAL库的话用户可以直接配置。当 HSE 故障的时候, 且选择 PLL 时钟作为SYSCLK, 那么当 HSE 故障的时候,不仅HSE 不能使用,连 PLL 也会被关闭,这个时候系统会自动切换 HSI 作为系统时钟,此时SYSCLK=HSI=8M,如果没有开启 CSS 和 CSS 中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样。如果开启了 CSS 功能的话,那么可以当 HSE 故障时,在CSS 中断里面采取补救措施,使用 HSI,并把系统时钟设置为更高的频率。+ A5 c9 f/ X- m: U
允许应用程序通过几个分频器来配置 AHB 和 APB 的频率,AHB 和 APB 的最高频率为48MHz。6 E" o' @1 U+ T, e
& I b9 g' Y& d' V2 K6 E& o
0 m" d7 n5 u. z) \
; \2 c$ d9 O# N4 Z- [/ u# Y上电复位时,启动文件运行如下程序:2 {# l) @6 H/ K' Q: m5 {
. f1 w, z# n S v8 M- ; Reset handler routine 复位处理程序. M' p4 L& a( N( B8 |$ i
- Reset_Handler PROC; e. A J/ l9 _1 X: O
- EXPORT Reset_Handler [WEAK]
7 K9 Y! f1 m- m! d - IMPORT __main
' @0 ?" H0 ?* p! D2 ~: m - IMPORT SystemInit 3 u3 a+ R& n- N9 ] c! F% ~3 Q
- LDR R0, =SystemInit3 C$ G! j1 K" A5 E- U
- BLX R0
) _8 l1 r/ C& q4 y. `5 i5 {8 m/ v - LDR R0, =__main/ y2 j3 k0 K; C% T( G/ N' T, O. z: T
- BX R0
) i7 l) K% i% n# F- r6 o - ENDP
复制代码 7 l; l/ x7 {' l+ T' C
上电启动过程中,先调用了SystemInit()函数,再进入main()函数。" b/ ?& c7 h- z; k n4 F( t/ O
SystemInit()函数在文件system_stm32f0xx.c中,它的作用是在单片机上电复位的过程中,会来调用这个函数设置系统时钟SYSCLK,默认STM32F0系列单片机时钟最大设置为48MHz,所以系统时钟默认是已经配置好的,无需用户自己进行配置,除非有特殊需要,用户可以进行修改。- v$ G& H I. P+ j7 _ ]2 r6 R7 I
% H- O" R7 D9 C4 W/ E. n
2、时钟系统特性- d I- O- D# r: f
系统时钟(SYSCLK)有以下三种选择:
/ q, x( W. ^/ O+ X9 F, m9 t) a* H1 Z9 ~' _. ^5 x8 t+ V
HSE(外部高速晶振):晶振范围(4MHz~32MHz)
1 V( k# K8 S8 \8 B" v8 S可被外部时钟源旁路,意思就是不经过分频和倍频直接将外部时钟源选为系统时钟 SYSCLK,外部时钟信号范围1MHz~32MHz2 L- g& b/ W1 g4 |# v
HSI(内部高速RC振荡器):8MHz,出厂已校准到±1%的精度# d8 w% T) u! T/ S3 k6 Z
PLL经过2,3,… 16倍频(所有输出的最小频率16MHz)% }1 ^9 i$ t1 l4 T$ l$ C' A
其他时钟源:% F; X' t( a# i% y' ^2 f% ]
9 L6 ^) l) d2 n, P2 L2 VLSI(内部低速RC振荡器):~40KHz3 J* f* m* }+ r$ e0 e @3 L
LSE(外部低速晶振):32.768kHz. d! J& G W: I
可悲外部时钟源旁路,可以直接给RTCCLK提供时钟,最高频率1MHz1 R; ]5 G- E c) B) @' `# x% N5 O% S: K
外部晶振的驱动能力可配置(功耗VS起振能力). T; g6 p7 h' x* s
HSI14(内部高速14MHz RC振荡器):专用于给ADC模块提供时钟
( B' ]6 _4 ?1 e# U; K) Y4 u. \+ @) l
3、时钟参数一览
! ]" ]2 u) \0 K; Q+ E& C* k7 ]3 u% S6 B
2 ?: G1 k$ @; K2 X e
. d9 J6 n6 ^* X8 I, N3 v. d0 J2 {3 t
" K3 ~* j& D2 A! Q8 D4 l6 `4、时钟配置案例
9 I7 j1 C. z2 u9 @使用标准库进行系统时钟的修改,为什么说是修改,而不是说配置,如上所述,因为程序来到main函数之前,启动文件:startup_stm32f0xx.s 已经调用SystemInit()函数把系统时钟初始化成 48MHz了,SystemInit()在 system_stm32f0xx.c 中定义,如果用户想修改系统时钟,可参照SystemInit()函数中调用的SetSysClock() 函数,自行编写修改程序,官方文件一般不进行直接修改。
6 O9 @( d7 N8 O6 H" g" L以下代码为system_stm32f0xx.c原文件中的 SystemInit() 函数和 SetSysClock() 函数,源代码分析:
1 K# W! X, Y7 d- [$ y. v. U( X
- void SystemInit (void)) w: ^$ M2 c/ C9 W$ U& u4 |- ]" f8 |
- {
* C9 u: l; j' V5 v& i3 R$ t - /* Set HSION bit */
. k* ]9 s6 v0 ]$ Z) d. e6 X' } - /* HSI时钟使能,开启HSI振荡器 */4 [5 A9 H9 E& ~( \* O ]! |0 @
- RCC->CR |= (uint32_t)0x00000001;
! T) a" m) P5 g' G - 5 x" g0 w K3 M8 `4 h
- #if defined(STM32F051)
9 C+ X; ~/ d" E0 k8 o9 J. A* ?' z - /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
# s2 k! @7 h J- E3 ~! i* B% ~7 t! h - /* 复位 系统时钟切换, AHB总线的分频因子, APB总线的分频因子, ADC的时钟频率 和微控制器时钟输出选择 */
* ]- A, E3 a R! `" Y - RCC->CFGR &= (uint32_t)0xF8FFB80C;
' k4 M$ p2 A0 b# g' h, d - #else4 J1 O5 B* a) x* D
- /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
7 J! c) c7 f7 ]) F3 K4 z) J# ^- D, G - /* 复位 系统时钟切换, AHB总线的分频因子, APB总线的分频因子, ADC的时钟频率 ,微控制器时钟输出选择,微控制器预分频选择,/ @- w) v6 h$ `5 r, ], o7 f! E
- PLL左时钟输出时是否要分频 */6 m3 H7 [ v2 c
- RCC->CFGR &= (uint32_t)0x08FFB80C;) S0 p3 u1 A' v! P7 J) [2 z H7 t
- #endif /* STM32F051 */
7 r5 d( ~" l' z) f- m
D/ y# B- s0 d* e4 m" e- /* Reset HSEON, CSSON and PLLON bits */% E) S. H" x* K6 r
- /* HSE时钟使能位, 时钟安全系统使能位 和PLL使能位清0 */
' [7 u4 O; Q" U$ d; @, ` - RCC->CR &= (uint32_t)0xFEF6FFFF;
0 x, @; E9 _4 k" o
1 ^6 }$ m6 p+ L: f0 @7 e+ d- /* Reset HSEBYP bit */ m$ Y! j$ _ V. V
- /* 外部高速时钟旁路清0 */
( J) P" ?5 a- m! t: X0 T - RCC->CR &= (uint32_t)0xFFFBFFFF;
+ z% _2 C' l3 q - 7 ?5 { W3 C& \; }
- /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */* X& w. C% J- O$ D3 v0 ]& _
- /* PLL输入时钟源位,分频HSE后作为PLL输入,PLL倍频系数清0 */
" M( I$ K5 h% \6 O - RCC->CFGR &= (uint32_t)0xFFC0FFFF;5 t: U- A: g" t* g2 Y& W% ?0 k
( x0 v; O5 E) z3 w- /* Reset PREDIV1[3:0] bits */
1 d5 q0 @1 s4 s ] - /* PREDIV1分频因子清0 */
5 i* q; Z3 _" \+ A - RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;
3 z4 Z' N m5 @( F3 r5 [* H' j - 0 y8 t3 a) t6 p6 P2 b9 X. [
- /* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */+ [" N: a6 A. ?" I6 K
- /* USART1时钟源选择位,I2C时钟源选择位,HDMI CEC时钟源选择位,ADC时钟源选择位清0 */. w& d* v& t( {5 ^' [1 }$ g
- RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;
' O* J* F5 |' W( D! R! X. e" P
, C) i/ v3 N' [- /* Reset HSI14 bit */
! ?' l' p; v8 @. [) a, s8 D5 x - /* HSI14时钟使能位清0 */
1 K9 b- y7 W& @6 B) t0 k' M# L - RCC->CR2 &= (uint32_t)0xFFFFFFFE;
! D* F9 r" n% I3 L5 j - - _* F' ?0 p/ e' R% i) h2 q
- /* 禁止所有时钟中断 */) `$ T2 w1 X( M% M
- RCC->CIR = 0x00000000;
$ o! A8 o# O7 t* V
, k6 A: N+ ?" R- u- /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */! j' r+ F5 b/ J y
- /* 配置系统时钟频率,AHB/APBx分频器和闪存设置 */9 {1 g- B7 r1 P1 j# T- a; b
- SetSysClock();3 T4 L2 ?) t# {) c
- }. {0 d2 Q2 S. X
( g7 z' u' E1 \" a( x; I8 H- ===========================================================================================================================
' d7 `8 d: t d4 X. M1 I
1 o5 e' {; n8 ~1 J# _- static void SetSysClock(void)
+ W7 }0 c" F4 F - {
$ U! X# {# _+ W8 X3 m - __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
( o; H+ m9 A+ a7 G$ R+ h5 m* E - ! q0 ]: B' n/ {" v' M
- /* SYSCLK, HCLK, PCLK configuration 系统时钟,AHB总线时钟,APB总线时钟配置-----------*/( q+ [/ {/ g7 L9 V1 B3 ~( Y
- /* Enable HSE 使能HSE */ ( @! Z/ l- R! F- t5 l
- RCC->CR |= ((uint32_t)RCC_CR_HSEON);: ]1 c4 u7 Y3 N3 e$ s+ N5 ~' J7 ~
- ; x+ M4 M6 y4 I( x
- /* Wait till HSE is ready and if Time out is reached exit 等待HSE稳定,如果超时,则退出 */: c% J+ Z& Q. y" Y
- do
9 O2 @6 c* @7 l& \ - {6 G* J! F& f0 Y
- HSEStatus = RCC->CR & RCC_CR_HSERDY;% A Q: B; P2 s2 X; A, _7 y5 ^( F' l
- StartUpCounter++;
% W, p+ e/ q6 Q0 s7 D+ k7 K1 c - } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));5 n3 d& H4 o) t+ ?) F1 P7 S
" V# f1 a3 v5 m- /* 到这边要么是超时了,要么就是HSE稳定了,准备好了,再次判断HSE是否准备好 */8 M+ \" r" x! e' d; F. ?/ Q. D
- if ((RCC->CR & RCC_CR_HSERDY) != RESET)& H7 b8 |+ L H
- {
1 [7 h0 _, w6 o - HSEStatus = (uint32_t)0x01; // HSE状态标识置1(稳定了)* B( A2 {0 R, L% m
- }# D$ G3 p* R9 |- w+ R; c( q
- else
( Q C( n: r* \; ` - {
4 L% P; x5 E, U1 d" e& o% Y - HSEStatus = (uint32_t)0x00; // HSE状态标识清0(没稳定)+ q) L( r/ L3 M/ ?2 h" e
- } & J9 g# U3 B- Z( f; f" }
- & n! v- F" J g4 A; O
- if (HSEStatus == (uint32_t)0x01) //如果HSE稳定了/ X D" m2 a( \, l9 D) l
- {3 f) b5 P: E" h* s/ @1 c: a( n
- /* Enable Prefetch Buffer and set Flash Latency 启用 Flash 预取缓冲区 和 设置Flash延迟*/& X% K% _7 V9 \: l) N' x
- FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
$ j6 l# g5 Y: d/ ]5 o* }7 p - . }+ {* c# ?+ L2 }1 Q
- /* HCLK = SYSCLK AHB 预分频因子设置为 1 分频, HCLK = SYSCLK */% \) c) S4 ?% X3 Z( g \- r
- RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
( a' d V5 Q# Z- c/ Z+ d - . N* h+ g. A- B/ O, ~# @' B) F
- /* PCLK = HCLK APB 预分频因子设置为 1 分频, PCLK = HCLK */4 _. i/ Q) a; m1 T( g3 p
- RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;
3 w% F+ z7 `+ N ]9 p( ~7 M g4 Y
9 U$ O& k, l0 N- z; _; W3 {- J6 i- /* PLL configuration = HSE * 6 = 48 MHz 设置 PLL 时钟来源为 HSE(外接了8MHz),设置PLL不分频,设置 PLL 6倍频 */
+ o5 ]7 m: Q: }+ | - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
9 o5 }- O7 R6 v7 k+ v8 _8 [ - RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);
& w6 }9 {7 E# N1 ^, N2 H: N/ u - + |7 O8 b% i9 ^
- /* Enable PLL PLL使能 */" B0 M4 e' `2 \; ?! c' @# ^% P
- RCC->CR |= RCC_CR_PLLON;
m3 i6 Y+ J' R. k - 6 ~1 n9 ^$ a4 A# j6 j0 @7 _
- /* Wait till PLL is ready 等待PLL稳定 */
8 t e4 a- V2 J4 o- I5 h( R - while((RCC->CR & RCC_CR_PLLRDY) == 0)7 `0 D' k7 m5 a
- {8 p! e/ [! C3 u
- }
7 L7 ^' Z( j& x3 \( q9 p+ a0 C( p - " f, [3 L$ a) U3 x7 m
- /* Select PLL as system clock source 选择PLL作为系统时钟源*/
4 ^7 f. A- @$ A - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));9 g7 ~+ D; L0 N
- RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 1 w3 a: t9 s% K
! e$ k7 z( i* y- /* Wait till PLL is used as system clock source 等待PLL作为系统时钟源稳定 */! A, R! d9 J( o- j
- while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
) M. h- D5 U" w, ^4 B% u: B8 u* e - {
! L0 ]& z! o: V# {; D - }
! v, L) k( `+ |; L/ S2 `; E - }+ `7 w! h6 E) j. @8 L6 H
- else
, m1 ~$ N$ y# T& K) X - { /* If HSE fails to start-up, the application will have wrong clock 如果HSE启动失败,应用将使用错误的配置0 K" ]1 V4 X& _
- configuration. User can add here some code to deal with this error 用户可以在这里添加处理错误的代码 */
1 Z' O$ f0 ^" g$ U3 t3 h1 j& Z - }
$ E8 U* I. W2 [' z - }
复制代码 . @2 \" t- h7 N+ P' U6 S
以上分析了标准库函数中配置系统时钟的程序逻辑,下面介绍当用户需要修改自己的时钟频率时的处理方法,% e* r' i# ^4 ~
' f. ]! f- m6 J3 I, f4.1【标准库】的配置方式
5 b/ t) `. d* x! D4 D; |& F u( ^( V步骤:
4 u5 v$ o }) A1 g1 t- b1 s9 J1、只要参照 SetSysClock() 函数重新编写一个自己的配置系统时钟的函数8 o2 G& v5 o5 F* O/ _
2、在main函数中进行调用,调用语句需要放在main函数的函数体第一行,以确保在他初始化前已经重新设置好了自己需要的系统时钟
$ I. P* A) d3 n: z9 l% t* s5 ~
1 M* A! a5 W! \& D1 ?: c- /*========================================= 自定义 HSE 系统时钟 =========================================*/
8 }' L* C, P, M - /************************************************3 a+ G# f! W: F1 v) L" ]
- 函数名称 : HSE_SetSysClock) T, l* F: B4 Z( R' d% k
- 功 能 : 外部高速时钟配置
" ?* [, I, j6 H* `8 e# [+ ?" f - 参 数 : RCC_PLLMul_x:用来设置PLL的倍频因子,在调用的时候形参可以是: RCC_PLLMul_x , x:[2,3,...16],这些宏来源于
! V3 v& f. | r1 a# S6 E - 库函数的定义,具体功能是配置了时钟配置寄存器 CFGR 的位 21-18 PLLMUL[3:0],预先定义好倍频因子,方便调用
S# K9 B9 w; b5 _ - 返 回 值 : 无9 [( F" @/ y- a5 i$ C3 B
- 作 者 : JayYang3 K( V3 d; H# u z' i4 M
- *************************************************/$ k1 S3 x( J) x" d, b4 ?9 R5 V
- void HSE_SetSysClock(uint32_t RCC_PLLMul_x){
4 g: L& c( f$ y& s% X - __IO uint32_t StartUpCounter = 0, HSE_StartUpStatus = 0; //定义启动计数参数,以及外部高速时钟启动状态参数+ V; b. n% g7 M+ F
- + e6 a# m# y1 X7 W2 L
- //把RCC外设初始化成复位状态,这句是必须的
/ X/ ?* T" c& N' b5 ^- Y- ^& _ - RCC_DeInit();
( v r; z3 k7 ~: I
8 K# c" D$ `- F, D+ c- //使能HSE,开启外部晶振,本人系统中用的是 8MHz
$ F+ J1 [* }7 }3 U, O: x - RCC_HSEConfig(RCC_HSE_ON);
9 M3 b" Q) v6 p5 ^$ `% F( J" U - 3 [9 a. [/ K( \; j7 R- H
- //等待 HSE 启动稳定
; w4 H1 {5 p, D, v$ ` - HSE_StartUpStatus = RCC_WaitForHSEStartUp();
4 n( l* Z1 `; t/ x; [0 ]
9 w u" s6 I5 `- //如果 HSE 成功启动并且稳定下来
1 r6 ]: m* U1 R - if(HSE_StartUpStatus==SUCCESS){
N+ b$ @0 u7 e( t! } - ! U0 s: ?: {8 z3 s
- //-----------------------------------------------------------------//
8 Q0 [1 G& i4 H! k9 J- r1 t, M - //使能 FLASH 预存取缓冲区+ ~ f6 ~, {, Y. R
- FLASH_PrefetchBufferCmd(ENABLE);/ a: A, N" o1 o" G# `7 ~- X9 t+ s
7 S* s5 m E" x$ _) f- // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 1
$ P* i# U3 i) w0 l( G - // 设置成 1 的时候, SYSCLK 低于 24M 也可以工作,如果设置成 0 的时候,
' W8 h/ q& G6 E7 l/ U( r - // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
" {9 g0 A: X/ t- y - // 0: 0 < SYSCLK <= 24M
2 l4 T/ I1 R+ E* H# Z" p0 M4 ~ - // 1: 24< SYSCLK <= 48M* A1 I3 E! ?3 b& H
- FLASH_SetLatency(FLASH_Latency_1);6 m `& c. ]" x r4 l, B8 W7 }
- //-----------------------------------------------------------------//
9 A! i$ ]1 c- K. g y- u( O N - //AHB 预分频因子设置为1分频,HCLK = SYSCLK
' Y" ]9 R: h+ f0 a - RCC_HCLKConfig(RCC_SYSCLK_Div1);' s) @" l* @: V, z- I8 S
- ! E/ R, z" w0 K) o/ L3 Y
- //APB 预分频因子设置为1分频,PCLK = HCLK
. `5 j9 `6 U1 v$ Y( |' R - RCC_PCLKConfig(RCC_HCLK_Div1);, }/ B5 Y2 q- @+ A, r1 {6 K/ D
- //-----------------------------------------------------------------//
6 s$ k( M0 _7 \- l) V0 }* b7 x* y9 e - + I( m; |" j. h8 R* ?
- //-----------------设置各种频率主要就是在这里设置-------------------//0 @# T# A* ~$ M1 c9 h$ f4 R
- // 设置 PLL 时钟来源为HSE,设置PLL倍频因子
$ q" o8 {% W# L - // PLLCLK = 8MHz * RCC_PLLMul_x6 @, r! V; W! Z0 g5 O
- RCC_PLLConfig(RCC_PLLSource_PREDIV1,RCC_PLLMul_x);' a2 \) Y& q- n# {4 d P$ S
9 L1 L' K+ Z$ b$ B* t* x- //开启 PLL2 F8 K; S8 N4 i
- RCC_PLLCmd(ENABLE);* }" ~' t7 m2 F9 b
9 c% I5 i9 A1 F3 i! Z; F- //等待 PLL 稳定
7 O( {: ~0 U j: k. c" i5 I) T$ X - while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); k3 f+ @6 T$ E
- % J( X( V; ^" L5 J( q2 g6 p
- //当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
. ]7 N: e9 G9 `( v - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);% D( d2 i [, K# P& V- F. |
- }
8 [) F- W! |" ~& I. p r% \2 X, g - else{
' {* R% h; \- y) a - //当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI 是内部的高速时钟,8MHz2 T: Y; @) n+ T* ?! e/ p3 c
- //程序就会来到这里,用户可在这里添加出错的代码处理/ J8 e# W- i. T
- while(1){( d! R. `" w7 q" Z
- }
/ p' s7 s1 V( g$ g1 p' q - }
4 E3 G! A# C9 c - }& f$ r) x" S+ A' ?: H4 D" D
- & C# S' q9 _8 m) a( G3 D
- /*=========================== main.c主函数调用 ===========================*/, J% \2 j* {9 \- y6 T+ s& ?
- int main(void){
" _! d+ l8 ]6 ?4 H& m! K- r( [ - HSE_SetSysClock(RCC_PLLMul_9); //设置系统时钟为: 8MHZ * 9 =72MHZ。
/ O: @, v4 e3 ^+ x3 j9 b: p - }: |; n7 ?+ G! t4 k- ]
- - z ?3 v; @+ q; f
, h) ]" \( d$ o9 f- /*========================================= 自定义 HSI 系统时钟 =========================================*/+ X- I( t k6 `5 w4 g
- /************************************************$ n4 ^8 f' ?, u* D& G* l5 I
- 函数名称 : HSI_SetSysClk
+ `! z( }* Q x9 U/ }- D" } - 功 能 : 外部高速时钟配置. G8 x; n+ I. h9 q
- 参 数 : RCC_PLLMul_x:用来设置PLL的倍频因子,在调用的时候形参可以是: RCC_PLLMul_x , x:[2,3,...16],这些宏来源于" d* X% \6 K6 z
- 库函数的定义,具体功能是配置了时钟配置寄存器 CFGR 的位 21-18 PLLMUL[3:0],预先定义好倍频因子,方便调用, f4 {, V! ]. \" k
- 返 回 值 : 无1 t+ H' X7 D( t; S$ P
- 作 者 : JayYang
, }0 d% a) s/ {" m6 D9 a' K# q - *************************************************/
; H$ ]- x5 x( |7 C' P; W - void HSI_SetSysClk( uint32_t RCC_PLLMul_x)* T2 A* Y: k* {1 k, Y4 m
- {
: O" S9 T1 G- D4 O - __IO uint32_t HSI_StartUpStatus = 0; //定义内部高速时钟启动状态
- h! ~2 F8 `8 i& g) v5 I - 4 K. ]( f; j" _2 S7 D" {
- // 把 RCC 外设初始化成复位状态,这句是必须的
" P/ t, S1 X. H! U) r6 u - RCC_DeInit();
" p& C {3 _$ w* Y% Y: C - $ ~0 g6 ~6 p/ h9 }: G
- // 使能 HSI + V& o" `' R. Y; ]
- RCC_HSICmd(ENABLE);
6 e A, E) G, C+ P2 S3 p& ] - ' C+ u3 z) E$ h. q' b4 o; m- T: A
- // 等待 HSI 就绪( ]# Z& j% I. [0 E' k
- HSI_StartUpStatus = RCC->CR & RCC_CR_HSIRDY;
# n2 L$ @7 _2 { - $ g1 k* @# q% Q; L$ @* i
- // 只有 HSI 就绪之后则继续往下执行$ w/ C7 s' H# d* J. L8 F/ _$ m
- if( HSI_StartUpStatus== RCC_CR_HSIRDY )/ z6 N; {* k- \: t; f
- {0 Q0 u5 T- Z+ t) E
- //-----------------------------------------------------------------//8 Y8 M5 J- T/ A2 ]% q: p3 w4 }
- //使能 FLASH 预存取缓冲区4 o$ _& i4 a' z3 D w1 O
- FLASH_PrefetchBufferCmd(ENABLE);% r. y% s+ Z G' Q& z
& c# b0 H" ?4 `0 }4 T+ ?1 a: P- // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成 1
$ S4 K/ Q) [4 [! I0 b+ F - // 设置成 1 的时候, SYSCLK 低于 24M 也可以工作,如果设置成 0 的时候,
+ v( Q$ B0 U' ]# r2 F" f - // 如果配置的 SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了4 l; D; E& J) `& r) K- u; g4 U& p8 W
- // 0: 0 < SYSCLK <= 24M& ^6 v+ h5 `! n! j' }, G
- // 1: 24< SYSCLK <= 48M
/ h& f8 N# J7 O5 o& \8 a2 o# i - FLASH_SetLatency(FLASH_Latency_1);% l) ~) }3 Y7 B' v
- 5 p0 g+ o, {6 }% T: i
- //AHB 预分频因子设置为1分频,HCLK = SYSCLK; M) I$ s( T2 [! j2 r, o/ c
- RCC_HCLKConfig(RCC_SYSCLK_Div1);
! o. p6 o8 a. g! m6 k
' R$ W" u: [- j6 V7 D+ l0 X# k$ L- //APB 预分频因子设置为1分频,PCLK = HCLK% V' P" h( f- [# A1 x- a( H! l! I2 V5 k
- RCC_PCLKConfig(RCC_HCLK_Div1);: J6 J+ @# X9 D/ U6 A4 r( ~0 E3 o1 w
- //-----------------------------------------------------------------//: i; B( d& R' F2 V$ Y- M: [0 e2 X$ u
- ' P- o! Z7 r, N1 b5 c8 |+ g
- //-----------------设置各种频率主要就是在这里设置-------------------//
: j3 j, c) N' a# S7 d* i4 u& l - // 配置 PLLCLK = HSI * RCC_PLLMul_x u1 J9 l, I1 v+ f$ j
- RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);
5 U# u) F0 ?# I9 f/ H+ E4 j -
6 v2 Q- ` R3 X4 o% z. q% n - // 使能PLL0 m* e7 R' m7 c% \7 W( m# c! n
- RCC_PLLCmd(ENABLE);
1 |+ Y P8 ], O* v0 q - 1 u4 S% g {4 F8 o1 f4 ^5 B: _: |
- // 等待PLL稳定
' ]. G4 Z5 H9 I0 n" Z% w - while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
: f9 X( \+ h8 x+ V -
& K- X& B9 m1 }4 z6 _ - //当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
( q0 _2 C7 E6 }: q ^ v" { - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
& L0 i; @3 o" f3 {! }5 f -
: N$ J8 [$ _& i4 v - //读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
' g5 X, b0 C* u1 M1 s) y - while( RCC_GetSYSCLKSource() != 0x08 );
/ E2 r! z+ c$ }2 K2 h& f - }; ^5 `- u9 L% `+ Y# S" J
- else
: F' {$ _/ c$ Y$ F8 Q - {- d- g# \. N" S$ f: e6 `8 y
- //当HSI开启失败或者故障的时候,程序就会来到这里,用户可在这里添加出错的代码处理" `7 U4 b% R0 g6 O$ p/ G6 i
- while(1){
6 k& t- q7 @5 ]# p* P& M5 V5 V9 | - }
& |7 V1 s+ ?4 [3 M2 O w) T( `4 M+ H - }
4 U/ E9 x1 V7 I5 z# Y- o. h+ j- ^( ? - }
' l* C6 l; q' _* p& {, N0 Z - " ~* J: Q0 m- V# `: i
- /*=========================== main.c主函数调用 ===========================*/1 m( N8 x# s+ ]8 c* u% y0 @
- int main(void){4 Z. h7 ~8 E T* q/ @
- HSI_SetSysClock(RCC_PLLMul_12); //设置系统时钟为: 4MHZ * 12 =48MHZ。: ^2 Q, a% `5 H4 L" I, |+ k3 Q# Q
- }
复制代码 % j! |# r$ o4 q
4.2【HAL库】的配置方式
. S- e' @1 z: m, ^1 x# F |5 L步骤:/ G' E# l4 N6 U1 U9 x/ ~( `
1、配置HSE系统时钟:在 STM32CubeMX 软件中选择 【System Core】→【RCC】→【选择HSE高速时钟】→【点击下拉菜单选择“晶体/陶瓷谐振器”,即电路中外接晶振的】
+ ]! d* S. o( H/ w6 l( {" g
* I) ~; c c! a6 i. R7 D- B; U' U3 L* l4 s9 z* t: i
6 e# h7 x0 D% f- q% X# c
4 d( I$ b ~* B( j5 ?6 w$ q
* I, m |2 I. D F
6 z/ y' S: r5 c) r7 R) X
$ ?' x8 Q6 I, [- q
. B) d5 }. \! [/ {# u' p; @
8 Q6 j+ n5 w- _2 D) H" DSTM32CubeMX软件可以直接生成MDK项目代码,双击打开项目工程,先进行项目编译,然后打开 main.c 查看源代码,找到 **SystemClock_Config();**函数,这个就是STM32CubeMX生成的系统时钟配置函数,代码已全部自动生成,无需用户再次修改任何内容,生成的系统时钟,就是上文配置的外部8MHz的晶振,经过6倍频后,最终生成了48MHz的系统时钟频率。
% H3 X; i/ o) ^. s* A( `2 v8 E9 \" S, @- {
5 ~* a' Q- C; Y; G; O5 {2 I9 h. E
4 A* Y+ W' u1 [! y9 n! x
进入函数体内查看源代码/ C7 O( r1 ^" ]) A
5 h! e3 k4 \6 _# z! j: ?3 X
8 B" V8 R. }" e$ I; j4 l- B: X6 @8 a( C, D& y" \# @+ S
2、配置HSI系统时钟,按下图设置CubeMX软件,并生成代码
l8 `) w" O. [( l7 Y" D: N( Y3 ^; u) R' |& c
9 v6 X4 V' V2 ?: o: C8 }
7 P7 s2 G7 r- B, D, w+ `
, @6 x5 F5 l" H7 y* D1 T0 e* t
1 t- j8 F9 Z9 O
同样打开项目中的main文件
0 K6 w" x" w, b
8 S; i9 e5 u5 k0 b G# i- g: o; }& G4 j; y( _
3 A1 y- O# H3 Q! {
进入函数体内查看源代码4 p3 T5 U. W" r4 q- x( h! F6 ]' O) S
8 y3 w" N( |( `7 v; `
, B7 e$ v1 q* F7 C
: e7 `. H0 {3 y至此,关于STM32F0系列的系统时钟的配置及修改方法如上所述,本人刚开始写博客,有不好或者错误的地方希望各路大神指教。此博文的主要目的是,作为自己的一个学习笔记,在自己学习的过程中做好相应的记录,方便以后回顾和巩固,也方便有需要的朋友。因为我发现只看一些教学视频的讲解,和动手练习操作一遍是很难学会的,还是要通过做笔记的方式来对知识进行巩固和累积才能帮助我真正的消化和学懂相关知识,谢谢!" d8 s$ l K/ p7 a5 ] R: T# g) G
' l& `% O5 A6 s0 h, f( V j0 `1 f7 ]/ W4 W" a( E: j# o
0 }$ d$ i D! W2 q
|