你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32~配置时钟频率[一文带你解决STM32主频配置]

[复制链接]
STMCU-管管 发布时间:2020-9-14 12:41
前言

  最近开发项目,对MCU主频要求比较精确,尝试了两种配置主频的方法,掌握这两种方法也就熟悉STM32系列主频的配置方法了。分别是,使用外部晶振作为时钟源;内部RC时钟作为时钟源。介绍两种时钟源的区别:


+ e( F3 }  T. R

  • HSI内部8MHz的RC振荡器的误差在1%左右,内部RC振荡器的精度通常比用HSE(外部晶振)要差上十倍以上。
  • 内部RC频率受温度影响比较大,如果省电Sleep模式下内部RC会停止工作。
    2 O7 n% h& h7 o- P

    8 x" l1 Q5 b% I9 @& F' d

8 ]6 ~9 R. N# M. b+ k  u2 j

& O5 `, t1 H& R! r3 E, x. T1 . 时钟系统
/ H9 Q3 _& ^# O

  在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。


% L& u8 f* M8 n1 S2 G. K* R

  • HSI是高速内部时钟,RC振荡器,频率为8MHz。
  • HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
  • LSI是低速内部时钟,RC振荡器,频率为40kHz。
  • LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
  • PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。- T: ]1 d, ?0 P: x
( X. W3 ]4 _0 R. S& M, I
1.png
, k1 \3 u% X: |# {5 d2 {8 A( K# j

7 n5 f7 h( F% r5 B! A3 [

. J( t- t1 u) Z3 n& ]

 用户可通过多个预分频器配置AHB总线、高速APB2总线和低速APB1总线的频率。AHB和APB2域的最大频率是72MHZ。APB1域的最大允许频率是36MHZ。SDIO接口的时钟频率固定为HCLK/2。
6 [' C9 H& q8 m- ^' Y  40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外,实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。
/ R/ {# v, f6 k+ F5 a9 F/ k  STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。
& a1 W( I! X3 c, z8 G; V! c  另外,STM32还可以选择一个PLL输出的2分频、HSI、HSE、或者系统时钟SYSCLK输出到MCO脚(PA8)上。系统时钟SYSCLK,是供STM32中绝大部分部件工作的时钟源,它可选择为PLL输出、HSI或者HSE,(一般程序中采用PLL倍频到72Mhz)在选择时钟源前注意要判断目标时钟源是否已经稳定振荡。Max=72MHz,它分为2路,1路送给I2S2、I2S3使用的I2S2CLK,I2S3CLK;另外1路通过AHB分频器分频(1/2/4/8/16/64/128/256/512)分频后送给以下8大模块使用:

+ s/ X" I- s4 ]+ ^  K

  • 送给SDIO使用的SDIOCLK时钟。
  • 送给FSMC使用的FSMCCLK时钟。
  • 送给AHB总线、内核、内存和DMA使用的HCLK时钟。
  • 通过8分频后送给Cortex的系统定时器时钟(SysTick)。
  • 直接送给Cortex的空闲运行时钟FCLK。
  • 送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer2-7)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4、5、6、7使用。
  • 送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer1、Timer8)1、2倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后得到ADCCLK时钟送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。
  • 2分频后送给SDIO AHB接口使用(HCLK/2)。
    $ m" g* O; J  w4 }1 S' n- i
    2 T# `+ E8 h% @+ T# }2 V
