打印
[STM32F1]

tm32f10x 时钟系统详解/时钟树/时钟初始化/SystemInit函数全注解

[复制链接]
855|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、时钟系统
1、概述
   用通俗的话来说,时钟是单片机的“脉搏”,是单片机的驱动源,使用单片机中的任何一个外设都必须打开此外设相应的时钟。这样的好处是,在不使用某个外设的时候,关闭此时钟外设,从而可以降低系统的总功耗,进而节能,以实现低功耗的效果。

   衡量“脉搏”强度的量为xx次/分钟,在单片机中衡量时钟强度的量叫做时钟频率,即 xx 周期/秒,就是我们常说的赫兹 Hz;脉搏(心跳)每跳动一次,就会将血液能量送往全身以供使用,脉搏越快,每分钟输送能量越大,耗能越快;相对应的时钟每发送一个时钟信号,单片机处理一条指令(或一条指令的一部分),频率越高,每秒钟处理指令越多,耗能相应越快。

2、原理
  由时钟源(晶体振荡器等)产生时钟信号,该时钟信号经过分频/倍频后作为系统时钟,系统时钟经分频/倍频后作为各个外设的频率;在时钟信号发出后,单片机内部各部分收到了来自时钟的信号,执行指令(类似于触发器,由电平高低触发)。系统时钟,是处理器运行时间基准(每一条机器指令一个时钟周期)。这样每个时钟 tick 系统都会处理一步指令,这样使得工作一步一步,不会出现紊乱。所以频率越高,每秒钟处理指令数越多,速度越快。(此处为理想情况,不考虑其他外接设备造成的“木桶效应”)

   时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行指令。以 Pentium 4 2.0 为例,它的工作主频为 2.0GHz,具体来说,2.0GHz 意味着每秒钟它会产生 20 亿个时钟脉冲信号,每个时钟信号周期为 0.5 纳秒。而 Pentium 4 CPU 有 4 条流水线运算单元,如果负载均匀的话,CPU 在 1 个时钟周期内可以进行 4 个二进制加法运算。这就意味着该 Pentium 4 CPU 每秒钟可以执行 80 亿条二进制加法运算。

3、stm32 时钟概述
   STM32 本身十分复杂,外设非常多,但是在我们实际使用的过程中往往不会用到所有的外设,如果将全部外设都开启,就会造成浪费,加大耗能;所以在实际情况下,STM32 的外设时钟是独立的,可以控制使能或除能,即“打开”需要用到的外设,不用的外设是默认“关闭”的。

   使用任何外设都需要时钟才能启动,但是不同的外设所需要的时钟频率不同,都是用低速无法满足需求,都是用高速又会造成浪费。一个单片机内提供多个不同的系统时钟,可以适应更多的应用场合。不同的功能模块会有不同的时钟上限,因此提供不同的时钟,也能在一个单片机内放置更多的功能模块。对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗。并且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的 MCU 都是采用多时钟源的方法来解决这些问题。所以便有了 STM32 复杂的时钟系统和时钟树。主要的目的就是给相对独立的外设模块提供时钟,也是为了降低整个芯片的耗能。


使用特权

评论回复
沙发
keaibukelian|  楼主 | 2021-6-4 18:54 | 只看该作者
二、STM32 系统框图



图 1-stm32f10x 系统时钟框架图



图 2-stm32f10x 系统时钟框架图-官方数据手册版
1、时钟源
   stm32f10x 时钟系统总共有四个时钟源:HSE、LSE、HSI、LSI 和 PLL,其中 PLL 是由时钟信号输入再进行倍频,间接产生时钟信号,另外四个时钟源为石英晶振或 RC 振荡器,直接产生时钟信号;芯片内的其他时钟都是通过以上五个时钟源分频或倍频得来;图 1 中为蓝色方块。

(1)HSE(External high-speed clock)
  HSE Osc,高速外部时钟信号,可接晶体/陶瓷谐振器,或者接外部时钟源,频率范围为 4MHz-16MZh;通常选用 8MHz 的晶振;

(2)LSE(External low-speed clock)
  LSE Osc,低速外部时钟,接频率为 32.768MHz 的石英晶体,为实时时钟或其他定时器时钟提供一个低功耗且准确的时钟源;

(3)HSI(Internal high-speed clock)
  HSI RC,高速内部时钟,RC 振荡器,频率为 8MHz,精度较于晶体/陶瓷谐振器较低。

(4)LSI(Internal low-speed clock)
  LSI RC,低速内部时钟,RC 振荡器,频率为 40KHz,可以在停机和待机模式下保持运行。

(5)PLL(Phase Locked Loop)
  PLL,锁相环倍频输出,时钟输入源可选择为 HSI/2、HSE 或者 HSE/2;可选择 16 种倍频 1-16;输出为锁相环倍频输出时钟(PLLCLK,最大 72MHz)。

2、选择器
   stm32f10x 芯片时钟系统中,共有 5 个频率选择器:PLLXTPRE、PLLSRC、SW、MCO、RTCSEL,功能为从多个输入时钟源中选择一个作为选择器输出;图 1 中为灰色梯形。

(1)PLLXTPRE(HSE divider for PLL entry)
   PLLXTPRE,HSE 分频器作为 PLL 输入;选择 HSE 不分频或 HSE 二分频输出到 PLLSRC。

(2)PLLSRC(PLL entry clock source)
   PLLSRC,PLL 输入时钟源,选择 HSI 经二分频或者 PLLXTPRE 输出时钟作为输出时钟到 PLL;

(3)SW(System clock switch)
   SW,系统时钟切换,选择 HSI 或 HSE 或 PLL 输出作为系统时钟;

(4)MCO(Microcontroller clock output)
   MCO,微控制器时钟输出,选择不输出、系统时钟(SYSCLK)输出、内部 RC 振荡器时钟(HSI)输出、外部振荡器时钟(HSE)输出或 PLL 时钟 2 分频后输出。

(5)RTCSEL(RTC clock source selection)
   RTCSEL,RTC 时钟源选择,无时钟、LSE 振荡器作为 RTC 时钟、LSI 振荡器作为 RTC 时钟、HSE 振荡器在 128 分频后作为 RTC 时钟。

3、(预)分频器/“倍频器”
   stm32f10x 芯片时钟系统中,可供设置选择分频系数的共有 5 个预分频器和 2 个倍频器;5 个预分频器:AHB prescaler、APB1 prescaler、APB2 prescaler、ADC prescaler、USB prescaler;2 个倍频器:TIM2,3,4 Multiplier、TIM1 Multiplier;功能是将输出时钟频率倍频或分频后输出;图 1 中为绿色方形。

(1)AHB prescaler
   AHB prescaler,AHB 预分频,系统时钟(SYSCLK)作为输入经过分频后输出,输出作为 AHB 总线时钟(HCLK,最大 72MHz);分频因子为 1、2、4、8、16、32、64、128、256、512。

(2)APB1 prescaler(APB low-speed prescaler)
   APB1 prescaler,低速 APB 预分频,AHB 总线时钟(HCLK)作为输入经过分频后输出,输出作为低速外设总线时钟(PCLK1,最大 36MHz);分频因子为:1、2、4、8、16。

(3)APB2 prescaler(APB high-speed prescaler)
   APB2 prescaler,高速 APB 预分频,AHB 总线时钟(HCLK)作为输入经过分频后输出,输出作为高速外设总线时钟(PCLK2,最大 72MHz);分频因子为:1、2、4、8、16。

(4)ADC prescaler
   ADC prescaler,ADC 预分频,高速外设总线时钟(PCLK2)作为输入经过分频后输出,输出作为 ADC 时钟(ADCCLK);分频因子为:2、4、6、8。

(5)USB prescaler
   USB prescaler,USB 预分频,锁相环输出时钟(PLLCLK,48MHz 或 72MHz)作为输入经过分频后输出,输出作为 USB 时钟(USBCLK,固定为 48MHz);分频因子为:1、1.5;若 PLLCLK 为 48MHz 则分频因子选择 1,否则选择 1.5,保证分频后输出为 48MHz。

(6)TIM2,3,4 Multiplier
   TIM2,3,4 Multiplier,TIM2,3,4 倍频器,低速外设总线时钟(PCLK1)作为输出经过倍频后输出,输出作为通用定时器时钟(TIMxCLK);根据 APB1 预分频系数进行倍频,预分频系数为 1 则倍频系数为 1,否则为 2;

(7)TIM1 Multiplier
   TIM2,3,4 Multiplier,TIM1 倍频器,高速外设总线时钟(PCLK2)作为输出经过倍频后输出,输出作为 TIM1 时钟(TIM1CLK);根据 APB2 预分频系数进行倍频,预分频系数为 1 则倍频系数为 1,否则为 2;

4、时钟安全系统
   stm32f10x 芯片时钟系统中,CSS 作为时钟安全系统,使用时钟监测器监测 HSE 时钟,如果 HSE 时钟发生故障,HSE 时钟将会被自动关闭,失效时间会被送入高级定时器并产生时钟安全中断 CSSI,同时时钟故障将导致系统时钟自动切换到 HSI 振荡器;CSS 作用就是监测 HSE 振荡器的正常运行,能够在 HSE 出故障时,及时调整系统时钟源保证系统正常运作;图 1 中为黄色方形。


使用特权

评论回复
板凳
keaibukelian|  楼主 | 2021-6-4 18:55 | 只看该作者
三、STM32F10x 时钟控制
1、控制概述
   STM32F10x 芯片的时钟控制主要包括以下几个方面知识;系统时钟源的选择、系统时钟频率的配置、总线时钟的配置、总线设备时钟的使能/除能;

   (1)系统时钟源的选择:系统时钟源为 SYSCLK,由 SW(系统时钟切换)选择时钟源来作为系统时钟 SYSCLK,可供选择的输入时钟有 HSE、HSI、PLLCLK(PLL 输出);

   (2)系统时钟频率的配置:选择 SW(系统时钟切换)的 3 个输入时钟源的其中一个的时钟频率作为系统频率;HSI 则是 HSI RC 的频率,通常为 8MHz;PLLCLK 作为系统时钟频率时,需要根据 PLLXTPRE 和 PLLSRC 两个选择器的输出经 PLL 倍频后作为系统时钟频率,通常为 8MHz;HSE 直接作为系统时钟频率使用相对较少;
和 SW 三个选择器和 PLL 倍频器来控制,输入时钟源经 PLL 倍频后输出作为 SW(系统时钟切换)的其中一项输入,再经过 SW 选择后作为 SYSCLK(系统时钟);

   (3)总线时钟的配置:AHB 总线,输入时钟源 SYSCLK 经 AHB 预分频器分频后输出,输出为 HCLK,高性能总线供给时钟;APB1 低速外设总线,输入时钟源 HCLK 经 APB1 分频器预分频后输出,输出为 PCLK1,低速外设总线时钟;APB2 高速外设总线,输入时钟源 HCLK 经 APB2 分频器预分频后输出,输出为 PCLK2,高速外设总线时钟;

   (4)总线设备时钟的使能/除能:主要是外设总线 APB1 和 APB2 的使能/不使能,时钟初始化默认是不使能,如果要使用外设,则需要打开外设对应的时钟;

   即在使用STM32F10x之前需要先进行时钟系统的相关配置,也就是时钟初始化,在工程文件中会在启动时进行一个默认的初始化SystemInit(),使用者在初始化完成后在进行所需的个性配置;默认配置是在启动时系统操作寄存器来进行配置,寄存器配置想对麻烦,所以在使用者进行个性化设置时,是使用库函数直接配置。

2、时钟初始化
   初始化时钟系统时,在工程文件 system_stm32f10x.c 中通过 SystemInit()和 SetSysClock()两个函数进行初始化,使用的是寄存器操作的方法来初始化时钟系统;因为是直接操作寄存器,所以初始化过程非常复杂,以下是这两个函数的解析,仅供学习、参考用(以 STM32F103C8T6 为例);可跳过,直接看如何通过库函数配置即可。

   SystemInit(),启动芯片,将系统频率设置为8MHz,关闭所有外设;具体操作为:启动内部时钟源 HSI RC(8MHz)作为系统时钟、配置总线时钟预分频器和外设总线时钟预分频器、配置 ADC 预分频器和定时器预分频器/倍频器、微控制器时钟不输出、关闭 HSE Osc、关闭 CSS 时钟监视系统、PLL 不使能、配置 PLLXTPRE、PLLSRC、PLL 输出、USB 预分频、关闭时钟中断使能(LSI、LSE、HSE、HSI、PL 就绪中断使能)、清除中断标志位(LSI、LSE、HSE、HSI、PLL就绪)、清除安全系统时钟中断;

void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose)  */
  /*Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;
  // 将时钟控制寄存器RCC_CR[0]位置1
  // 内部8MHz振荡器启动
  //
  //
  // RCC-CR[0]
  // HSION内部高速时钟使能 (Internal high-speed clock enable)
  // 0:内部8MHz振荡器关闭; 1:内部8MHz振荡器开启。

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
// 判断所使用stm32f10x类型:(点击魔术棒选择C/C++选项,观察Define即可查看产品类型)
// cl:互联型产品,stm32f105/107系列
// vl:超值型产品,stm32f100系列
// xl:超高密度产品,stm32f101/103系列
// ld:低密度产品,FLASH小于64K
// md:中等密度产品,FLASH=64 or 128
// hd:高密度产品,FLASH大于128
// 这里使用的是md中等密度产品,所以执行if not define 中的代码

// 将RCC-CFGR寄存器中的[0:1]、[4:7]位和[24:26]位置0
// 将HSI作为系统时钟,AHB预分频系数配置为SYSCLK不分频,低速APB预分频系数配置为HCLK不分频,高速APB预分频系数配置为HCLK不分频,ADC预分频系数配置为PCLK2 2分频后作为ADC系统时钟,微控制器时钟输出配置为没有时钟输出,因为RCC-CFGR[3:2]为系统时钟切换状态,是只读位,由硬件置1或置0;
//
//
// RCC-CFGR[1,0]:SW,系统时钟切换 (System clock switch)
// 00:HSI作为系统时钟; 01:HSE作为系统时钟;
// 10:PLL输出作为系统时钟; 11:不可用。
//
// RCC-CFGR[7,4]
// HPRE,AHB预分频 (AHB Presscaler)
// 0xxx:SYSCLK  不分频       1000:SYSCLK  2分频     1001:SYSCLK  4分频    1010:SYSCLK   8分频
// 1011:SYSCLK  16分频       1100:SYSCLK  64分频    1101:SYSCLK 128分频    1111:SYSCLK 512分频    1110:SYSCLK 256分频
//
//
// RCC-CFGR[10,8]
// PPRE1,低速APB预分频 (APB1)(APB low-speed prescaler (APB1))
// 0xx:HCLK不分频 100:HCLK 2分频 101:HCLK 4分频 110:HCLK 8分频 111:HCLK 16分频
//
// RCC-CFGR[13,11]
// PPRE2,高速APB预分频 (APB2) (APB high-speed prescaler (APB2))
// 0xx:HCLK不分频 100:HCLK 2分频 101:HCLK 4分频 110:HCLK 8分频 111:HCLK 16分频
//
// RCC-CFGR[15,14]
// ADCPRE,ADC预分频 (ADC prescaler)
// 00:PCLK2 2分频后作为ADC时钟 01:PCLK2 4分频后作为ADC时钟 10:PCLK2 6分频后作为ADC时钟 11:PCLK2 8分频后作为ADC时钟
//
// RCC-CFGR[26,24]
// MCO,微控制器时钟输出,置0为不输出时钟输出;PA8引脚具有复用功能——时钟输出(MCO)
// 0xx:没有时钟输出; 100:系统时钟(SYSCLK)输出; 101:内部RC振荡器时钟(HSI)输出; 110:外部振荡器时钟(HSE)输出; 111:PLL时钟2分频后输出

  /* Reset HSEON, CSSON and PLLON bits  */
  RCC->CR &= (uint32_t)0xFEF6FFFF;
