第17章.RCC-STM32时钟配置

 目录

 

0. 《STM32单片机自学教程》专栏

17.1 STM32时钟树

17.1.1 时钟源

17.1.2 锁相环PLL  

17.1.3 系统时钟

17.1.3.1 系统时钟SYSCLK         

17.1.3.2 AHB/APB总线时钟

17.1.3.3 其他时钟 

17.1.3.4 MCO 时钟输出 

17.2 系统时钟库函数 

17.3 系统时钟配置练习 


0. 《STM32单片机自学教程》专栏

        本文作为专栏《STM32单片机自学教程》专栏其中的一部分,返回专栏总纲,阅读所有文章,点击Link:  

STM32单片机自学教程-[目录总纲]_stm32 学习-优快云博客     

        RCC(Reset Clock Control) 复位和时钟控制,包括复位和时钟控制两部分内容,本章我们主要讲解时钟部分. 大家可以参考《STM32F10X-中文参考手册》中的RCC 章节,里面有更详细的解释。本章我们首先学习一下STM32的时钟树,然后通过学习系统时钟函数SetSysClockTo72()加深对系统时钟配置的理解,最后通过一个简单的实例来实践时钟配置的方法。

17.1 STM32时钟树

        MCU都是基于时序控制的,时序系统对MCU非常重要,因此在MCU设计时就设计了专门用于控制时序的电路,在芯片设计中称为时钟树设计。由此设计出来的时钟,可以精确控制我们的单片机系统。一个MCU越复杂,时钟系统也会相应地变得复杂,如STM32F1的时钟系统比较复杂,不像简单的51单片机一个系统时钟就可以解决一切。对于STM32F1系列的芯片,正常工作的主频可以达到72Mhz,但并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及RTC只需要几十kHZ的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的MCU一般都是采取多时钟源的方法来解决这些问题。所以我们称为时钟树而不是时钟。

        STM32非常复杂,外设多,为了实现低功耗的要求,STM32的主控默认不开启这些外设功能。用户可以根据需要决定STM32要开启的功能,这个功能开关在STM32主控中也就是各个外设的时钟。

图17.1-1 STM32时钟树 

        在上图中把主要常关注几处标注出来了。A部分表示其他电路需要的输入时钟信号源;B为一个特殊的振荡电路“PLL”;C-K为各类时钟;L是STM32的时钟输出功能。其他部分后续如有用到我们再详细探讨。接下来我们对各部分进行讲解:

17.1.1 时钟源

        如图17.1-1中标识A的部分。对于STM32F1系列单片机,输入时钟源(InputClock)主要包括四种,即HSI,HSE,LSI,LSE。其中,从时钟频率来分可以分为高速时钟源和低速时钟源,其中HIS和HSE为高速时钟,LSI和LSE是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中HSE和LSE是外部时钟源;其他是内部时钟源,芯片上电即可产生,不需要借助外部电路。

①高速外部振荡器 HSE (High Speed External Clock signal)

        HSE是高速的外部时钟信号,可以由有源晶振或者无源晶振提供,频率从4-16MHZ不等。当使用有源晶振时,时钟从OSC_IN引脚进入,OSC_OUT引脚悬空,当选用无源晶振时,时钟从OSC_IN和OSC_OUT进入,并且要配谐振电容。HSE 最常使用的就是8M 的无源晶振。

        当确定PLL 时钟来源的时候,HSE 可以不分频或者2 分频,这个由时钟配置寄存器CFGR 的17位PLLXTPRE 设置,我们一般设置为HSE 不分频。

 ②低速外部振荡器 LSE (Low Speed External Clock signal)

        外接 32.768kHz晶振,主要作用于 RTC 的时钟源。 

③高速内部振荡器 HSI(High Speed Internal Clock signal) 

        HSI时钟信号由内部8MHz的RC振荡器产生,可直接作为系统时钟或在2分频后作为PLL输入。HSI的RC振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间比HSE晶体振荡器短。但是由于即使在校准之后它的时钟频率精度仍较差。我们一般使用的HSE。

④低速内部振荡器 LSI(Low Speed Internal Clock signal)  

        LSI担当一个低功耗时钟源的角色,它可以在停机和待机模式下保持运行,为独立看门狗和自动唤醒单元提供时钟。LSI时钟频率大约40kHz(在30kHz和60kHz之间)。LSIRC可以通过控制/状态寄存器(RCC_CSR)里的LSION位来启动或关闭。  

17.1.2 锁相环PLL  

        锁相环是自动控制系统中常用的一个反馈电路,在STM32主控中,锁相环的作用主要有是输入时钟净化和倍频。前者是利用锁相环电路的反馈机制实现,后者我们用于使芯片在更高且频率稳定的时钟下工作。在STM32中,锁相环的输出也可以作为芯片系统的时钟源。根据图17.1-1的时钟结构(图中标识为B的部分),使用锁相环时需要进行三个部分的配置。为了方便查看,截取了使用PLL作为系统时钟源的配置部分,如图17.1-2所示。         

图17.1-2 PLL配置示意图 

 ①PLLXTPRE:HSE 分频器作为 PLL 输入  (HSE divider for PLL entry) 

        即上图在标注为①的地方,它专门用于 HSE,ST 设计它有两种方式,并把它的控制功能放在 RCC_CFGR 寄存器中,说明如下图 17.1-3。我们由此得知,HSE输入PLL可以是 2 分频,或者是 1 分频(不分频)。经过 HSE 分频器处理后的输出振荡时钟信号比直接输入的时钟信号更稳定。 

图17.1-3 PLLXTPRE 设置选项值

②PLLSRC:PLL 输入时钟源  (PLL entry clock source) 

        图中②表示的是 PLL 时钟源的选择器。参考 F103 参考手册,如下图17.1-4. 它有两种可选择的输入源:一个是HSI 的二分频时钟,另一个是 前面PLLXTPRE 处理后的 HSE 信号。 

图17.1-4 PLLSRC 锁相环时钟源选择 

③PLLMUL:  PLL 倍频系数  (PLL multiplication factor) 

        图中③表示配置锁相环的倍频系数,倍频之后便是PLL 时钟PLLCLK。倍频因子可以是 2~16 倍。 如果我们要实现 72MHz 的主频率,我们通过选择 HSE 不分频作为 PLL 输入的时钟信号,即输入 8MHz,通过标号③选择倍频因子,可选择 2-16 倍频,我们选择 9 倍频,这样可以得到时钟信号为 8*9=72MHz。即PLL 时钟PLLCLK为72MHz,再下一步我们选为系统时钟后,系统时钟频率便为72MHz。

17.1.3 系统时钟

17.1.3.1 系统时钟SYSCLK         

        如图17.1-1中标识C的部分。STM32的系统时钟SYSCLK为整个芯片提供了时序信号。对单片机来说时钟频率越高,指令的执行速度越快,单位时间能处理的功能越多,但功耗也会越大。STM32的系统时钟是可配置的,在STM32F1系列中,系统时钟来源可以是:HSI、PLLCLK、HSE,通过CFGR的位SW[1:0]设置。

        我们一般设置系统时钟:SYSCLK = PLLCLK = 72M。如果仍使用PLL作为系统时钟源,如果使用HSI/2,那么可以得到最高主频8MHz/2*16=64MHz。

17.1.3.2 AHB/APB总线时钟

        从上面的图 17.1-1时钟树图可知,AHB、APB1、APB2、内核时钟等时钟通过系统时钟分频得到。 

AHB 总线时钟HCLK 

        如图17.1-1中标识D的部分。系统时钟SYSCLK经过AHB预分频器分频之后得到时钟叫APB总线时钟,即HCLK,分频因子可以是[1,2,4,8,16,64,128,256,512],具体的由时钟配置寄存器CFGR的位7-4:HPRE[3:0]设置。片上大部分外设的时钟都是经过HCLK分频得到,至于AHB总线上的外设的时钟设置为多少,得等到我们使用该外设的时候才设置。AHB的时钟我们一般设置为1分频,即HCLK=SYSCLK=72M。 

APB1 总线时钟PCLK1 

        如图17.1-1中标识E的部分。APB1总线时钟PCLK1由HCLK经过低速APB预分频器得到,分频因子可以是[1,2,4,8,16],具体的由时钟配置寄存器CFGR的位10-8:PRRE1[2:0]决定。PCLK1属于低速的总线时钟,最高为36M,片上低速的外设就挂载到这条总线上,比如USART2/3/4/5、SPI2/3,I2C1/2等。至于APB1总线上的外设的时钟设置为多少,得等到我们使用该外设的时候才设置。APB1这里我们设置为2分频,即PCLK1=HCLK/2=36M。

APB2 总线时钟PCLK2 

        如图17.1-1中标识F的部分。APB2总线时钟PCLK2由HCLK经过高速APB2预分频器得到,分频因子可以是[1,2,4,8,16],具体由时钟配置寄存器CFGR的位13-11:PPRE2[2:0]决定。PCLK2属于高速的总线时钟,片上高速的外设就挂载到这条总线上,比如全部的GPIO、US-ART1、SPI1等。至于APB2总线上的外设的时钟设置为多少,得等到我们使用该外设的时候才设置。对于APB2的时钟我们一般设置为1分频,即PCLK2=HCLK=72M。

17.1.3.3 其他时钟 

        除了上面的时钟,STM32还有很多时钟,剩下的时钟部分我们讲解几个重要的。原理都是一样的,大家会看手册配置即可,也没必要全部介绍。

USB时钟

        如图17.1-1中标识H的部分。USB时钟是由PLLCLK经过USB预分频器得到,分频因子可以是[1,1.5],具体的由时钟配置寄存器CFGR的位22:USBPRE配置。USB的时钟最高是48M,根据分频因子反推过来算,PLLCLK只能是48M或者是72Mhz。

        一般我们设置PLLCLK=72M,USBCLK=48Mhz。USB对时钟要求比较高,所以PLLCLK只能是由HSE倍频得到,不能使用HSI倍频。 

Cortex 系统时钟

        如图17.1-1中标识I的部分。Cortex 系统时钟由HCLK 8 分频得到,等于9MHZ,Cortex 系统时钟用来驱动内核的系统定时器SysTick。SysTick 一般用于操作系统的时钟节拍,也可以用做普通的定时。 

ADC时钟 

        如图17.1-1中标识G的部分。ADC时钟由PCLK2经过ADC预分频器得到,分频因子可以是[2,4,6,8],具体的由时钟配置寄存器CFGR的位15-14:ADCPRE[1:0]决定。ADC时钟最高只能是14MHz,反推PCLK2的时钟只能是:28Mhz、56Mhz、84Mhz、112Mhz,鉴于PCLK2最高是72Mhz,所以只能取28M和56Mhz。 

RTC 时钟 

        如图17.1-1中标识J的部分。RTC时钟可由HSE/128分频得到,也可由低速外部时钟信号LSE提供,频率为32.768KHZ,也可由低速内部时钟信号LSI提供,具体选用哪个时钟由备份域控制寄存器BDCR的位9-8:RTCSEL[1:0]配置。

独立看门狗时钟 

        如图17.1-1中标识K的部分。独立看门狗的时钟只能由LSI提供.LSI是低速的内部时钟信号,频率为40KHZ。 

17.1.3.4 MCO 时钟输出 

        MCO是Microcontroller Clock Output的缩写,是微控制器时钟输出引脚,在STM32F1系列中由PA8复用所得,主要作用是可以对外提供时钟,相当于一个有源晶振。MCO的时钟来源可以是:PLLCLK/2、HSI、HSE、SYSCLK,具体选哪个由时钟配置寄存器CFGR的位26-24:MCO[2:0]决定。除了对外提供时钟这个作用之外,我们还可以通过示波器监控MCO引脚的时钟输出来验证我们的系统时钟配置是否正确。 

17.2 系统时钟库函数 

        在固件库文件system_stm32f10x.c 的最后便是设置系统时钟的库函数SetSysClockTo72,如下代码:

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
   
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* 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));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    /* 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;    

 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK/2 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);


    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  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 */
  }
}

        上述代码把把互联型相关的代码删掉了(STM32F10X_CL部分),该函数是直接操作寄存器的,有关寄存器部分请参考数据手册的RCC的寄存器描述部分。从上面代码可以大致总结配置步骤如下:

        1.使能 HSE,并等待 HSE稳定

        2.设置 AHB、APB2、APB1预分频因子

        3.设置 PLL时钟来源,设置PLL倍频因子

        4.使能PLL

        5.等待PLL稳定 

        6.选择 PLL作为系统时钟来源

        7.读取时钟切换状态位,确保 PLLCLK  被选为系统时钟

        如果我们要配置某个外设的时候,假如配置APB1,我们也可以根据时钟树结构,根据时钟来源选择,设置分频因子这个步骤去配置,如下图17.2-1. 

图17.2-1 时钟设置流程示意 

17.3 系统时钟配置练习 

        STM32F103 默认的情况下使用的是内部8M的HSI作为时钟源,初始化后使用HSE,然后HSE经过PLL倍频之后作为系统时钟。通常的配置是:HSE=8Mhz,PLL的倍频因子为9,系统时钟就设置成:SYSCLK=8Mhz*9=72Mhz。使用HSE,系统时钟SYSCLK最高是128M。我们的库函数,当程序来到main函数之前,启动文件statup_stm32f10x_hd.s已经调用SystemInit()函数把系统时钟初始化成72MHZ,SystemInit()在库文件system_stm32f10x.c中定义。如果我们想把系统时钟设置低一点或者超频的话,可以修改底层的库文件,但为了维持库的完整性,我们可以根据时钟树的流程自行写一个。 

        当HSE故障的时候,如果PLL的时钟来源是HSE,那么当HSE故障的时候,这个时候CSS系统会自动切换HSI作为系统时钟,此时SYSCLK=HSI=8M。STM32时钟树的CSS(Clock Security System,时钟安全系统)是一个重要的安全机制,旨在提高系统的稳定性和可靠性,如图17.2-1中红色虚线框中位置。

        因为调用库函数都是使用HSE,如果用户对精度要求不高,不想用HSE(因为要配置外部电路),而是想用稳定性和精度都差的HSI,这样可以省去外围电路的配置。下面我们给出个使用HSI配置系统时钟例子供参考,方法和系统时钟配置类似。代码如下(摘自野火):

void HSI_SetSysClock(uint32_t pllmul)
{
	__IO uint32_t HSIStartUpStatus = 0;

	// 把RCC外设初始化成复位状态,这句是必须的
  RCC_DeInit();

  //使能HSI
	RCC_HSICmd(ENABLE);
	
	// 等待 HSI稳定
	while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET)
	{
	}	
  // 读取 HSI 就绪状态
	HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
	
	// 只有 HSI就绪之后则继续往下执行
  if (HSIStartUpStatus == RCC_CR_HSIRDY)
  {
//----------------------------------------------------------------------//
    // 使能FLASH 预存取缓冲区
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
		// 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
		// 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
		// 0:0 < SYSCLK <= 24M
		// 1:24< SYSCLK <= 48M
		// 2:48< SYSCLK <= 72M
    FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//
 
    // AHB预分频因子设置为1分频,HCLK = SYSCLK 
    RCC_HCLKConfig(RCC_SYSCLK_Div1); 
  
    // APB2预分频因子设置为1分频,PCLK2 = HCLK
    RCC_PCLK2Config(RCC_HCLK_Div1); 

    // APB1预分频因子设置为1分频,PCLK1 = HCLK/2 
    RCC_PCLK1Config(RCC_HCLK_Div2);
		
//-----------------设置各种频率主要就是在这里设置-------------------//
    // 设置PLL时钟来源为HSI,设置PLL倍频因子
		// PLLCLK = 4MHz * pllmul
		RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//

    // 开启PLL 
    RCC_PLLCmd(ENABLE);

    // 等待 PLL稳定
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
  else
  { // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
		// 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,
		// HSI是内部的高速时钟,8MHZ
    while (1)
    {
    }
  }
}

 参考资料:

        【1】哔站江协科技STM32入门教程

        【2】《STM32单片机原理与项目实战》刘龙、高照玲、田华著

        【3】《ARM Cortex-M3嵌入式原理及应用》黄可亚著

        【4】《STM32嵌入式微控制器快速上手》陈志旺著

        【5】《STM32单片机应用与全案例实践》沈红卫等著

        【6】《野火STM32开发指南》

        【7】《正点原子STM32开发指南》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

村里大明白

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值