' T$ M" r6 H2 F/ Y+ Z( Q
2 . 外部晶振作为时钟源  I+ `: w0 p- }

接下来,解决使用12M外部晶振时,如何配置作为系统时钟源。/ I% h7 m4 I3 n9 e, k
第一步,修改stm32f10x.h中的HSE_VALUE为12000000


' B% q% q9 B* C1 t: ?; q

  1. /**/ \: q- P4 B. u, B# W
  2. * @brief In the following line adjust the value of External High Speed oscillator (HSE)0 G" `( i6 Z" H: X7 j
  3.    used in your application 7 L  p) }, X. ~9 D- s& D
  4.    # \6 n- w: q: N% a; d
  5.    Tip: To avoid modifying this file each time you need to use different HSE, you% l: o: T) U! j3 q0 E
  6.         can define the HSE value in your toolchain compiler preprocessor.
    : u7 U. W" V5 |
  7.   */           
    $ @$ d3 j8 P& N# q
  8. #if !defined  HSE_VALUE
    ) @7 N! v/ o7 b( ?/ I
  9. #ifdef STM32F10X_CL   5 c; {4 n' }, Q2 n5 R
  10.   #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    ) l" Y5 V; w& D4 j6 q- ^/ ~- d& V
  11. #else ; |2 Z/ v% ]& s/ r  V8 F
  12.   #define HSE_VALUE    ((uint32_t)12000000) /*!< Value of the External oscillator in Hz */
    % ^& Q0 v% A! h9 j1 [
  13. #endif /* STM32F10X_CL */
    * @+ m2 T' g5 x9 U
  14. #endif /* HSE_VALUE */
复制代码

( K2 P" c. D2 R( _' N' e( c

第二步,修改system_stm32f10x.c中的时钟配置,先找到void SystemInit(void)—》SetSysClock()—》SetSysClockTo72(),将9倍频改为6倍频,12*6=72MHz


( E7 Z, |/ E& r" J& v3 q* A- r- C

  1. /**" w- u8 S/ K5 Y8 s6 D5 A
  2.   * @brief  Sets System clock frequency to 72MHz and configure HCLK, PCLK2 2 G7 t0 u$ F" x- b
  3.   *         and PCLK1 prescalers. - i: s' g+ i. a( t4 J
  4.   * @note   This function should be used only after reset.
    # h2 R% m& i  X' I
  5.   * @param  None! U6 L7 o) c/ m
  6.   * @retval None$ H1 R/ |# k7 f! j, `  A8 z
  7.   */
    2 w" u/ n0 R( n; W+ X3 V
  8. static void SetSysClockTo72(void)
    . t! t$ b2 A8 S9 J) D
  9. {
    $ n% u% O5 Z! B" a8 G! P
  10.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;$ _( J+ k8 p4 m# N# J8 x  l
  11.   ( z/ A- X' v7 U1 B! n; }1 v
  12.   /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    9 W5 |2 F* o! G9 e- M9 H; u
  13.   /* Enable HSE */   
    / o* m# c& i. ]1 o& k5 g% t- A
  14.   RCC->CR |= ((uint32_t)RCC_CR_HSEON);
    ! R) C. X1 Q: c. ^, d
  15. , x5 N. I) k" I/ O( O/ _# S( ~/ O( U* b
  16.   /* Wait till HSE is ready and if Time out is reached exit */
    ) K4 R/ p8 ]$ f: ~5 K5 n! _
  17.   do
    8 b* i' \- c' h. a; Y& {' `, b
  18.   {
    1 K$ S0 z' e# V/ Y( P3 P$ {  S
  19.     HSEStatus = RCC->CR & RCC_CR_HSERDY;
    + l$ y3 M8 b. p3 f
  20.     StartUpCounter++;  
    % [' ]2 f: q5 _# b& G
  21.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
    $ W0 [8 z: ^, h+ i- T  g1 `! k
  22. : @& s& n% U- o
  23.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)5 [5 r! c3 t) p5 F
  24.   {
    6 L  Q, I5 z9 L: d7 d/ C/ ~+ E
  25.     HSEStatus = (uint32_t)0x01;1 t" H! r4 k9 [1 w. A
  26.   }7 f" G. t2 I: `& q) |$ R
  27.   else1 k  X6 K; Z" Y4 t0 E
  28.   {
    " k- d5 I3 v! P1 d
  29.     HSEStatus = (uint32_t)0x00;
    " g* d$ h- J+ b7 u$ K5 `' v  `8 C
  30.   }  
    " ^5 d+ g3 C$ D, T3 a* P& b; B
  31. ; A. G5 A1 i* {0 b
  32.   if (HSEStatus == (uint32_t)0x01)
    ( e- p0 J7 w  g# |
  33.   {2 v' h: W- C3 q2 _9 |7 s$ o
  34.     /* Enable Prefetch Buffer */
    0 _4 E6 R9 {7 N- Z, {. y
  35.     FLASH->ACR |= FLASH_ACR_PRFTBE;) @9 X/ ^5 t$ g  h5 t8 s

  36. , {1 p' b. O3 [3 ]( g2 O
  37.     /* Flash 2 wait state */- o* q4 f* V6 \. a5 D3 a
  38.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    ! N! k& e/ H6 A1 f* H
  39.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;   
    2 ~2 W* S6 z3 h3 y7 K3 b! |( f& B
  40. : r, G0 f1 Q6 {$ o5 `

  41. * d9 p- x: K! Z5 ]7 u' ]4 d! r
  42.     /* HCLK = SYSCLK */
    * h9 ?2 e, m; C
  43.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
    + T  _2 X/ b$ M! o8 S
  44.       
    + ?; w  R% O# t! i2 v
  45.     /* PCLK2 = HCLK */& r! a: t3 ?9 f/ w9 p; u6 z' u9 m
  46.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    4 u+ O; \6 B$ f3 d
  47.    
    2 ~0 z3 q  J- i5 L+ w
  48.     /* PCLK1 = HCLK */
    7 m7 Y8 ^4 J2 \2 e
  49.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;$ r  p5 {# [" \/ A1 O9 H
  50. / w5 Z. q2 B3 _: G  X
  51. #ifdef STM32F10X_CL
    / e$ A. P0 E" U. L
  52.     // ...
    3 X4 V) i3 s* D1 p: a2 h
  53. #else    % g$ N# e- n8 ?' i: q/ I
  54.     /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    7 y; D. m6 M$ E% A" e( s: _
  55.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |, y/ y: Q5 `+ L. L  [" M! s2 C
  56.                                         RCC_CFGR_PLLMULL));0 v% B. B) C. d+ Z3 b- F) S0 O6 Y
  57.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6); // 121 Z" q, n' W0 e2 P3 M! n# g5 t1 @; S
  58. #endif /* STM32F10X_CL */
    9 [, L! [5 k' q4 t2 ?8 o4 S8 O' r
  59.   x0 \# h( F# h4 B: _
  60.     /* Enable PLL */8 @; k; N4 E& o: \
  61.     RCC->CR |= RCC_CR_PLLON;
    " L: `& p8 {- g! q+ a. U5 z4 F

  62. - z- }! T2 \. `9 {
  63.     /* Wait till PLL is ready */
    . E3 O; {9 n, S; {$ w
  64.     while((RCC->CR & RCC_CR_PLLRDY) == 0)
    - d2 Q& {' G$ f3 n; r3 b
  65.     {
    6 Y6 R3 ?9 h( Y" s: g$ B- k7 T
  66.     }7 k+ n4 c/ ]) I9 r
  67.    
    6 R3 t+ n/ j0 ?# }: C# y3 S
  68.     /* Select PLL as system clock source */
    1 T$ a2 q# i' s
  69.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    & D; j: X1 ?1 a5 H# D& N
  70.     RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    % e7 v2 {  G0 n! [3 p% z; ^9 S

  71. 3 I8 ?8 _0 A. e3 W: H6 c1 x5 B' h
  72.     /* Wait till PLL is used as system clock source */
    " \9 _& G) @/ G4 y
  73.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)* _: x. j# g# J- |% [
  74.     {
    ! g% a  \* q7 m3 b; N) g$ R' ?
  75.     }
    7 \: `9 Z% j5 ]  T$ D1 X
  76.   }- j: `6 B, N/ i, g8 o
  77.   else
    , W, T8 i, b' C  M( _: v
  78.   { /* If HSE fails to start-up, the application will have wrong clock : ?- b, T( E2 x
  79.          configuration. User can add here some code to deal with this error */9 t, l1 j+ h$ N/ E7 ^+ h
  80.   }
    / I; s$ e- M- R( E
  81. }
复制代码
& G. z9 q' o; L" p# h) Y
2 Z1 p+ T! a4 B& ~) D" g' n/ n6 i) P
3 . 内部RC作为时钟源
" v8 X0 m# q9 N- p

  实际开发中使用内部RC振荡器主频不能达到72,我使用的是STM32F103C8T6,库函数最多支持16倍频也就是8/2*16=64Mhz,实际测试芯片跑不起来功能没有正常工作。使用内部RC振荡最大能达到52M,不信大家可以试验一下。
+ f6 B* C5 g. z  v3 z" q) N6 ~

在system_STM32f10x.c中,找到函数void SystemInit (void){} 注释掉所有代码,添加下属代码。


' t3 n" ~$ m! Z$ n/ F0 U- @+ h

  1. //开启HSI5 i0 t$ a. u7 X4 Y. B
  2.         RCC->CR |= (uint32_t)0x00000001; ! I/ Z; C. m3 v
  3.         //选择HSI为PLL的时钟源,HSI必须2分频给PLL! [1 b3 i! M: w1 c
  4.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2;
    ( K6 Z6 i5 p+ {* f% j8 b- D4 x8 ~
  5.         // 8/2 *13 = 52 8/2 *9 = 36 8/2 * 12,设置倍频  v/ X3 N( w" D( U6 J' v! {! j
  6.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL12;# A2 _# c! _  u# `+ x
  7.         //PLL不分频
    % Y4 K' R% A" q/ Y& q
  8.         RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
    4 F0 l& C' ?' i4 _" }& ]! B, O
  9.         // 使能PLL
    ) p& ~. l; G6 T( M4 \/ Z
  10.         RCC->CR |= RCC_CR_PLLON;
    9 U" E, \: f* ?: @& c' _
  11.         // 等待PLL始终就绪; B) V' _' d: t
  12.         while((RCC->CR & RCC_CR_PLLRDY) == 0){}
    ( a2 R, w% b. b
  13.         // 选择PLL为系统时钟源
    * g1 {) I7 P+ C7 [' K1 K
  14.         RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    6 v( v) S' t1 U9 H. g6 Z
  15.         RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;6 C- C, O( R. Q$ v# y+ |6 p3 \* t
  16.         // 等待PLL成功0 ?1 H; U3 a0 z4 {  B$ I. `
  17.         while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){}
复制代码

/ R4 T0 X, V$ F; _9 d7 z
+ ]4 R* E; I; N4 .Keil MDK中Xtal的作用

# K8 k/ f/ m) S7 s! v& W' x7 L% v# R
2.png
7 Q( }$ J% f5 e! \( K" u' u9 a
) S$ y# u" e$ y9 b& q4 b1 u

* d2 a% K6 ~4 m, B" a% ^! m! H9 Z: F# L  g$ g' b7 b8 }
; W  T- Z. z% A5 R
在手动配置主频的过程中,想到Keil工程菜单应该提供了配置主频的选项,于是又看到这个。百度了一下,这个参数只用于软件仿真的,对于硬件仿真或者直接把程序下载到板子里是没有影响的。
$ q# }% f% k% H& k0 W- a, w  s8 c4 @( U4 t0 R4 W9 k
  Xtal 后面的数值是晶振频率值,默认值是所选目标 CPU 的最高可用频率值 。该数值与最终产生的目标代码无关,仅用于软件模拟调试时显示程序执行时间。正确设置该数值可使显示时间与实际所用时间一致,一般将其设置成与你的硬件所用晶振频率相同。
4 y: W4 k8 Y$ ?( a" n* \7 Q( A5 |3 g: m8 |3 D- c/ k  K
2 \% P  J# d% _+ \6 f9 s" X0 l

9 T/ ]0 u; p5 y( k& ~* D0 {
% t5 q4 F6 M. M, I" I
收藏 1 评论0 发布时间:2020-9-14 12:41

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版