// 将时钟控制寄存器RCC-CR[16]、[19]、[24]置0
// 外部高速时钟使能配置为HSE振荡器关闭,时钟安全系统使能配置为时钟检测器关闭,PLL使能配置为PLL关闭
//
//
// RCC-CR[16]:
// HSEON,外部高速时钟使能 (External high-speed clock enable)
// 0:HSE振荡器关闭; 1:HSE振荡器开启
//
// RCC-CR[19]:
// CSSON,时钟安全系统使能 (Clock security system enable)
// 0:时钟监测器关闭; 1:如果外部4-16MHz振荡器就绪,时钟监测器开启
//
// RCC-CR[24]:
// PLLON,PLL使能 (PLL enable)
// 0:PLL关闭; 1:PLL使能

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;
  // 将时钟控制寄存器RCC-CR[18]置0
  // 将外部高速时钟旁路配置为外部4-16MHz振荡器不被旁路


  // RCC->CR[18]:
  // HSEBYP,外部高速时钟旁路 (External high-speed clock bypass)
  // 0:外部4-16MHz振荡器没有旁路; 1:外部4-16MHz外部晶体振荡器被旁路
  //????????

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;
  // 将时钟配置寄存器RCC-CFGR[22,16]置0
  // 将PLL输入时钟源配置为HSI振荡器时钟经2分频后作为PLL输入时钟,PLL倍频系数配置为PLL 2倍频输出,USB预分频配置为PLL时钟1.5倍分频作为USB时钟;

  // RCC->CFGR[16]:
  // PLLSRC,PLL输入时钟源 (PLL entry clock source)
  // 0:HSI振荡器时钟经2分频后作为PLL输入时钟 1:HSE时钟作为PLL输入时钟。

  // RCC->CFGR[17]:
  // PLLXTPRE,HSE分频器作为PLL输入 (HSE divider for PLL entry),0:HSE不分频;
  // 0:HSE不分频 1:HSE 2分频

  // RCC->CFGR[21,18]:
  // PLLMUL,PLL倍频系数 (PLL multiplication factor),0000:PLL 2倍频输出;
  // 0000:PLL 2倍频输出    1000:PLL 10倍频输出    0001:PLL 3倍频输出     1001:PLL 11倍频输出
  // 0010:PLL 4倍频输出    1010:PLL 12倍频输出    0011:PLL 5倍频输出     1011:PLL 13倍频输出
  // 0100:PLL 6倍频输出    1100:PLL 14倍频输出    0101:PLL 7倍频输出     1101:PLL 15倍频输出
  // 0110:PLL 8倍频输出    1110:PLL 16倍频输出    0111:PLL 9倍频输出     1111:PLL 16倍频输出

  // RCC->CFGR[22]:
  // USBPRE,USB预分频 (USB prescaler),0:PLL时钟1.5倍分频作为USB时钟
  // 0:PLL时钟1.5倍分频作为USB时钟 1:PLL时钟直接作为USB时钟

//ifdef同上,只执行else中的一句
#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
// 时钟中断寄存器RCC-CIR[23]和[20,16]位置1,[12:8]位置0
// 配置LSI就绪中断使能、LSE就绪中断使能、HSE就绪中断使能、HSI就绪中断使能、PLL就绪中断使能为中断关闭
// 清除LSI就绪中断标志位、LSE就绪中断标志位、HSE就绪中断标志位、HSI就绪中断标志位、PLL就绪中断标志位,清除时钟安全系统中断;
// 总结来说就是关闭所有时钟中断,清除所有时钟中断标志位
//
// RCC-CIR[8]:
// LSIRDYIE,LSI就绪中断使能(LSI ready interrupt enable)
// 0:LSI就绪中断关闭; 1:LSI就绪中断使能
//
// RCC-CIR[9]:
// LSERDYIE,LSE就绪中断使能 (LSE ready interrupt enable)
// 0:LSE就绪中断关闭; 1:LSE就绪中断使能
//
// RCC-CIR[10]:
// HSIRDYIE,HSI就绪中断使能 (HSI ready interrupt enable)
// 0:HSI就绪中断关闭; 1:HSI就绪中断使能
//
// RCC-CIR[11]:
// HSERDYIE,HSE就绪中断使能 (HSE ready interrupt enable)
// 0:HSE就绪中断关闭; 1:HSE就绪中断使能
//
// RCC-CIR[12]:
// PLLRDYIE,PLL就绪中断使能 (PLL ready interrupt enable)
// 0:PLL就绪中断关闭; 1:PLL就绪中断使能
//
// RCC-CIR[16]:
// LSIRDYC,清除LSI就绪中断 (LSI ready interrupt clear)
// 0:无作用; 1:清除LSI就绪中断标志位LSIRDYF
//
// RCC-CIR[17]:
// LSERDYC,清除LSE就绪中断 (LSE ready interrupt clear)
// 0:无作用; 1:清除LSE就绪中断标志位LSERDY F
//
// RCC-CIR[18]:
// HSIRDYC,清除HSI就绪中断 (HSI ready interrupt clear)
// 0:无作用; 1:清除HSI就绪中断标志位HSIRDYF
//
// RCC-CIR[19]:
// HSERDYC,清除HSE就绪中断 (HSE ready interrupt clear)
// 0:无作用; 1:清除HSE就绪中断标志位HSERDYF
//
// RCC-CIR[20]:
// PLLRDYC,清除PLL就绪中断 (PLL ready interrupt clear)
// 0:无作用; 1:清除PLL就绪中断标志位PLLRDYF
//
// RCC-CIR[23]:
// CSSC,清除时钟安全系统中断 (Clock security system interrupt clear)
// 0:无作用; 1:清除CSSF安全系统中断标志位

