STM32學習 | RCC__系統時鍾篇


一、理解RCC與時鍾樹

 RCCReset and Clock Control ,意思是復位和時鍾控制器,它負責單片機的復位以及時鍾的配置。


1.復位

STM32F10xxx支持三種復位形式,分別為系統復位電源復位備份區域復位 

(1)系統復位

 當發生以下任一事件時,產生一個系統復位:
 1. NRST引腳上的低電平(外部復位)
 2.
窗口看門狗計數終止(WWDG復位)
 3.
獨立看門狗計數終止(IWDG復位)
 4.
軟件復位(SW復位)
 5.
低功耗管理復位 

除了時鍾控制器的RCC_CSR寄存器中的復位標志位和備份區域中的寄存器以外,系統
復位將復位所有寄存器至它們的復位狀態。
 

(2)電源復位

 當以下事件中之一發生時,產生電源復位:
 1. 上電/掉電復位(POR/PDR復位)
 2.
從待機模式中返回 

電源復位將復位除了備份區域外的所有寄存器。 

(3)備份域復位

 當以下事件中之一發生時,產生備份區域復位。
 1. 軟件復位,備份區域復位可由設置備份域控制寄存器 (RCC_BDCR)中的BDRST位產生。
 2. VDDVBAT兩者掉電的前提下, VDDVBAT上電將引發備份區域復位。 

 備份區域擁有兩個專門的復位,它們只影響備份區域 

2.時鍾

時鍾系統為硬件系統的各個模塊提供時鍾信號,就像人的脈搏心跳一樣不可或缺,而STM32的結構較為復雜,不同的硬件可能對時鍾信號有不同的要求,因此在系統中設置多個振盪器,分別提供時鍾信號,實際中經常從一個主振盪器開始,經過多次的倍頻、分頻、鎖相環等電路,生成每個模塊的時鍾信號。

下圖為單片機的時鍾樹,它描述了整個系統從振盪器到各個模塊的時鍾信號通路。

這個圖初看可能會覺得比較復雜,但只要抓住核心,也就容易了,其核心就是圖中標號4的部分,SYSCLK系統時鍾。

我們可以把時鍾樹以SYSCLK為界限分為左右兩部分,左邊是驅動SYSCLK所需的振盪器輸入、分頻、鎖相環倍頻等過程,而右邊是SYSCLK提供給總線以及各個外設的時鍾信號通路。

左邊:

   有三種不同的時鍾源可被用來驅動系統時鍾(SYSCLK)HSI振盪器時鍾、HSE振盪器時鍾 和 PLL時鍾。

   PLL時鍾是Phase Lock Loop時鍾即“鎖相環時鍾”的簡稱,它利用外部輸入的參考信號控制環路內部振盪信號的頻率和相位,故可以實現外部控制倍頻的效果。

   PLL鎖相環時鍾被三種時鍾源其中之一驅動,分別是HSI/2 、HSE 和 HSE/2。

右邊:

   由圖中5、6、7三個預分頻器控制HCLK、PCLK1和PCLK2的時鍾頻率,其中HCLK1最大只能達到36M,而HCLK2可以達到72M。

   由HLK、PCLK1、PCLK2這三個總線時鍾,經過分頻和倍頻產生其他外設需要的時鍾頻率。

其他:

   LSE、LSI用來產生RTC實時時鍾 和 IWDG獨立看門狗時鍾。

   由PLLCLK/2、HSI、HSE、SYSCLK這四個其中之一,可以向單片機外部輸出MCO時鍾信號,對應單片機PA8引腳。

 

二、探究系統的初始化時鍾配置函數


 

在啟動文件“startup_stm32f10x_hd.s”中,有這樣一段匯編代碼,當系統復位時執行,效果是初始化系統的時鍾,並運行__main這段代碼,最終執行我們寫的main函數。

; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

說一個題外話:

__main和main是完全兩個不同的函數,並且你無法找到__main代碼,因為這個是編譯器自動創建的。

查看MDK的文檔,會發現有這么一句說明:It is automatically created by the linker when it sees a definition of main(),意思是當編譯器發現定義了main函數,那么就會自動創建__main。

 

我們這里關注SystemInit這個函數,它初始化了系統的時鍾配置,對函數go to definition后發現該函數定義在“system_stm32f10x.c”中,其函數內容如下。

啟動文件中調用的的系統初始化函數

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemCoreClock variable.
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* 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;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

#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 
}

 

函數內部基本是對RCC寄存器的配置,前面大部分語句都是把RCC寄存器初始化成默認的復位狀態,最后又調用了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

 

函數內部判斷是否定義了SYSCLK_FREQ_72MHz,如果有定義過,則執行SetSysClockTo72();這個函數,而根據“system_stm32f10x.c”這一源文件的代碼,對stm32f10x的HD型設備,其默認定義了SYSCLK_FREQ_72MHz。

HD型設備默認定義
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000
#endif

 

由於默認定義,會執行SetSysClockTo72()函數,函數內部如下。

SetSysClockTo72()函數
#elif defined SYSCLK_FREQ_72MHz
/**
  * @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 */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    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)
  {
    /* Enable Prefetch Buffer */
    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;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

#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 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */

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

    /* 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));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  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 */
  }
}
#endif

函數總體流程為:

  1. 使能HSE  
  2. 等待HSE使能完畢
  3. 使能預取指
  4. 設置FLASH等待2個周期
  5. 配置HCLK
  6. 配置PCLK2
  7. 配置PCLK1
  8. 配置PLL鎖相環時鍾,包含兩個參數,分別是鎖相環的時鍾源和倍頻系數
  9. 使能PLL鎖相環
  10. 等待PLL鎖相環穩定
  11. 配置PLL鎖相環作為SYSCLK系統時鍾
  12. 等待PLL鎖相環成功配置為SYSCLK源

 

 

三、自己寫HSE配置系統時鍾函數


 

如果不使用系統自帶的RCC初始化函數,我們自己也可以根據庫函數仿照系統自帶的流程寫出一個配置函數,所需要的庫函數,在"stm32f10x_rcc.c"中有定義,在"stm32f10x_rcc.h"最下方有聲明,可以很方便地找到。

仿照系統自帶的配置流程,依據"stm32f10x_rcc.c"庫函數,寫出的HSE配置系統時鍾函數如下。

HSE配置系統時鍾函數
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
	ErrorStatus HSEStatus;
	//把RCC寄存器復位成復位值
	RCC_DeInit();
	//使能HSE
	RCC_HSEConfig(RCC_HSE_ON);
	//等待HSE准備完畢
	HSEStatus = RCC_WaitForHSEStartUp();
	if(HSEStatus == SUCCESS)
	{
		//使能預取指緩沖
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		
		//設置FLASH等待2個周期
		FLASH_SetLatency(FLASH_Latency_2);
		
		//設置HCLK,即AHB的時鍾頻率為不分頻
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		
		//設置PCLK2時鍾頻率為不分頻
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		//設置PCLK1時鍾頻率為2分頻
		RCC_PCLK1Config(RCC_HCLK_Div2);
		
		//設置PLL鎖相環時鍾為HSE*x, x為函數的入口參數。
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
		
		//使能PLL鎖相環
		RCC_PLLCmd(ENABLE);
		
		//等待PLL穩定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ;
		
		//配置PLL作為系統時鍾
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		//等待PLL成功設置為SysClk源
		while(RCC_GetSYSCLKSource() != 0x08) ;
		
	}
	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 */
	}
	
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM