stm32F429启动时钟配置

STM32f429在启动时会在startup_stm32f429_439xx.s中调用static void SetSysClock(void)函数。默认使用的是25M晶振,把系统时钟设置为180M.

在system_stm32f4xx.c中给出了相关的默认时钟参数设置。static void SetSysClock(void)函数执行的就是这个参数设置的过程。

*=============================================================================
  *                    Supported STM32F42xxx/43xxx devices
  *-----------------------------------------------------------------------------
  *        System Clock source                    | PLL (HSE)
  *-----------------------------------------------------------------------------
  *        SYSCLK(Hz)                             | 180000000
  *-----------------------------------------------------------------------------
  *        HCLK(Hz)                               | 180000000
  *-----------------------------------------------------------------------------
  *        AHB Prescaler                          | 1
  *-----------------------------------------------------------------------------
  *        APB1 Prescaler                         | 4
  *-----------------------------------------------------------------------------
  *        APB2 Prescaler                         | 2
  *-----------------------------------------------------------------------------
  *        HSE Frequency(Hz)                      | 25000000
  *-----------------------------------------------------------------------------
  *        PLL_M                                  | 25
  *-----------------------------------------------------------------------------
  *        PLL_N                                  | 360
  *-----------------------------------------------------------------------------
  *        PLL_P                                  | 2
  *-----------------------------------------------------------------------------
  *        PLL_Q                                  | 7
  *-----------------------------------------------------------------------------
  *        PLLI2S_N                               | NA
  *-----------------------------------------------------------------------------
  *        PLLI2S_R                               | NA
  *-----------------------------------------------------------------------------
  *        I2S input clock                        | NA
  *-----------------------------------------------------------------------------
  *        VDD(V)                                 | 3.3
  *-----------------------------------------------------------------------------
  *        Main regulator output voltage          | Scale1 mode
  *-----------------------------------------------------------------------------
  *        Flash Latency(WS)                      | 5
  *-----------------------------------------------------------------------------
  *        Prefetch Buffer                        | ON
  *-----------------------------------------------------------------------------
  *        Instruction cache                      | ON
  *-----------------------------------------------------------------------------
  *        Data cache                             | ON
  *-----------------------------------------------------------------------------
  *        Require 48MHz for USB OTG FS,          | Disabled
  *        SDIO and RNG clock                     |
  *-----------------------------------------------------------------------------
  *=============================================================================

假设外部晶振改成了其他的数值比如说8M ,要修改  "stm32f4xx.h " 文件中的 "HSE_VALUE" 宏定义。

如果程序在运行过程中动态修改了PLL倍频系数,或者切换了时钟源,请务必执行一次 SystemCoreClockUpdate()函数,这个函数会自动根据PLL倍频参数计算出实际的主频。

void SystemCoreClockUpdate(void)函数会检测 RCC clock configurat ion register (RCC_CFGR)寄存器的 Bits 3:2 这两位是只读的,由硬件清除和设置,不能软件写入。

Bits 3:2 SWS: System clock switch status
Set and cleared by hardware to indicate which clock source is used as the system clock.
00: HSI oscillator used as the system clock
01: HSE oscillator used as the system clock
10: PLL used as the system clock
11: not applicable

根据这两位通过置swtich语句分析设置源的选择,进一步进行设置,如果是用PLL还要读取RCC_PLLCFGR寄存器的值

获取P N M PLLSRC等参数,来更新系统时钟。系统时钟SYSCLK 有下列公式决定。

PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
SYSCLK = PLL_VCO / PLL_P

最后获得HCLK frequency :

tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
SystemCoreClock >>= tmp;

void SystemCoreClockUpdate(void)
{
  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
#if defined(STM32F446xx)  
  uint32_t pllr = 2;
#endif /* STM32F446xx */
  /* Get SYSCLK source -------------------------------------------------------*/
  tmp = RCC->CFGR & RCC_CFGR_SWS;

  switch (tmp)
  {
    case 0x00:  /* HSI used as system clock source */
      SystemCoreClock = HSI_VALUE;
      break;
    case 0x04:  /* HSE used as system clock source */
      SystemCoreClock = HSE_VALUE;
      break;
    case 0x08:  /* PLL P used as system clock source */
       /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
         SYSCLK = PLL_VCO / PLL_P
         */    
      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
      
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F446xx) || defined(STM32F469_479xx)
      if (pllsource != 0)
      {
        /* HSE used as PLL clock source */
        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }
      else
      {
        /* HSI used as PLL clock source */
        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }
#elif defined(STM32F410xx) || defined(STM32F411xE)
#if defined(USE_HSE_BYPASS)
      if (pllsource != 0)
      {
        /* HSE used as PLL clock source */
        pllvco = (HSE_BYPASS_INPUT_FREQUENCY / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }  
#else  
      if (pllsource == 0)
      {
        /* HSI used as PLL clock source */
        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }  
#endif /* USE_HSE_BYPASS */  
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F446xx || STM32F469_479xx */  
      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
      SystemCoreClock = pllvco/pllp;      
      break;
#if defined(STM32F446xx)      
      case 0x0C:  /* PLL R used as system clock source */
       /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
         SYSCLK = PLL_VCO / PLL_R
         */    
      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
      if (pllsource != 0)
      {
        /* HSE used as PLL clock source */
        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }
      else
      {
        /* HSI used as PLL clock source */
        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);      
      }
 
      pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >>28) + 1 ) *2;
      SystemCoreClock = pllvco/pllr;      
      break;
#endif /* STM32F446xx */
    default:
      SystemCoreClock = HSI_VALUE;
      break;
  }
  /* Compute HCLK frequency --------------------------------------------------*/
  /* Get HCLK prescaler */
  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
  /* HCLK frequency */
  SystemCoreClock >>= tmp;
}

