1. 程式人生 > >stm32系統時鐘初始化過程剖析

stm32系統時鐘初始化過程剖析

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)。

具體呼叫如下圖:

到此我們就知道了系統是如何選擇時鐘源以及配置系統時鐘的了,具體我們來看看SystemInit()函式具體配置時鐘的過程。

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
 
}

該函式通過巨集來判斷系統定義的是多高頻率的巨集來呼叫對應的函式設定系統時鐘的。

如圖此時我們系統預設定義的是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
  { 
   
  }
}

至此整個系統時鐘就初始化完畢。。。。。

附錄:

 轉載請標明原貼出處:https://blog.csdn.net/zj490044512