1. 程式人生 > >STM32F2系列系統時鐘預設配置

STM32F2系列系統時鐘預設配置

新到一家公司後,有個專案要用到STM32F207Vx微控制器,找到網上的例子照貓畫虎的寫了幾個例子,比如ADC,可是到了ADC多通道轉換的時候就有點傻眼了,這裡面的時鐘跑的到底是多少M呢?微控制器外掛的時鐘是25M,由於該微控制器時鐘系統較為複雜,有內部高/低、外部高/低 、PLL鎖相環時鐘,又有AHB匯流排時鐘、APB1/2時鐘,而例子中很少講到系統時鐘的預設配置是怎麼配置呢?那麼就發點時間研究下這個微控制器內部的複雜時鐘系統吧。

下圖是STM32F2系列的時鐘樹結構圖:

1、內部高速時鐘HSI、外部高速時鐘HSE和PLL時鐘PLLCLK時鐘都接到了SW開關處,通過SW選擇哪一路作為SYSCLK,SYSCLK經過AHB分頻器進行分頻得到HCLK,APB1和APB2是掛在匯流排AHB上的,通過APB1和APB2分頻得出fpclk1和fpclk2。

2、PLL輸入時鐘源主要是靠外部高速時鐘和內部高速時鐘作為時鐘源,通過PLLCFGR暫存器的bit22來選擇具體哪一路作為時鐘源。選擇好了時鐘源進入/M分頻器,也就是PLLM進行分頻,送入VCO,在通過xN,進行倍頻,也就是PLLN:(1)通過/P進行分頻(PLLP)得到PLLCLK;(2)通過/Q分頻(PLLQ),得到PLL48CK。

然後邊看程式碼邊對照結構圖進行分析,看軟體如何給微控制器配置系統時鐘的。

然後找到啟動程式碼“startup_stmf32xx.s”,該程式碼是用匯編寫的,可以看到,在呼叫main函式之前,是先呼叫了SystemInit函式的,該函式是在“system_stm32f2xx.c”中

複製程式碼
; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
複製程式碼

程式碼如下,變數直接賦個16進位制的數,都不知道是啥意思,目的是幹什麼的,不知道,所以看下面程式碼時最好對照STM32F2x使用者手冊。當然這個只是一個初始化,待會主要看SetSysClock();這個函式,在呼叫該函式之前,我們知道微控制器是先啟用了內部高速時鐘等一些配置。

複製程式碼
void SystemInit(void)
{
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001; //RCC_CR復位值0x0000_xx83,內部高速時鐘使能,也就是說上電開始就使用內部高速時鐘,16MHZ

  /* Reset CFGR register */
  RCC->CFGR = 0x00000000;    //通過開關SW選擇內部高速時鐘作為系統時鐘16MHZ
                                                        //AHB prescaler 不分頻
                                                      //APB Low speed prescaler (APB1) 不分頻,fplck1 = 16MHZ
                                                        //APB high-speed prescaler (APB2)不分頻,fplck2 = 16MHZ
                              //MCO1和MCO2時鐘輸出等配置可參考使用者手冊Page95

  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset PLLCFGR register */
  RCC->PLLCFGR = 0x24003010; //RCC_CFGR復位值是0x2400_3010

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;  //對bit18 HSEBYP 設定為0,外部高速時鐘被禁止

  /* Disable all interrupts */
  RCC->CIR = 0x00000000;  //所有時鐘中斷都被禁止

#ifdef DATA_IN_ExtSRAM
  SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM */
         
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
     AHB/APBx prescalers and Flash settings ----------------------------------*/
  SetSysClock();

  /* Configure the Vector Table location add offset address ------------------*/
#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
}
複製程式碼

在SystemInit(void)函式中在配置完一些引數後,還呼叫了SetSysClock()函式,該函式程式碼如下

複製程式碼
static void SetSysClock(void)
{
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);  //外部高速時鐘使能,25MHZ
 
  /* Wait till HSE is ready and if Time out is reached exit */  //外部時鐘使能後,得需要一點時間到達各個埠
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY為0,說明外部時鐘還沒準備好,1說明外部時鐘已準備好
    StartUpCounter++;//對讀的次數進行累加,當累加次數到達1280次時,就意味著啟動時間超時
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)//這裡判斷上面的do while迴圈是因哪個條件結束的
  {
    HSEStatus = (uint32_t)0x01;   //說明時鐘已準備好了,才結束do whlie迴圈
  }
  else
  {
    HSEStatus = (uint32_t)0x00;  //說明是因為超時了而退出do while迴圈
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分頻,AHB出來後時鐘就是sysclk=120M
      
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分頻,fpclk2 = sysclk/2 = 60M
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分頻,fplck1 = sysclk/4 = 30M

    /* Configure the main PLL */ //主要對PLL和PPI2S 進行配置
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
//配置完後RCC->PLLCFGR = 0x05403c19,然後對照暫存器RCC_PLLCFGR檢視哪些位對應哪些功能
//通過bit5~bit0可計算出PLLM=25,那麼input VCO = PLL input clock /PLLM = 25M/25 = 1M,對應時鐘結構圖中的/M
//通過bit14~bit6可計算出倍頻因子PLLN = 240,那麼VCO output clock = PLLN * input VCO = 240 * 1 = 240M,對應時鐘結構圖中的xN
//bit17~bit16可計算出分頻因子PLLP = 2,那麼PLLCLK = VCO output clock /PLLP = 240/2 = 120M
//bit22是選擇給PLL輸入的時鐘源,輸入時鐘源有外部和內部高速時鐘,這裡選擇的是外部高速時鐘即PLL input clock = HSE =25M
//bit27~24可計算出分頻因子PLLQ = 5,那麼PLL48CK = VCO output clock/PLLQ = 240/5 = 48M
        
    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON; //使能PLL

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
        //到這裡RCC->CR已配置完,最終值0x03036783
        //通過檢視使用者手冊知道,內部高速時鐘、外部高速時鐘、PLL時鐘都已開啟
        
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS;

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;
        
        //到這裡RCC->CFGR已配置完,最終值是0x0000_940A
        //通過檢視使用者手冊,知道,PLL時鐘作為系統時鐘即120M
        //AHB不分頻,即HCLK = 120M
        //APB1 4分頻,即fpclk1 = 120/4=30M
        //APB2 2分頻,即fpclk2 = 120/2=60M

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }

}
複製程式碼

OK,分析完這段程式碼後,呼叫系統韌體函式後,現在知道了時鐘樹結構圖中右邊出來的時鐘是多少MHz了吧。

總結:

1、使用的是外部時鐘25MHZ,通過PLL進行分頻倍頻分頻得到PLLCLK 120M,PLLCLK作為系統時鐘SYSCLK。

2、APB1出來是30M,也就是FPCLK1。

3、APB2出來是60M,也就是FPCLK2。

轉自 http://www.cnblogs.com/wen2376/p/4468319.html