STM32时钟系统讲解

STM32时钟总结:

系统时钟图:(先上劝退图!)
在这里插入图片描述

1、时钟系统结构总结

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。
2. 系统时钟SYSCLK可来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、PLL时钟
3.STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL
输出的2分频、HSI、HSE、或者系统时钟。

4.几个重要时钟:
SYSCLK(系统时钟) :
AHB总线时钟
APB1总线时钟(低速): 速度最高36MHz
APB2总线时钟(高速): 速度最高72MHz
PLL时钟

4.任何一个外设在使用之前,必须首先使能其相应的时钟。

2、RCC寄存器配置总结

调用结构体里面各个成员即对应寄存器:(斜体为常用配置)都是32位!
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; //控制状态寄存器
} RCC_TypeDef;
各寄存器详解在stm32中文参考指南中有详解,需要自己查询要了解寄存器原理要多看看
打开时钟源之后要等待其稳定

stm32库函数解析:
首先上SystemInit();函数
此函数用于初始化以上写到的寄存器

void SystemInit (void)
{
// 配置RCC_CR寄存器,对比指南:打开HSI RC时钟振荡器
  RCC->CR |= (uint32_t)0x00000001;

  /* 置零CFGR寄存器中 SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO 位 */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* 置零CR寄存器中 HSEON, CSSON and PLLON 位 */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /*置零CR寄存器中  HSEBYP */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* 置零CFGR寄存器中  PLLSRC, PLLXTPRE, PLLMUL  和 USBPRE/OTGFSPRE */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* 置零CR寄存器中  PLL2ON and PLL3ON*/
  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
  /*关掉中断及其相应位 */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif         // 一般未执行

//以上的配置完成,下面是一个重点函数涉及到具体设置系统时钟的频率
  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 
}

SetSysClock();函数具体配置

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
}

要调用哪个频率,宏定义中就定义哪个频率来配置:
保留想要配置的频率,注释不需要配置的频率
/* #define SYSCLK_FREQ_HSE HSE_VALUE /
/
#define SYSCLK_FREQ_24MHz 24000000 /
/
#define SYSCLK_FREQ_36MHz 36000000 /
/
#define SYSCLK_FREQ_48MHz 48000000 /
/
#define SYSCLK_FREQ_56MHz 56000000 /
#define SYSCLK_FREQ_72MHz 72000000
/
如果上述定义均未启用,则HSI用作系统时钟源(重置后默认) */

详解频率配置函数内容,以72M为例!(一大波预警!)
先上一波关于寄存器的宏定义
太多略过。。。 详细解读!

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* 使能打开 HSE CR低16位 */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /*  等待HSE稳定  */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;   //CR_低17位
    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)
  {
    /* 使能预取用缓存区,资料参考FALSH闪存参考资料! */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* 等待!参考资料*/
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

 //下面的是配置分频!分频!分频!,即配置开头图中AHB后面各个输出的频率(通过分频器分频)
    /* 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;


#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  //CL系列的暂时不看
  
    /* 输入频率配置 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);   // 倍频 9倍 8*9 = 72!
#endif /* STM32F10X_CL */

    /* 使能打开 PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* 等待稳定 Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /*设置PLL作为系统时钟源  */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* 等待PLL生效 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  else
  { 
  }
}

OVER

补充一下,一个设置在main函数之前执行预设函数的方法:
头文件中插入:
;是注释的意思,该段是先执行编译SystemInit(先去除注释才行)

; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
				;寄存器版本代码,因为没有用到SystemInit函数,所以注释掉以下代码为防止报错!
				;库函数版本代码,建议加上这里(外部必须实现SystemInit函数),以初始化stm32时钟等。
                ;IMPORT  SystemInit			
                ;LDR     R0, =SystemInit	
                ;BLX     R0                  
                LDR     R0, =__main
                BX      R0
                ENDP

猜你喜欢

转载自blog.csdn.net/qq_45396672/article/details/103020133