平时在做项目的时候都用的是外部晶振做为时钟源,想试试用内部RC振荡器做为时钟源,在网上搜了一下如何设置内部时钟,发现资料比较少的。决定将设置内部RC振荡器做为时钟源的方法记录下来。
: y# U! k1 O, {$ g用的单片机是STM32F103C8T6,项目工程是在正点原子的示例代码上修改来的。用一个LED工程测试,在主程序中闪烁LED灯。
! V r+ N) S, }! {' N$ U1 j* _ q( X+ q: Y9 M' g9 }" U7 o [
- #include "sys.h"1 L) e5 i& J/ i& N2 l2 R' U
- #include "delay.h"7 Z6 o1 b& U) ]4 j+ W
- #include "usart.h"1 G0 m( a" ]+ y# O1 F2 H3 {( R
- #include "led.h"1 [5 I, Q+ Y2 L* r
- int main ( void )% S+ E" |* C) N o0 v! m. j
- {! [8 I* t' t" A' A; K
- delay_init(); //延时函数初始化# \& ]9 E* O2 D2 L/ t
- LED_Init(); //初始化与LED连接的硬件接口
& A- I% M. Y4 N, W7 u p/ o, V, G - while ( 1 )& d/ D, I4 k( z- L: B
- {
, d8 A3 C9 c3 I ~, V - LED = 0;
! q$ R6 n* _9 c, @& ` - delay_ms ( 100 ); //延时300ms, D" F* ~, W. x
- LED = 1;' P( u# \. J: w" T+ H7 O
- delay_ms ( 100 ); //延时300ms T3 @0 q# K) J+ L5 N: H
- }5 M6 Q! [* b! Q' i
- }
复制代码 / B3 h) ^9 _ w9 [8 p! |
首先使用默认时钟设置,也就是外部8M晶振做为时钟源。LED灯灭100ms,然后再亮100ms。$ m3 y7 Z" r' \3 [
下来看默认时钟设置的代码:
7 L& G a4 o# c; r0 B. e在 system_stm32f10x.c 文件里面, SystemInit()函数设置时钟。
3 n! V2 _6 X% m, v' G: K- p9 \9 n4 E. i# p/ {) W- X
- void SystemInit (void)& }3 f3 u: x ?3 w5 K
- {
& W U0 `8 G- q - /* Reset the RCC clock configuration to the default reset state(for debug purpose) */% M. o9 o* E, ~+ `
- /* Set HSION bit */
$ t( F/ o% O3 ^. q2 [: r4 @* ^, q) R/ a - RCC->CR |= (uint32_t)0x00000001;
' X; O) ]3 F. s/ `5 X7 ^) S7 k* D - /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */* f7 }' y( D8 O* k. R
- #ifndef STM32F10X_CL
, t6 t2 \5 _; {3 n1 m- a: @: o" M - RCC->CFGR &= (uint32_t)0xF8FF0000;% a$ j" W9 b6 h- W
- #else7 d$ p6 d: t: x
- RCC->CFGR &= (uint32_t)0xF0FF0000;
/ i* f. x. H! ?7 R8 f' H4 G: z9 a - #endif /* STM32F10X_CL */6 A9 M' ?5 e [' K
- /* Reset HSEON, CSSON and PLLON bits */4 c% x+ ?; S6 G5 \1 A9 F
- RCC->CR &= (uint32_t)0xFEF6FFFF;6 y/ }" ~7 L! E" t
- /* Reset HSEBYP bit */
' J, T8 |0 J# ~7 k - RCC->CR &= (uint32_t)0xFFFBFFFF;
0 C! e! w8 T8 }) e0 l# U1 U - /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */* C1 L7 o% F+ m# g2 T
- RCC->CFGR &= (uint32_t)0xFF80FFFF;
% \8 P- n: f' ~ - #ifdef STM32F10X_CL
* j1 s2 ?) ^8 H - /* Reset PLL2ON and PLL3ON bits */4 E+ G( L. F0 I9 m# q
- RCC->CR &= (uint32_t)0xEBFFFFFF;5 ~8 ]$ I! @% b) B8 W* a* z j3 Y
- /* Disable all interrupts and clear pending bits */( O5 I' T* _8 v. j6 M
- RCC->CIR = 0x00FF0000;
& A; o4 U7 N' R3 |' R7 F - /* Reset CFGR2 register */7 O. ]; h5 T% ?2 z+ R8 \% _1 s
- RCC->CFGR2 = 0x00000000;
/ v1 ?( C3 N* y: u - #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
2 p5 s6 A" k0 ]0 `+ e - /* Disable all interrupts and clear pending bits */
) L. @* y, G2 a4 Z+ r# ` - RCC->CIR = 0x009F0000;( w/ C* u0 ^' n0 ~" J
- /* Reset CFGR2 register */
2 R5 j# E& T! p- Z o; d - RCC->CFGR2 = 0x00000000;
/ F& f/ }! S% ]- Z* Z$ N# S# [3 z v - #else
2 B: q& w! |2 h9 b$ H [ { - /* Disable all interrupts and clear pending bits */
F" d r* m4 d% |, f - RCC->CIR = 0x009F0000;
" C+ ~9 y* Q: H - #endif /* STM32F10X_CL */; c4 \: I: p; o* l- ~
- #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)3 X Q6 w) f2 Z2 W s0 J+ w' u- k
- #ifdef DATA_IN_ExtSRAM$ ]3 ^5 c( M0 u2 s2 S0 }2 I
- SystemInit_ExtMemCtl();9 U b2 ?# x. B% H* i, `4 {: }) Q
- #endif /* DATA_IN_ExtSRAM */+ b6 f3 J6 [* }
- #endif* j6 g- W8 d: Y4 I' D& _2 n9 o& i
- /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
3 h9 b. G2 K1 a2 T0 i/ u - /* Configure the Flash Latency cycles and enable prefetch buffer */9 w: d! k. }5 m8 v1 |- A
- SetSysClock();' V; y! g) C) }6 w0 t
- 4 p( Z1 m! ~& u( G p, w$ N
- #ifdef VECT_TAB_SRAM4 Z! c/ X$ R7 [$ |8 s9 A
- SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */: g( Z2 V5 E4 [( \8 ~3 ^
- #else
9 m3 j% o' |; W |+ M& S8 V - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */* G# q8 q* y/ x& e
- #endif/ p. S) D" i* r
- }
复制代码 8 p+ o8 Q5 {: V! Q4 s- o- Q5 h
如何要使用内部RC振荡器做时钟源的话,需要重写SystemInit()函数。设置代码如下:- /* 开启HSI 即内部晶振时钟 */5 Y7 @. v) v, J3 E4 @4 T
- RCC->CR |= ( uint32_t ) 0x00000001;( H. Z+ }5 o I
- /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/6 r2 s( n' [# s+ h
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_PLLSRC_HSI_Div2;/ _& O5 h+ E! I+ W) p
- /*PLLCLK=8/2*9=36MHz 设置倍频得到时钟源PLL的频率*/. S' h, ]; d/ _3 O& e4 I9 U) n6 [
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_PLLMULL9; //设置倍频后的频率! h# t( A2 u6 ^+ Q
- /* PLL不分频输出 ?*/
9 L; x5 z% h; H+ J* i; [4 X - RCC->CFGR |= ( uint32_t ) RCC_CFGR_HPRE_DIV1;
4 {- M+ ~' _7 P" y" m: ?+ h; Z" l# Y/ n - /* 使能 PLL时钟 */# q( }1 M) n; E7 O" B* \
- RCC->CR |= RCC_CR_PLLON;
. h; s% K" u: Y. ^3 | - /* 等待PLL时钟就绪*/4 c; {% m; f0 y* r5 w
- while ( ( RCC->CR & RCC_CR_PLLRDY ) == 0 )
* C; N3 U ]- k* [ - {& W7 Z% ~2 ~* `; C
- }
4 [( u' A: X3 m+ k3 S1 F) z, Z - /* 选择PLL为系统时钟的时钟源 */" G e# v9 o% n: _
- RCC->CFGR &= ( uint32_t ) ( ( uint32_t ) ~ ( RCC_CFGR_SW ) );
; l4 S/ o6 H p( D0 R - RCC->CFGR |= ( uint32_t ) RCC_CFGR_SW_PLL;
: G8 T6 m+ f( n, \# F$ R% }6 S2 K - /* 等到PLL成为系统时钟的时钟源*/
2 Z; G4 \2 ^, K* m - while ( ( RCC->CFGR & ( uint32_t ) RCC_CFGR_SWS ) != ( uint32_t ) 0x08 )
3 I/ M- x) C7 I" u, _% r3 J( k - { }
复制代码 9 v1 ]; k. h4 \ |! P9 U
为了方便和以前的代码兼容,这里使用条件编译来选择使用外部晶振或者内部RC震荡。代码流程如下:$ H1 o0 {/ k& ~; k
#if USE_HSI
d r; s' G, M+ `1 R/ H7 s/ o{undefined0 r% _( @) r: _5 |. m8 O; E `; F
//使用内部RC
% c: c8 W3 o9 J9 R2 u}+ D/ F" a/ d0 w
else" ? ?! T" ^0 c
{undefined
2 S2 v! q$ A4 [5 P//使用外部晶振4 e6 _, {7 o4 o& L5 r' p
}
- r$ c% h' t% `9 b6 A/ `0 a7 V- m#endif' ^( j+ V2 R% D' O# c
通过宏定义 USE_HSI 来选择时钟源,USE_HSI 为1时,使用内部RC做为时钟源,USE_HSI 为0时,使用外部晶振做为时钟源。
7 }" }7 P8 K# z9 e$ z修改后的代码如下:! b3 W9 r& \- F5 F
- * r; X: v+ s3 I
- #define USE_HSI 0 // 是否使用内部晶振 0 不使用 1使用% t; r# v; G. {% p, k
- void SystemInit ( void )
2 D) m9 i5 G- f4 j, z' m - {
1 y. Y4 d# c) D5 [ ~4 u
3 l( ~1 W+ R" V% \3 l5 d! [+ R- #if USE_HSI! V3 d7 p) B+ ~* F6 l. U( X; V4 h. a
- {% l" W9 ^ H7 q" L
- //设置使用内部晶振) N& n! }) r' A+ D. A; L
- /* 开启HSI 即内部晶振时钟 */) ~: ?$ W ~9 q( J4 ~+ l, W
- RCC->CR |= ( uint32_t ) 0x00000001;
- g. ?6 E* j: @, A7 a - /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/+ c* F: [7 _9 Q* J/ I7 P/ B* ~- }2 e
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_PLLSRC_HSI_Div2;. m+ C' R+ Y9 e( v. A. @* X
- /*PLLCLK=8/2*9=36MHz 设置倍频得到时钟源PLL的频率*/6 q A x1 k7 p
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_PLLMULL9; //设置倍频后的频率
8 L5 |/ f9 z( ]! M D - /* PLL不分频输出 ?*/& Y* @; y/ m3 J3 V" }' U
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_HPRE_DIV1;
/ N6 F- w/ b! |. _ - /* 使能 PLL时钟 */( a' w p" e) R3 Z8 \ b% j
- RCC->CR |= RCC_CR_PLLON;
% ?8 i A7 f. M' I - /* 等待PLL时钟就绪*/; L6 T1 W5 y. N
- while ( ( RCC->CR & RCC_CR_PLLRDY ) == 0 )( I* e) a3 y" S1 s6 K: Z
- {4 i# @% @! ]) N, O" w) S, q+ ]7 j% Q- V
- }1 V9 o/ {# C( ^. D6 l
- /* 选择PLL为系统时钟的时钟源 */
5 v. m, x' p- F! F5 c" u) G - RCC->CFGR &= ( uint32_t ) ( ( uint32_t ) ~ ( RCC_CFGR_SW ) );
: u4 E5 t, p: h' K$ r - RCC->CFGR |= ( uint32_t ) RCC_CFGR_SW_PLL;
5 n5 @8 ?3 F( F& ^% c& [ - /* 等到PLL成为系统时钟的时钟源*/
% k6 Q9 @, u& f' u& } - while ( ( RCC->CFGR & ( uint32_t ) RCC_CFGR_SWS ) != ( uint32_t ) 0x08 )
6 K- Q- e; y6 Z( k, D - { }1 z+ x, S/ M: r: E1 }6 J9 `
- }0 [/ b3 N9 q3 J# v0 S) i+ O3 f
- #else
) ~9 o2 e) y$ }$ C - {
% R, r1 d/ ]9 M( z: _. S - //设置使用外部8M晶振1 r' P# u0 u, b' c- [: m
- /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
0 Z3 E! @ j9 T1 z- o* g1 ]$ { - /* Set HSION bit */1 m+ g+ E3 F0 T$ g% T9 z
- RCC->CR |= ( uint32_t ) 0x00000001;' m2 h5 o' C8 B$ Y% |! e
- U2 x: s8 L6 ]$ `3 c( z" b; x1 b- /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
7 [; [3 M J+ C" P( G% m - #ifndef STM32F10X_CL8 Y: J! v O# Z# T& e4 a& M: V- x
- RCC->CFGR &= ( uint32_t ) 0xF8FF0000;( E; _+ x, \* P$ m; R) q1 `6 a
- #else
' K$ H( x; C- `7 U - RCC->CFGR &= ( uint32_t ) 0xF0FF0000;, z2 P' \9 x# ^( }- y8 H
- #endif /* STM32F10X_CL */) N9 f) _# r# m6 y
- & K. ^) v2 d7 x, A2 s
- /* Reset HSEON, CSSON and PLLON bits */8 u9 H4 G4 Y( [
- RCC->CR &= ( uint32_t ) 0xFEF6FFFF;6 R5 L! p6 v1 s+ F
o5 K7 ^, D0 B+ A+ z- O' U$ ]9 [" n' O- /* Reset HSEBYP bit */: \2 k: M& r; c' o. L
- RCC->CR &= ( uint32_t ) 0xFFFBFFFF;9 X9 t5 i, E3 ]) v
7 N0 E0 d) F: I0 c; }- /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
3 q7 z2 |5 d5 H% s8 C7 ?) j - RCC->CFGR &= ( uint32_t ) 0xFF80FFFF;
?* V: ~0 {& M7 ]# _ - : c M( Q. T* J/ q: u4 r
- #ifdef STM32F10X_CL5 ~$ X; w: Y; r% e2 O! v7 {$ m
- /* Reset PLL2ON and PLL3ON bits */3 ]) B9 ]7 h4 a: x1 z
- RCC->CR &= ( uint32_t ) 0xEBFFFFFF;
" T% x$ {/ P/ |- @! c, ~/ t1 V, D
- e4 Z w$ H+ N* i' q7 F- /* Disable all interrupts and clear pending bits */& t9 j( }& D# T; t' M3 b
- RCC->CIR = 0x00FF0000;
& d3 n1 O2 z) Y/ g/ x - 8 l" M9 c( ]$ v% b
- /* Reset CFGR2 register */
; ~4 W6 _. g- W0 Z/ c - RCC->CFGR2 = 0x00000000;# t# W7 ` H1 U# D% P
- #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
0 j# A! v2 L- F! [- U) ^5 W - /* Disable all interrupts and clear pending bits */! t3 Y$ o$ ?; f3 q- `6 ?
- RCC->CIR = 0x009F0000;
`4 o) X" i7 ]$ ~! o/ A
6 u4 ^% v! {% [5 u I& C/ _% t: V. c- /* Reset CFGR2 register */
Q x2 [2 }- D8 B- O* J# r - RCC->CFGR2 = 0x00000000;
8 l) ? t% _/ Z- f3 J. t) p - #else
7 e" ~: d; J7 m; P - /* Disable all interrupts and clear pending bits */# ?8 H6 ^! P! R% F! M
- RCC->CIR = 0x009F0000;
: o+ _& k9 h1 ? - #endif /* STM32F10X_CL */
& x8 u$ K! W1 k/ z" t& Z+ P - + K. {7 H! B! C9 A
- #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)! |( r' q4 e2 a* W
- #ifdef DATA_IN_ExtSRAM" c: I$ t$ [$ \$ j
- SystemInit_ExtMemCtl();
3 A& t! Y9 N/ Q" ^3 s - #endif /* DATA_IN_ExtSRAM */ S! T% q; ?& i. S0 M0 w+ G
- #endif% F x7 D1 J* s& S; M+ z- Y
- h# I5 `" f) _# J4 |! ^; c
- /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
2 h0 @8 h$ G1 ^) Q9 o' P9 Z& a - /* Configure the Flash Latency cycles and enable prefetch buffer */* B* ]" l# S/ `* F. i
- SetSysClock();
2 r+ o$ f& q( t. t& S& C9 [ - $ A5 l/ x6 i* W/ l% t
- #ifdef VECT_TAB_SRAM
% h6 O5 y' J3 `6 ` f - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
# w! U! {" C2 ^% l$ c& ~" H; V4 ] - #else
) ~( v2 ?( {0 w. w - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
B0 w. \, W1 \2 C) Z - #endif
4 u% W$ y# J3 h( i - }
( _" V f% Z; b* B( d - & R3 q9 t) T3 h4 h8 Y9 v
- #endif1 |' [1 H6 M9 d3 Q& L
- }
复制代码
& M$ N0 G8 c( p) `+ i* Q' ~9 i9 n内部RC默认为8M,将倍频数设置为9,这样使用内部RC振荡器之后,时钟频率就是36MHz。为使用外部晶振频率72MHz的一半。
; P: }" `4 ^6 m" I* a5 }
. s. I- k: N- x5 J. x! a- /*PLLCLK=8/2*9=36MHz 设置倍频得到时钟源PLL的频率*/1 s3 H" E" _" J
- RCC->CFGR |= ( uint32_t ) RCC_CFGR_PLLMULL9; //设置倍频后的频率
复制代码 & V$ q' B2 E) J+ L# U: t
这时候在运行代码,用示波器测试LED的电平,发现是高电平200ms,低电平200ms。比使用外部时钟慢了一半,说明使用内部RC振荡器的设置代码是正确的。
" ~, x. B6 D; G" D+ M4 f& V- S( b1 O$ v5 Q: N
0 I! D2 H w/ n, l g& ^
|