【STM32学习笔记】系统时钟详解

前言

什么是系统时钟,系统时钟我们可以理解为人的心脏,没有系统时钟就没有,单片机就正常无法运行。

一.系统时钟原理

1.系统时钟框图

下面来分析各个元素

1.最左边

OSC_OUT和OCS_IN是两个外部引脚,用来接晶振。根据规格书我们知道可以接4-16mhz的晶振。

mco:STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL   输出的2分频、HSI、HSE、或者系统时钟,可以用来验证系统时钟配置是否正确。

OSC32_OUT和OCS32_IN引脚是STM32单片机上的外部32.768 kHz低速晶体振荡器的输入和输出引脚。这些引脚用于连接外部32.768 kHz晶体振荡器,以提供低功耗时钟信号给单片机。适用于需要长时间运行、低功耗和精确时间计量的应用场景。

2.stm32的5个时钟源

1. STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。    

①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。

②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围4MHz~16MHz。 ③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。WDG

④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC  

⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

系统时钟SYSCLK可来源于三个时钟源:HSI振荡器时钟、HSE振荡器时钟、PLL时钟。

3.几个重要的时钟

这些时钟都是通过时钟源、倍频器和预分频器的各种配置而得到的:

SYSCLK(系统时钟)
AHB(advanced high performance bus,高级高性能总线)总线时钟
APB1(advanced peripheral bus,高级外围设备总线)总线时钟(低速):速度最高为36MHz
APB2总线时钟(高速):速度最高为72MHz
PLL时钟
以及还有一些未提及的外设时钟(最右侧),这些外设的时钟要根据外设的需求来进行配置。

4.预分频器(为绿色)

分频器的作用是:系统时钟先经过固定的分频系数后产生相应频率的时钟,提供给单片机定时器的计时输入。

5.时钟安全系统CSS

 

如果外部晶振出了问题(例如被短路),系统时钟(SYSCLK)就会崩溃。因此,STM32时钟系统设置了CSS时钟安全系统,一旦HSE失效,则自动切换至SYSCLK = HSI,意味着系统用内部RC振荡器作为时钟源(对照时钟树的路线看看是怎么进行切换的!)。当然RC振荡器并不是十分精确的,当外部晶振恢复正常后,我们还是希望系统切换回外部晶振。

2.选择其中一条线 

OSC_IN (8MHz) –> HSE_OSC –> 选择器 –> 选择器 –> PLL (8MHz X 9 = 72MHz) –> 作为PLLCLK –> 选择器 –> 作为SYSCLK –> AHB预分频器 –> 作为HCLK(advanced high performance bus clock,高级高性能总线时钟)

3.RCC相关定时器函数及其定义

/** 
  * @brief Reset and Clock Control
  */

typedef struct
{
  __IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就绪标志位
  __IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定
  __IO uint32_t CIR; // 清除/使能 时钟就绪中断
  __IO uint32_t APB2RSTR; //APB2线上外设复位寄存器
  __IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
  __IO uint32_t AHBENR; //DMA,SDIO等时钟使能
  __IO uint32_t APB2ENR; //APB2线上外设时钟使能
  __IO uint32_t APB1ENR; //APB1线上外设时钟使能
  __IO uint32_t BDCR; //备份域控制寄存器
  __IO uint32_t CSR; //控制状态寄存器

#ifdef STM32F10X_CL  
  __IO uint32_t AHBRSTR;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_CL */ 

#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)   
  uint32_t RESERVED0;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */ 
} RCC_TypeDef;

需要注意,STM32F10X_CL这些都属于互联型,编写者为了兼容更多机型而采用了条件编译的办法。我们使用的是大容量(STM32F10X_HD),因此有条件编译的语句只看STM32F10X_HD的即可。在库函数中经常涉及这种情况。

二.系统初始化的时钟配置

不知道大家有没有发现一个问题,明明没有初始化过时钟,但是只要打开引脚相应的时钟,等和蜂鸣器就会正常工作,原因是因为系统在启动时会优先执行SystemInit,然后才会进进入main函数

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

1.首先看一下SystemInit()函数

我们可以看到在相关文件的开头定义了宏定义,那么在默认模式使用的是72MHZ。

  

void SystemInit (void)
{
  //首先设置开启内部8MHz振荡器
  RCC->CR |= (uint32_t)0x00000001;
  SetSysClock();
}/** 

2.SetSysClock()

由于我们把系统时钟频率通过宏定义设置为72MHz,所以这里直接进入最下面的程序即SetSysClockTo72(),

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) */ 
}

3.SetSysClockTo72()


static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 配置---------------------------*/    
  /* 使能 HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);//RCC_CR寄存器第16位置1,开启HSE
 
  /* 等待HSE就绪 超时退出 */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;  //读取RCC_CR第17位
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
 
  if ((RCC->CR & RCC_CR_HSERDY) != RESET) //读取RCC_CR第17位
  {
    HSEStatus = (uint32_t)0x01;   //HSE状态写1
  }
  else
  {
    HSEStatus = (uint32_t)0x00;   //HSE状态写0
  }  
 
  if (HSEStatus == (uint32_t)0x01) //FLASH寄存器部分可以不用管
  {
    /* 使能 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;  //RCC_CFGR寄存器第4~7位写0,即AHB不分频,即HCLK使用72MHz时钟
       
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //RCC_CFGR寄存器第11~13位写0,APB2不分频,即PCLK2使用72MHz时钟
    
    /* PCLK1 = HCLK/2 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //RCC_CFGR寄存器第8~10位写100,APB1二分频,即PCLK1使用36MHz时钟
 
#else    
    /*  PLL 配置: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL)); //RCC_CFGR寄存器的16~21位清零
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//RCC_CFGR寄存器第16位置1,使HSE作为PLL输入时钟;17位置0不变,使HSE作为PLL输入时钟时不分频。RCC_CFGR寄存器第18~21位置0111,配置位PLL 9倍频输出给PLLCLK,即PLLCLK为72MHz
#endif /* STM32F10X_CL */
 
    /* 使能 PLL */
    RCC->CR |= RCC_CR_PLLON; //RCC->CR寄存器第24位置1,使能PLL
 
    /* 等待PLL就绪 */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)  //读取PLL时钟就绪标志位(第25位)
    {
    }
    
    /* 选择PLL作为系统时钟源 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); //RCC->CFGR寄存器第0~1位清零
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    //RCC->CFGR寄存器第0~1位(SW位)置10,即PLL输出作为系统时钟
 
    /* 等待PLL被用作系统时钟源就绪 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) //等待RCC->CFGR寄存器第2~3位(SWS位)为10即就绪
    {
    }
  }
  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 */
  }
}

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值