1. 程式人生 > 其它 >STM32-時鐘配置與使用

STM32-時鐘配置與使用

STM32中時鐘樹,系統與外設時鐘配置相關

0、前言

RCC-復位和時鐘控制器

可以實現配置系統時鐘SYSCLK,配置AHB(HCLK)匯流排時鐘,配置外設APB1(PCLK1)APB2(PCLK2)時鐘

庫函式的標準配置為PCLK2=HCLK=SYSCLK=PLLCLK=72M,PCLK1=HCLK/2=36M

系統初始化時會呼叫函式實現時鐘配置。

#ifdef SYSCLK_FREQ_HSE
  uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;     /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_24MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_24MHz;   /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_36MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_36MHz;   /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz;   /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz;   /*!< System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz
  uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;   /*!< System Clock Frequency (Core Clock) */
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

system_stm32f10x.c檔案中可更改巨集定義改變系統時鐘頻率

#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

1、時鐘樹

主要時鐘

  • HSE:高速外部時鐘,可由有源晶振或無源晶振提供,4-16MHz

    PLL以HSE為來源時可設定不分頻或2分頻

  • PLL:鎖相環時鐘源,可配置來自HSE或HSI/2

  • PLLCLK:鎖相環時鐘,可設定倍頻[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

  • SYSCLK:系統時鐘

  • HCLK:AHB匯流排時鐘,系統時鐘經AHB預分頻得到,分頻因子[1,2,4,8,16,64,128,256,512]

  • PCLK1:APB1匯流排時鐘,由HCLK通過低速APB1預分頻得到,分頻因子[1,2,4,8,16]

  • PCLK2:APB2匯流排時鐘,由HCLK通過高速APB2預分頻得到,分頻因子[1,2,4,8,16]

其他時鐘

  • USB時鐘:由PLLCLK通過USB預分頻器得到,分頻因子[1,1.5]
  • Cortex系統時鐘:由HCLK8分頻得到,用來驅動核心的系統定時器SysTick
  • ADC時鐘:由PCLK2經ADC預分頻得到,分頻因子[2,4,6,8]
  • RTC時鐘:由HSE/128或LSE或LSI得到
  • MCO時鐘:輸出時鐘,可由PLLCLK/2,HSI,HSE,SYSCLK配置

2、時鐘配置

相關庫函式

配置函式

/*
	將RCC外設初始化為復位狀態
*/
void RCC_DeInit(void);		
/*
	使能HSE,可選引數RCC_HSE_OFF,RCC_HSE_ON,RCC_HSE_Bypass 
*/
void RCC_HSEConfig(uint32_t RCC_HSE);	
/*
	等待時鐘源啟動穩定,返回SUCCESS,ERROR
*/
ErrorStatus RCC_WaitForHSEStartUp(void); 
/*
	配置PLL時鐘源和PLL倍頻因子
	RCC_RLLSource:RCC_PLLSource_HSE_Div1,RCC_PLLSource_HSE_Div2,RCC_PLLSource_HSI_Div2
	RCC_PLLMul:RCC_PLLMul_2 [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
*/
void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);
/*
	配置系統時鐘,可選引數RCC_SYSCLKSource_HSI,RCC_SYSCLKSource_HSE,RCC_SYSCLKSource_PLLCLK
*/
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
/*
	配置HCLK,可選引數RCC_SYSCLK_Div1 [1,2,4,8,16,64,128,256,512]
*/
void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
/*
	配置PCLK1,可選引數RCC_HCLK_Div1 [1,2,4,8,16]
*/
void RCC_PCLK1Config(uint32_t RCC_HCLK);
/*
	配置PCLK2,可選引數RCC_HCLK_Div1 [1,2,4,8,16]
*/
void RCC_PCLK2Config(uint32_t RCC_HCLK);

操作函式

/*
	控制PLL開關,可選引數DISABLE,ENABLE
*/
void RCC_PLLCmd(FunctionalState NewState);
/*
	獲取狀態,可選引數
    #define RCC_FLAG_HSIRDY                  ((uint8_t)0x21)
    #define RCC_FLAG_HSERDY                  ((uint8_t)0x31)
    #define RCC_FLAG_PLLRDY                  ((uint8_t)0x39)
    #define RCC_FLAG_LSERDY                  ((uint8_t)0x41)
    #define RCC_FLAG_LSIRDY                  ((uint8_t)0x61)
    #define RCC_FLAG_PINRST                  ((uint8_t)0x7A)
    #define RCC_FLAG_PORRST                  ((uint8_t)0x7B)
    #define RCC_FLAG_SFTRST                  ((uint8_t)0x7C)
    #define RCC_FLAG_IWDGRST                 ((uint8_t)0x7D)
    #define RCC_FLAG_WWDGRST                 ((uint8_t)0x7E)
    #define RCC_FLAG_LPWRRST                 ((uint8_t)0x7F)
    返回SET,RESET
*/
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
/*
	讀取時鐘切換狀態位,返回
    *     - 0x00: HSI used as system clock
    *     - 0x04: HSE used as system clock
    *     - 0x08: PLL used as system clock
*/
uint8_t RCC_GetSYSCLKSource(void);

使用HSE配置系統時鐘

  • 1、開啟HSE ,並等待 HSE 穩定
  • 2、設定 AHB、APB2、APB1的預分頻因子
  • 3、設定PLL的時鐘來源,和PLL的倍頻因子,設定各種頻率主要就是在這裡設定
  • 4、開啟PLL,並等待PLL穩定
  • 5、把PLLCK切換為系統時鐘SYSCLK
  • 6、讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
/* 設定 系統時鐘:SYSCLK, AHB匯流排時鐘:HCLK, APB2匯流排時鐘:PCLK2, APB1匯流排時鐘:PCLK1
 * PCLK2 = HCLK = SYSCLK
 * PCLK1 = HCLK/2,最高只能是36M
 * 引數說明:pllmul是PLL的倍頻因子,在呼叫的時候可以是:RCC_PLLMul_x , x:[2,3,...16]
 * 舉例:HSE_SetSysClock(RCC_PLLMul_9);  則設定系統時鐘為:8MHZ * 9 = 72MHZ
 *       HSE_SetSysClock(RCC_PLLMul_16); 則設定系統時鐘為:8MHZ * 16 = 128MHZ,超頻慎用
 *
 * HSE作為時鐘來源,經過PLL倍頻作為系統時鐘,這是通常的做法
 */
void HSE_SetSysClock(uint32_t pllmul)
{
	__IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;

	// 把RCC外設初始化成復位狀態,這句是必須的
  	RCC_DeInit();

     //使能HSE,開啟外部晶振,野火開發板用的是8M
     RCC_HSEConfig(RCC_HSE_ON);
     // 等待 HSE 啟動穩定
     HSEStartUpStatus = RCC_WaitForHSEStartUp();
     // 只有 HSE 穩定之後則繼續往下執行
      if (HSEStartUpStatus == SUCCESS)
      {
        //----------------------------------------------------------------------//
         // 使能FLASH 預存取緩衝區
         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
         // SYSCLK週期與快閃記憶體訪問時間的比例設定,這裡統一設定成2
         // 設定成2的時候,SYSCLK低於48M也可以工作,如果設定成0或者1的時候,
         // 如果配置的SYSCLK超出了範圍的話,則會進入硬體錯誤,程式就死了
         // 0:0 < SYSCLK <= 24M
         // 1:24< SYSCLK <= 48M
         // 2:48< SYSCLK <= 72M
         FLASH_SetLatency(FLASH_Latency_2);
        //----------------------------------------------------------------------//
         // AHB預分頻因子設定為1分頻,HCLK = SYSCLK 
         RCC_HCLKConfig(RCC_SYSCLK_Div1); 
         // APB2預分頻因子設定為1分頻,PCLK2 = HCLK
         RCC_PCLK2Config(RCC_HCLK_Div1); 
         // APB1預分頻因子設定為1分頻,PCLK1 = HCLK/2 
         RCC_PCLK1Config(RCC_HCLK_Div2);
        //-----------------設定各種頻率主要就是在這裡設定-------------------//
         // 設定PLL時鐘來源為HSE,設定PLL倍頻因子
         // PLLCLK = 8MHz * pllmul
         RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
        //------------------------------------------------------------------//
         // 開啟PLL 
         RCC_PLLCmd(ENABLE);
         // 等待 PLL穩定
         while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
         {}
         // 當PLL穩定之後,把PLL時鐘切換為系統時鐘SYSCLK
         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        // 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
         while (RCC_GetSYSCLKSource() != 0x08)
         {}
      }
      else
      { 
          // 如果HSE開啟失敗,那麼程式就會來到這裡,使用者可在這裡添加出錯的程式碼處理
          // 當HSE開啟失敗或者故障的時候,微控制器會自動把HSI設定為系統時鐘,
          // HSI是內部的高速時鐘,8MHZ
            while (1)
            {
            }
       }
}

