stm32系统时钟初始化过程剖析

时间:2024-03-30 08:20:49

STM32有以下4个时钟源: 
高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,我们一般采用8MHz的晶振。 
高速内部时钟(HSI) : 由内部RC振荡器产生,频率为8MHz,但不稳定。 
低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。 
低速内部时钟(LSI) :由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。

然而我们在main函数中并没有调用什么函数来选择systemclock啊,那么这个系统时钟源的选择是在那里初始化的了。。。

其实我们都忽略了STM32的启动文件。

        一般而言,系统上电后第一个执行的是由汇编所编写的启动文件,其主要工作为一下五部分:

      (1)、初始化堆栈指针SP=_initial_sp

      (2)、初始化PC指针,令其=Reset_Handler

      (3)、初始化中断向量表

      (4)、配置系统时钟

      (5)、调用C库函数_main初始化用户堆栈,从而最终调用main函数进入C的世界

         这上述的五个功能一般都由STM32官方在它们提供的官方库里的ASM文件(汇编启动文件)startup_stm32f10x_hd.s实现,因此在实际中只需要根据所用编译软件的不同选择对应的ASM文件,然后将之加入编译的工程中,再编写自己的main文件便可,而系统时钟已在ASM文件中通过汇编指令调用SystemInit()函数来初始化系统的时钟以及时钟源的选取(默认选择HSE,PLL的9倍频也就是72M)。

具体调用如下图:

stm32系统时钟初始化过程剖析

到此我们就知道了系统是如何选择时钟源以及配置系统时钟的了,具体我们来看看SystemInit()函数具体配置时钟的过程。

stm32系统时钟初始化过程剖析

SystemInit()函数有许多的条件编译我们主要来看看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
 
}

该函数通过宏来判断系统定义的是多高频率的宏来调用对应的函数设置系统时钟的。

stm32系统时钟初始化过程剖析

如图此时我们系统默认定义的是72M,用户可根据需要选择其他时钟。下面我们来看看主要的系统时钟配置的函数我么以72M为例:

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
 /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON); //使能HSE
 
  /* Wait till HSE is ready and if Time out is reached exit */
    //等待外部高速时钟就绪标志置位(时钟初始化需要等待其稳定)
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY; //保存CR寄存器中的外部高速时钟就绪标志位得值
    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) //HSE时钟已经准备就绪
  {
    /* Enable Prefetch Buffer */
    //使能FLSH相关寄存器
    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; //AHB分频器不分频使得system=HCLK时钟=默认的72M
                                               
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //APB2分频器不分频==默认的72M
    
    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //APB1分频器2分频==默认的72M/2=36M

#ifdef STM32F10X_CL
 。。。省略条件编译的内容 .......
#else    
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));            //先设置相应位为0在设置相应位为1
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);  //设置PLLMUL倍频器值为9倍频(置位)
#endif /* STM32F10X_CL */

    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;                  //使能PLL时钟

    /* 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)); //时钟的切换先全部清0将CFGR的bit0和bit1设置为0
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;            //将CFGR的bit0不变bit1设置为1从而选择system为PLL时钟提供

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) //等待稳定
    {
    }
  }
  else
  { 
   
  }
}

至此整个系统时钟就初始化完毕。。。。。

stm32系统时钟初始化过程剖析

附录:

 

 转载请标明原贴出处:https://blog.csdn.net/zj490044512
————————————————
版权声明:本文为CSDN博主「const_zj」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zj490044512/article/details/85163366