// 同上,不执行
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl();
  #endif /* DATA_IN_ExtSRAM */
#endif

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}


使用特权

评论回复
地板
keaibukelian|  楼主 | 2021-6-4 18:56 | 只看该作者
SetSysClock(),根据system_stm32f10x.c文件中的宏定义来设置系统频率,宏定义系统频率多少,则初始化后的系统频率为多少;可以在启动前更改system_stm32f10x.c文件中的宏定义来更改初始化时系统频率;

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif

/* If none of the define above is enabled, the HSI is used as System clock
    source (default after reset) */

    //根据文件开头的宏定义来设置系统时钟频率
}


使用特权

评论回复
5
keaibukelian|  楼主 | 2021-6-4 18:56 | 只看该作者
SetSysClockTo72(),将系统频率更改为72MHz,并设置相关的分频器/倍频器;具体操作为打开HSE并等待HSE稳定、设置Flash、配置AHB不分频、APB2不分频、APB1二分频、设置PLLXTPRE寄存器、设置PLLCRC寄存器、设置PLL倍频系数和PLL输出时钟选择、PLL使能并等待PLL稳定、配置SW输出为PLLCLK并等待稳定;

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
  /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  // 将外部高速时钟使能配置为HSE振荡器开启
  //
  // #define  RCC_CR_HSEON ((uint32_t)0x00010000) /*!< External High Speed clock enable */
  //
  // RCC-CR[16]:
  // HSEON,外部高速时钟使能(External high-speed clock enable)
  // 0:HSE振荡器关闭; 1:HSE振荡器开启。

  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
  // 等待HSE外部高速时钟稳定或超时
  //
  // #define  RCC_CR_HSERDY ((uint32_t)0x00020000) /*!< External High Speed clock ready flag */
  //
  // RCC-CR[17]:
  // HSERDY,外部高速时钟就绪标志 (External high-speed clock ready flag)
  // 0:外部4-16MHz振荡器没有就绪; 1:外部4-16MHz振荡器就绪。

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
    //HSE稳定,说明上边do while循环语句是因为HSE稳定才退出,HSEStatus=0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
    // 否则说明超时了HSE还没有稳定,HSE=0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    // HSEStatus=0x01,说明HSE启动成功

    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;

    // Flash  使能flash预存储缓冲区,进行flash相关设置
    // CPU频率大于Flash频率,所以需要CPU等待Flash完成,等待与CPU频率相对应的周期


    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
    // AHB预分频不分频,输出时钟HCLK频率等于系统时钟SYSCLK频率
    //
    // #define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!< SYSCLK not divided */
    //
    // RCC-CFGR[7:4]:
    // HPRE,AHB预分频 (AHB Prescaler)
    // 0xxx:SYSCLK  不分频     1000:SYSCLK    2分频 1100:SYSCLK  64分频    1001: SYSCLK 4分频
    // 1101:SYSCLK 128分频     1010:SYSCLK    8分频 1110:SYSCLK 256分频    1011:SYSCLK 16分频 1111:SYSCLK 512分频

    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    // 高速APB预分频(APB2)不分频,PCLK2频率设置为HCLK频率,即PCLK2=HCLK
    //
    // #define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000) /*!< HCLK not divided */
    //
    // RCC-CFGR[13:11]:
    // PPRE2,高速APB预分频(APB2) (APB high-speed prescaler (APB2))
    // 0xx:HCLK不分频 100:HCLK 2分频 101:HCLK 4分频 110:HCLK 8分频 111:HCLK 16分频

    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
    // 疑似官方注释错误,代码中PCLK1为HCLK的二分频36MHz,且PCLK1最大为36MHz
    // 正确注释应该为 PCLK1 = HCLK/2
    //
    // 低速APB预分频(APB1)系数为HCLK 2分频,即PCLK=HCLK/2
    //
    // #define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400) /*!< HCLK divided by 2 */
    //
    // RCC-CFGR[2:0]:
    // PPRE1,低速APB预分频(APB1) (APB low-speed prescaler (APB1)),警告:软件必须保证APB1时钟频率不超过36MHz。
    // 0xx:HCLK不分频 100:HCLK 2分频 101:HCLK 4分频 110:HCLK 8分频 111:HCLK 16分频


    //同上,不执行ifdef中代码