使用HSI配置系統時鐘

  • 1、開啟HSI ,並等待 HSI 穩定
  • 2、設定 AHB、APB2、APB1的預分頻因子
  • 3、設定PLL的時鐘來源,和PLL的倍頻因子,設定各種頻率主要就是在這裡設定
  • 4、開啟PLL,並等待PLL穩定
  • 5、把PLLCK切換為系統時鐘SYSCLK
  • 6、讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
/* 設定 系統時鐘:SYSCLK, AHB匯流排時鐘:HCLK, APB2匯流排時鐘:PCLK2, APB1匯流排時鐘:PCLK1
 * PCLK2 = HCLK = SYSCLK
 * PCLK1 = HCLK/2,最高只能是36M
 * 引數說明:pllmul是PLL的倍頻因子,在呼叫的時候可以是:RCC_PLLMul_x , x:[2,3,...16]
 * 舉例:HSI_SetSysClock(RCC_PLLMul_9);  則設定系統時鐘為:4MHZ * 9 = 72MHZ
 *       HSI_SetSysClock(RCC_PLLMul_16); 則設定系統時鐘為:4MHZ * 16 = 64MHZ
 *
 * HSI作為時鐘來源,經過PLL倍頻作為系統時鐘,這是在HSE故障的時候才使用的方法
 * HSI會因為溫度等原因會有漂移,不穩定,一般不會用HSI作為時鐘來源,除非是迫不得已的情況
 * 如果HSI要作為PLL時鐘的來源的話,必須二分頻之後才可以,即HSI/2,而PLL倍頻因子最大隻能是16
 * 所以當使用HSI的時候,SYSCLK最大隻能是4M*16=64M
 */
void HSI_SetSysClock(uint32_t pllmul)
{
    __IO uint32_t HSIStartUpStatus = 0;
    // 把RCC外設初始化成復位狀態,這句是必須的
    RCC_DeInit();

    //使能HSI
    RCC_HSICmd(ENABLE);
    // 等待 HSI 就緒
    HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
    // 只有 HSI就緒之後則繼續往下執行
    if (HSIStartUpStatus == RCC_CR_HSIRDY)
    {
//----------------------------------------------------------------------//
        // 使能FLASH 預存取緩衝區
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
        // SYSCLK週期與快閃記憶體訪問時間的比例設定,這裡統一設定成2
        // 設定成2的時候,SYSCLK低於48M也可以工作,如果設定成0或者1的時候,
        // 如果配置的SYSCLK超出了範圍的話,則會進入硬體錯誤,程式就死了
        // 0:0 < SYSCLK <= 24M
        // 1:24< SYSCLK <= 48M
        // 2:48< SYSCLK <= 72M
        FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//
        // AHB預分頻因子設定為1分頻,HCLK = SYSCLK
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        // APB2預分頻因子設定為1分頻,PCLK2 = HCLK
        RCC_PCLK2Config(RCC_HCLK_Div1);
        // APB1預分頻因子設定為1分頻,PCLK1 = HCLK/2
        RCC_PCLK1Config(RCC_HCLK_Div2);
//-----------------設定各種頻率主要就是在這裡設定-------------------//
        // 設定PLL時鐘來源為HSE,設定PLL倍頻因子
        // PLLCLK = 4MHz * pllmul
        RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//
        // 開啟PLL
        RCC_PLLCmd(ENABLE);
        // 等待 PLL穩定
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
        // 當PLL穩定之後,把PLL時鐘切換為系統時鐘SYSCLK
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        // 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    {
        // 如果HSI開啟失敗,那麼程式就會來到這裡,使用者可在這裡添加出錯的程式碼處理
        // 當HSE開啟失敗或者故障的時候,微控制器會自動把HSI設定為系統時鐘,
        // HSI是內部的高速時鐘,8MHZ
        while (1)
        {
        }
    }
}

MCO輸出

MCO GPIO初始化

/*
 * 初始化MCO引腳PA8
 * 在F1系列中MCO引腳只有一個,即PA8,在F4系列中,MCO引腳會有兩個
 */
void MCO_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    // 開啟GPIOA的時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    // 選擇GPIO8引腳
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    //設定為複用功能推輓輸出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    //設定IO的翻轉速率為50M
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    // 初始化GPIOA8
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

輸出

// MCO 引腳初始化
MCO_GPIO_Config();
// 設定MCO引腳輸出時鐘,用示波器即可在PA8測量到輸出的時鐘訊號,
// 我們可以把PLLCLK/2作為MCO引腳的時鐘來檢測系統時鐘是否配置準確
// MCO引腳輸出可以是HSE,HSI,PLLCLK/2,SYSCLK
//RCC_MCOConfig(RCC_MCO_HSE);
//RCC_MCOConfig(RCC_MCO_HSI);
//RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);