欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stm32时钟初始化分析与systick跳动时间分析

程序员文章站 2022-06-09 10:50:42
...

stm32时钟初始化分析

要想了解stm32时钟配置,我们首先当了解其时钟树,以下是一张经典的stm32系统频率为72M的时钟树图stm32时钟初始化分析与systick跳动时间分析

在系统启动时会首先调用SystemInit()函数,并在其中进行系统的一系列初始化,SystemInit()函数相关代码如下

void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  //置位HSION(Internal high-speed clock enable),开启内部8M高速时钟(HSI)
  RCC->CR |= (uint32_t)0x00000001;                                              

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
   //将SW(System clock switch)系统时钟切换为HSI,且将MCO(Microcontroller clock output)微控制器时钟输出置为没有时钟输出,
   //将HPRE, PPRE1, PPRE2, ADCPRE(AHB PRESCALER,APB1 PRESCALER,APB2 PRESCALER, ADC PRESCALER)设置为0(不分频) 
  RCC->CFGR &= (uint32_t)0xF8FF0000;                                           
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;                                            
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  //关闭HSE,时钟安全系统(CSSON:Clock security system enable,如果外部4-16MHz振荡器就绪,时钟监测器开启),关闭PLL(重要)
  RCC->CR &= (uint32_t)0xFEF6FFFF;                                             

  /* Reset HSEBYP bit */
  //关闭HSE旁路模式(旁路模式:外部时钟信号(50%占空比的方波、正弦波或三角波)连到OSC_IN引脚,此时OSC_OUT为高阻态),有源晶振(加电就能输出,贵!!!)适用该模式。)
  //然而大部分情况使用无源晶振,此处关了也没见再打开了
  RCC->CR &= (uint32_t)0xFFFBFFFF;                                              

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */  
  //初始化PLLSRC(PLL entry clock source)为0(HSI振荡器时钟经2分频后作为PLL输入时钟)
  //PLLXTPRE (HSE divider for PLL entry HSE输入分频)为0(HSE不分频)
  //PLLMUL(PLL multiplication factor,PLL输出倍频系数)为0000(PLL 2倍频输出),在初始化这三项前必须保证PLL关闭(此处PLL关闭了,系统时钟为HSI,所以初始化后PLL也没有工作)
  //初始化USB分频系数
  RCC->CFGR &= (uint32_t)0xFF80FFFF;                                            

#ifndef STM32F10X_CL
  /* Disable all interrupts and clear pending bits  */
  //该寄存器为与CSS,PLL,HSE,HSI,LSE,LSI相关中断,作用依次为清除对应中断标志位,使能中断和中断标志位状态(不打开中断当事件发生时interrupt flag会置1吗?)
  RCC->CIR = 0x009F0000;                                                        
#else
  /* 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;
#endif /* STM32F10X_CL */
    
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();//开始配置系统时钟

}

在系统初始化结尾处会调用SetSysClock()函数配置系统时钟,其内容如下

/**
  * @brief  Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
  * @param  None
  * @retval None
  */
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) */ 
}
通过预定义宏来决定系统主频,我们这里是72M,所以需要定义SYSCLK_FREQ_72MHz

接下来将会调用SetSysClockTo72();函数,其内容如下

/**
  * @brief  Sets System clock frequency to 72MHz and configure HCLK, PCLK2 
  *          and PCLK1 prescalers. 
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */ 
  //打开HSE   
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);                                          
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    //取出RCC->CR中RCC_CR_HSERDY位
    HSEStatus = RCC->CR & RCC_CR_HSERDY;                                        
    StartUpCounter++;  
    //该位由硬件置’1’代表HSE就绪,此处判断HSE是否启动成功或超时(HSEStartUp_TimeOut = 1280)
  } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut));          
  //如果HSE启动成功
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)                                       
  {
    //记录HSE启动状态为成功
    HSEStatus = (uint32_t)0x01;                                                 
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  
  //如果HSE启动成功
  if (HSEStatus == (uint32_t)0x01)                                             
  {
    /* Enable Prefetch Buffer */
    //打开指令预取器
    FLASH->ACR |= FLASH_ACR_PRFTBE;                                             

    /* Flash 2 wait state
        0等待周期,当 0 < SYSCLK < 24MHz
        1等待周期,当 24MHz < SYSCLK ≤ 48MHz
        2等待周期,当 48MHz < SYSCLK ≤ 72MHz(此处设置为2等待周期)
    */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
              
 
    /* HCLK = SYSCLK */
    //set AHB Prescaler(分频后得到HCLK) = SYSCLK/1
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    //set AHB2 Prescaler = HCLK/1
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;                                 
    
    /* PCLK1 = HCLK/2 */
    //set AHB1 Prescaler = HCLK/2
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 
    //如果是从systemInit进入则此时时钟为HSI = SYSCLK = 8M
                                                                               
#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 */
    /*初始化PLLSRC(PLL entry clock source)为0(HSI振荡器时钟经2分频后作为PLL输入时钟)
       PLLXTPRE (HSE divider for PLL entry HSE输入分频)为0(HSE不分频)
       PLLMUL(PLL multiplication factor,PLL输出倍频系数)为0000(PLL 2倍频输出
       在初始化这三项前必须保证PLL关闭*/
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));    
    //设置PLLSRC为HSE,PLL倍频系数为9。
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);           
#endif /* STM32F10X_CL */

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

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /* Select PLL as system clock source */
    //初始化sw(System clock switch)位(必要,因为此函数不一定从systemInit进入,导致此位未必被初始化)
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));   
    //将系统时钟切换为PLL(HSE * PLLMULL9 = 8M * 9 = 72M)
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;                                     

    /* Wait till PLL is used as system clock source */
    //通过SWS位 (System clock switch status,硬件置位)判断时钟源切换是否成功
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)              
    {
    }
    //至此设置成功程序结束
  }
  else                                                                          //HSE启动失败,程序走死,emmm,:(
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */    

    /* Go to infinite loop */
    while (1)
    {
    }
  }
}

对于Systick定时器,我们主要关心四个寄存器
stm32时钟初始化分析与systick跳动时间分析
stm32时钟初始化分析与systick跳动时间分析
其中SysTick的CTRL寄存器(控制及状态寄存器)中的CLKSOURCE位是用于选择 SysTick 定时器时钟来源,如果该位为 1, 表示其时钟由HCLK直接提供即 72M(FCLK)。如果为 0, 表示其时钟由HCLK8分频后提供即 72/8=9M(STCLK),由时钟树图可知我们这里设置的时钟源为72M。即每秒跳动72M次,计数一次时间为1/72 us。
配置代码如下

/** \brief  System Tick Configuration

    The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
    Counter is in free running mode to generate periodic interrupts.

    \param [in]  ticks  Number of ticks between two interrupts.

    \return          0  Function succeeded.
    \return          1  Function failed.

    \note     When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
    function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
    must contain a vendor-specific implementation of this function.

 */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible 即ticks > 26位*/    

  SysTick->LOAD  = ticks - 1;                                  /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |                 //内核时钟fclk
                   SysTick_CTRL_TICKINT_Msk   |                 //数到0时产生异常请求(tick interrupt)
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */    //打开tick
  return (0);                                                  /* Function successful */
}