#ifdef STM32F10X_CL
    /* Configure PLLs ------------------------------------------------------*/
    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */

    RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
                              RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
    RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
                             RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);

    /* Enable PLL2 */
    RCC->CR |= RCC_CR_PLL2ON;
    /* Wait till PLL2 is ready */
    while((RCC->CR & RCC_CR_PLL2RDY) == 0)
    {
    }


    /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
                            RCC_CFGR_PLLMULL9);
#else
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
    // 将RCC-CFGR寄存器[16,21]位清0,其它位不变
    //
    // PLL输入时钟源配置为HSI振荡器时钟经2分频后作为PLL输入时钟,HSE分频器配置为不分频作为PLL输入,
    // PREDIV1分频因子的低位即RCC-CFGR[17]置0,表示不分频,HES(8MHz)不分频直接输入;
    // PLL倍频系数[18,21]和PLL输入时钟源[16]需要置1,在下一条语句进行设置

    // #define  RCC_CFGR_PLLSRC ((uint32_t)0x00010000) /*!< PLL entry clock source */
    // #define  RCC_CFGR_PLLXTPRE ((uint32_t)0x00020000) /*!< HSE divider for PLL entry */
    // #define  RCC_CFGR_PLLMULL ((uint32_t)0x003C0000) /*!< PLLMUL[3:0] bits (PLL multiplication factor) */
    // (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |RCC_CFGR_PLLMULL) = 0x003f0000
    // ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |RCC_CFGR_PLLMULL) = 0xffc0ffff
    //
    // RCC-CFGR[16]:
    // PLLSR,PLL输入时钟源 (PLL entry clock source)
    // 0:HSI振荡器时钟经2分频后作为PLL输入时钟 1:HSE时钟作为PLL输入时钟。
    //
    // RCC-CFGR[17]:
    // PLLXTPRE,PREDIV1分频因子的低位 (LSB of division factor PREDIV1)
    // 0:HSE不分频 1:HSE 2分频
    //


    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
    // 将RCC-CFGR寄存器[16]和[18,20]位置1,其它位不变
    // 设置PLL输入时钟源为PREDIV1(8MHz)输入,设置PLL倍频系数为9倍频,输出PLLCLK频率为72MHz(8MHz*9=72MHZ);
    //
    // #define  RCC_CFGR_PLLSRC_HSE ((uint32_t)0x00010000) /*!< HSE clock selected as PLL entry clock source */
    // #define  RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000) /*!< PLL input clock*9 */
    //
    // RCC-CFGR[16]:
    // PLLSR,PLL输入时钟源 (PLL entry clock source)
    // 0:HSI振荡器时钟经2分频后作为PLL输入时钟 1:HSE时钟作为PLL输入时钟。
    //
    // RCC-CFGR[21,18]:
    // PLLMUL,PLL倍频系数 (PLL multiplication factor)
    // 0000:PLL 2倍频输出    1000:PLL 10倍频输出    0001:PLL 3倍频输出     1001:PLL 11倍频输出
    // 0010:PLL 4倍频输出    1010:PLL 12倍频输出    0011:PLL 5倍频输出     1011:PLL 13倍频输出
    // 0100:PLL 6倍频输出    1100:PLL 14倍频输出    0101:PLL 7倍频输出     1101:PLL 15倍频输出
    // 0110:PLL 8倍频输出    1110:PLL 16倍频输出    0111:PLL 9倍频输出     1111:PLL 16倍频输出

#endif /* STM32F10X_CL */

    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;
    // 将RCC-CR寄存器[24]位置1
    // 设置PLL使能配置为PLL使能
    //
    // #define  RCC_CR_PLLON ((uint32_t)0x01000000) /*!< PLL enable */
    // RCC-CR[24]:
    // PLLON,PLL使能 (PLL enable),1:PLL使能。

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    // 等待PLL时钟就绪
    // RCC-CR寄存器
    // PLLRDY[25]:PLL时钟就绪标志 (PLL clock ready flag),PLL锁定后由硬件置’1’,该位只读
    // 0:PLL未锁定; 1:PLL锁定。

    /* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    // 将RCC-CFGR寄存器的[1,0]位置0
    //
    // #define RCC_CFGR_SW ((uint32_t)0x00000003) /*!< SW[1:0] bits (System clock Switch) */
    //
    // RCC_CFGR[1:0]:
    // SW,系统时钟切换 (System clock switch),由软件置’1’或清’0’来选择系统时钟源(SYSCLK)
    // 00:HSI作为系统时钟; 01:HSE作为系统时钟; 10:PLL输出作为系统时钟; 11:不可用。

    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
    // 将RCC-CFGR寄存器的[1:0]位赋值
    // PLL作为系统时钟输出
    //
    // #define RCC_CFGR_SW_PLL ((uint32_t)0x00000002) /*!< PLL selected as system clock */
    // RCC_CFGR寄存器
    // SW[1:0]:系统时钟切换 (System clock switch),由软件置’1’或清’0’来选择系统时钟源(SYSCLK)
    // 10:PLL输出作为系统时钟

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
    // 等待PLL时钟作为系统时钟源稳定
    //
    // #define RCC_CFGR_SWS ((uint32_t)0x0000000C) /*!< SWS[1:0] bits (System Clock Switch Status) */
    //
    // RCC-CFGR[3:2]:
    // SWS,系统时钟切换状态 (System clock switch status),由硬件置’1’或清’0’来指示哪一个时钟源被作为系统时钟,该位只读
    // 00:HSI作为系统时钟; 01:HSE作为系统时钟; 10:PLL输出作为系统时钟; 11:不可用。
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
    //HSE启动失败,进行了错误的时钟配置,用户可以在这里添加代码处理这个错误
  }
}