另外也可以自己实现两个设置时钟的函数:

/*
 * 使用HSE时,设置系统时钟的步骤
 * 1、开启HSE ,并等待 HSE 稳定
 * 2、设置 AHB、APB2、APB1的预分频因子
 * 3、设置PLL的时钟来源
 *    设置VCO输入时钟 分频因子        m
 *    设置VCO输出时钟 倍频因子        n
 *    设置PLLCLK时钟分频因子          p
 *    设置OTG FS,SDIO,RNG时钟分频因子 q
 * 4、开启PLL,并等待PLL稳定
 * 5、把PLLCK切换为系统时钟SYSCLK
 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
 */

/*
 * m: VCO输入时钟 分频因子,取值2~63
 * n: VCO输出时钟 倍频因子,取值192~432
 * p: PLLCLK时钟分频因子  ,取值2,4,6,8
 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
 * 函数调用举例,使用HSE设置时钟
 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
 * HSE_SetSysClock(25, 360, 2, 7);
 * HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法
 
 * 系统时钟超频到216M爽一下
 * HSE_SetSysClock(25, 432, 2, 9);
 */
void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)    
{
  __IO uint32_t HSEStartUpStatus = 0;
  
  // 使能HSE,开启外部晶振,秉火F429使用 HSE=25M
  RCC_HSEConfig(RCC_HSE_ON);
    
  // 等待HSE启动稳定
   HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if (HSEStartUpStatus == SUCCESS)
  {
    // 调压器电压输出级别配置为1,以便在器件为最大频率
        // 工作时使性能和功耗实现平衡
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;
        
        // HCLK = SYSCLK / 1
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
        
     // PCLK2 = HCLK / 2
    RCC_PCLK2Config(RCC_HCLK_Div2);
        
        // PCLK1 = HCLK / 4
    RCC_PCLK1Config(RCC_HCLK_Div4);
        
    // 如果要超频就得在这里下手啦
        // 设置PLL来源时钟,设置VCO分频因子m,设置VCO倍频因子n,
        //  设置系统时钟分频因子p,设置OTG FS,SDIO,RNG分频因子q
    RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
        
        // 使能PLL
    RCC_PLLCmd(ENABLE);
  
      // 等待 PLL稳定
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }   

/*-----------------------------------------------------*/
    //开启 OVER-RIDE模式,以能达到更高频率
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
    }      
    // 配置FLASH预取指,指令缓存,数据缓存和等待状态
    FLASH->ACR = FLASH_ACR_PRFTEN 
                    | FLASH_ACR_ICEN 
                    | FLASH_ACR_DCEN 
                    | FLASH_ACR_LATENCY_5WS;
/*-----------------------------------------------------*/
        
        // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
  else
  { // HSE启动出错处理

    while (1)
    {
    }
  }
}

/*
 * 使用HSI时,设置系统时钟的步骤
 * 1、开启HSI ,并等待 HSI 稳定
 * 2、设置 AHB、APB2、APB1的预分频因子
 * 3、设置PLL的时钟来源
 *    设置VCO输入时钟 分频因子        m
 *    设置VCO输出时钟 倍频因子        n
 *    设置SYSCLK时钟分频因子          p
 *    设置OTG FS,SDIO,RNG时钟分频因子 q
 * 4、开启PLL,并等待PLL稳定
 * 5、把PLLCK切换为系统时钟SYSCLK
 * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
 */

/*
 * m: VCO输入时钟 分频因子,取值2~63
 * n: VCO输出时钟 倍频因子,取值192~432
 * p: PLLCLK时钟分频因子  ,取值2,4,6,8
 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15
 * 函数调用举例,使用HSI设置时钟
 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
 * HSI_SetSysClock(16, 360, 2, 7);
 * HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法
 
 * 系统时钟超频到216M爽一下
 * HSI_SetSysClock(16, 432, 2, 9);
 */

void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)    
{
  __IO uint32_t HSIStartUpStatus = 0;
    
    // 把RCC外设初始化成复位状态
  RCC_DeInit();
  
  //使能HSI, HSI=16M
    RCC_HSICmd(ENABLE);
    
  // 等待 HSI 就绪
    HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
    
    // 只有 HSI就绪之后则继续往下执行
  if (HSIStartUpStatus == RCC_CR_HSIRDY)
  {
    // 调压器电压输出级别配置为1,以便在器件为最大频率
    // 工作时使性能和功耗实现平衡
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;
        
        // HCLK = SYSCLK / 1
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        
        // PCLK2 = HCLK / 2
        RCC_PCLK2Config(RCC_HCLK_Div2);
        
        // PCLK1 = HCLK / 4
        RCC_PCLK1Config(RCC_HCLK_Div4);
        
    // 如果要超频就得在这里下手啦
        // 设置PLL来源时钟,设置VCO分频因子m,设置VCO倍频因子n,
        //  设置系统时钟分频因子p,设置OTG FS,SDIO,RNG分频因子q
        RCC_PLLConfig(RCC_PLLSource_HSI, m, n, p, q);
        
        // 使能PLL
        RCC_PLLCmd(ENABLE);
  
      // 等待 PLL稳定
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }   

/*-----------------------------------------------------*/
    //开启 OVER-RIDE模式,以能达到更高频率
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
    }      
    // 配置FLASH预取指,指令缓存,数据缓存和等待状态
    FLASH->ACR = FLASH_ACR_PRFTEN 
                    | FLASH_ACR_ICEN 
                    |FLASH_ACR_DCEN 
                    |FLASH_ACR_LATENCY_5WS;
/*-----------------------------------------------------*/
        
        // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
  else
  { // HSI启动出错处理
    while (1)
    {
    }
  }
}

猜你喜欢

转载自www.cnblogs.com/passion-love/p/9240597.html