使用特权

评论回复
6
keaibukelian|  楼主 | 2021-6-4 18:56 | 只看该作者
可以看出,初始化时,先使内部的时钟源HSI RC将系统时钟设置为8MHz,然后设置和时钟相关的总线和外设(总线时钟、外设时钟、定时器时钟、USB时钟、ADC时钟、MCO配置等),设置但是不使能,最后关闭时钟中断、清除时钟中断标志位;然后将系统时钟频率切换到宏定义的时钟(主要是通过设置PLL倍频系数来设置系统频率),然后再次设置时钟相关的总线外设、同样是设置但不使能;


使用特权

评论回复
7
keaibukelian|  楼主 | 2021-6-4 18:57 | 只看该作者
四、总结
1、常用寄存器
   AHBENR寄存器: DMA,SDIO等时钟使能;

   APB1ENR寄存器:APB1总线上外设时钟使能;

   APB2ENR寄存器:APB2总线上外设时钟使能;

   CR寄存器:HSI,HSE,CSS,PLL等的使能和就绪标志位,因为有些时钟源刚使能完不一定能马上稳定,需要等一段时间,当稳定之后就会在该寄存器相应的位置位,通过查看寄存器的值就可以知道该时钟源是否已经稳定;

   CFGR寄存器:时钟源的选择和分频系数的设定。

2、常用库函数
  时钟使能配置函数:
RCC_LSEConfig()、RCC_HSEConfig()、RCC_HSICmd()、RCC_LSICmd()、RCC_PLLCmd()

  时钟源相关配置函数:
RCC_PLLConfig ()、RCC_SYSCLKConfig()、RCC_RTCCLKConfig()

  分频系数选择配置函数:
RCC_HCLKConfig()、RCC_PCLK1Config()、RCC_PCLK2Config()

  外设时钟使能函数:
RCC_APB1PeriphClockCmd()、RCC_APB2PeriphClockCmd()、RCC_AHBPeriphClockCmd();

  其他外设时钟配置:
RCC_ADCCLKConfig ()、 RCC_RTCCLKConfig();

  状态参数获取参数:
RCC_GetClocksFreq()、RCC_GetSYSCLKSource()、RCC_GetFlagStatus()

  RCC中断相关函数 :
RCC_ITConfig()、RCC_GetITStatus()、RCC_ClearITPendingBit()

3、总结
   STM32的时钟系统确实是很复杂,不仅有倍频,分频,还有一系列的外设时钟开关。倍频是考虑到了电磁兼容性,如果外部直接提供一个72MHz的晶振,太高的震荡频率会给电路板的制作带来一定的难度。分频则是因为STM32既有高速外设,也有低速外设,各外设的工作频率不相同,需要分开来管理。最后,每个外设时钟还有自己独立的开关(在图上可以看到,在外设时钟之前需要经过一个与门,这就是它们的开关)在我们不使用该外设时,需要把时钟关闭以减少STM32的功耗。

  STM32为了实现低功耗,将所有的外设时钟都默认设置为不使能(disable),用到什么外设,只要打开对应外设的时钟就可以,其他的没用到的可以还是不使能(disable),这样耗能就会减少。这就是为什么不管你配置什么功能都需要先打开对应的时钟的原因。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

76

主题

4145

帖子

5

粉丝