天天看點

STM32開發---F103系統時鐘配置

本篇,用自問自答,記錄STM32系統時鐘配置一些關節點。

因為已重寫了系統時鐘配置的代碼,不再細研配置的過程、步驟,忘了就翻代碼就好,且網上不缺絮絮叨叨的各種教程。

一句話總結:  過程高深, 使用簡單, 調用SystemInit()函數搞掂.

詳細代碼、解釋已上傳:system_stm32f10x_c詳細解釋.zip,  同時上傳到Q群檔案夾:262901124 

1:什麼是系統時鐘?

  • 系統時鐘是代碼運作的:節拍依據,如每一個時鐘脈沖執行一個彙編操作指令。時鐘頻率越高,代碼執行速度越快。
  • 舉例:一個舞蹈班,由一老師帶領幾十個學生排舞。老師不停打節拍的雙手如同系統時鐘的脈沖,每打一個拍子,學生們就進行一個動作;  拍子越快,學生們的動作就越快,拍子慢了,學生們的動作也慢下來。

2:為什麼需要不同的時鐘頻率?

  • 不同的裝置,因為技術或工藝,要求不同的運作頻率,有的裝置運作頻率很高,而有的則比較慢;
  • 舉例:無線路由器,一個裝置中,就有各種不同的運作頻率。其中的wifi信号2.4G,簡單地了解為1秒鐘脈沖2400000000次,即每秒向空中發射2400M個電波信号;而路由器中的資料存儲晶片W25Q128,它的資料傳輸速度大約50000000Hz,每秒傳輸50M個電平信号.

3:如何進行系統時鐘配置?

  • 一般是在啟動檔案中調用晶片商編寫好的系統時鐘配置函數,當然也可以使用自己編寫的配置函數,但沒必要。
  • 如STM32F103C8, 在啟動檔案startup_stm32f103_md.s的第133行,調用系統時鐘配置函數SystemInit(),  把外部晶振(HSE)的8MHz進行9倍頻, 最終得到72MHz系統運作頻率 ;  函數中還設定了高、低速總線的分頻系數,使得APB2(高速總線)=HCLK/1=72MHz/1=72MHz,   APB1(低速總線)=HCLK/2=72MHz/2=36MHz
  • 系統時鐘配置函數SystemInit(),編寫在system_stm32f10x.c檔案中,如果我們要自己編寫這個函數,也可以儲存在能被調用到的任何位置。

4:系統時鐘的配置過程?

  • 主要是操作晶片中的寄存器值而改變時鐘的運作和參數。
  • 詳細的配置将在文章最後附代碼、解釋,其中代碼可完全替換工程的system_stm32f10x.c檔案内容,已在STM32F103C8中測試運作。
  • 調用的大概過程 : 闆子上電 > 晶片運作啟動檔案 > 啟動檔案中調用時鐘配置函數 > 進入main()......

5:  必須設定成晶片要求的頻率吧?

  • 沒規定哪個晶片一定要用某個頻率, 都是約定俗成的, 如STM32F103就是72MHz,  而使用8MHz晶振7倍頻的56MHz也可以正常運作,  但要注意和其它裝置通信時協調.  使用約定俗成的72MHz, 代碼可以更友善地移植.
  • 可以度娘不同的晶片時鐘頻率要求, F103通常是72MHz, F429通常是180MHz.....

6: 代碼解釋

system_stm32f10x.c原檔案1000多行,重寫了一份,120多行,已運作在STM32F103C8T6開發闆上。

/******************************************************************************
  * @file    system_stm32f10x.c
  * @author  魔女開發闆團隊
  * @version 
  * @date    2020年4月21日
  * @note    原來的檔案又長又啰嗦,看不過眼, 重寫了
******************************************************************************/
#include "stm32f10x.h"



// 中斷向量表偏移
#define VECT_TAB_OFFSET    0x0                                                       // 不能修改! 除非你已很熟悉STM32的啟動和中斷
// 分頻預設數值對比
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};    // 不能修改! 用于檢測系統時鐘頻率
// 系統時鐘頻率
uint32_t SystemCoreClock = 5201314;                                                  // 可先設随意數值,以便檢驗啟動後時鐘是否成功設定成: 72'000'000Hz



/*****************************************************************************
*函  數: system_ClockInit
*功  能: 系統時鐘配置
*參  數: 無
*傳回值: 無
*備  注: 注釋_魔女開發闆 
*****************************************************************************/
void SystemInit(void)
{
    // 複位時鐘至預設狀态
    RCC->CR   |= (u32)0x00000001;                                // 開啟内部時鐘
    RCC->CFGR &= (u32)0xF8FF0000;                                // 複位 SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits
    RCC->CFGR &= (u32)0xFF80FFFF;                                // 複位 PLL和分頻的配置
    RCC->CIR   = 0x009F0000;                                     // 關閉中斷,清理中斷位

    // 配置時鐘 HCLK, PCLK, PCLK1, PCLK2, FLASH 
    __IO u32 StartUpCounter = 0, HSEStatus = 0;      
    
    RCC->CR |= ((u32)RCC_CR_HSEON);                              // 使能 HSE  
    do{                                                          // 等待HSE就緒
        HSEStatus = RCC->CR & RCC_CR_HSERDY;
        StartUpCounter++;  
      } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));   // 0x0500

    if ((RCC->CR & RCC_CR_HSERDY) != RESET)  {  
        FLASH->ACR |= FLASH_ACR_PRFTBE;                          // Enable Prefetch Buffer */    
        FLASH->ACR &= (u32)((u32)~FLASH_ACR_LATENCY);            // Flash 2 wait state */
        FLASH->ACR |= (u32)FLASH_ACR_LATENCY_2;    
 
        RCC->CFGR  |= (u32)RCC_CFGR_HPRE_DIV1;                   // [7:4]   AHB  預分頻, HCLK = SYSCLK/1  不分頻     
        RCC->CFGR  |= (u32)RCC_CFGR_PPRE2_DIV1;                  // [13:11] APB2 預分頻, APB2 = HCLK/1,   不分頻
        RCC->CFGR  |= (u32)RCC_CFGR_PPRE1_DIV2;                  // [10: 8] APB1 預分頻, APB1 = HCLK/2,    2分頻

        RCC->CFGR  &= (u32)(~( 1<<16 | 0x01<<17 | 0xF<<18));     // 清零
        RCC->CFGR  |= (u32)(0x01<<16 | 0x07<<18);                // PLL 時鐘源,低頻因子,倍頻系數, 使PLLCK= HSE * 9= 72MHz   
        RCC->CR    |= (u32)(0x01<<24);                           // 使能 PLL    
        while((RCC->CR & RCC_CR_PLLRDY) == 0) {  }               // 等待PLL就緒
    
        RCC->CFGR &= (u32)((u32)~(0x3<<0));                      // 清0
        RCC->CFGR |= (u32)(0x1 << 1);                            // 切換系統時鐘源為:PLLCLOCK    
        while ((RCC->CFGR & (u32)RCC_CFGR_SWS) != (u32)0x08) { } // 等待系統時鐘切換完成
    }
    else
    { 
        // 重要!!!
        // 時鐘初始化失敗 
        // 處理位置
    }    
    // 重定向中斷向量表位置
    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
}



/*****************************************************************************
*函  數: SystemCoreClockUpdate
*功  能: 更新實際時鐘頻率,儲存在:SystemCoreClock
*參  數:
*傳回值:
*備  注: 注釋_魔女開發闆
*****************************************************************************/
void SystemCoreClockUpdate(void)
{
    u32 tmp = 0, pllmull = 0, pllsource = 0;    

    tmp = RCC->CFGR & RCC_CFGR_SWS;                                 // 擷取時鐘源

    switch (tmp)  {
        case 0x00:                                                  // HSI 内部高速晶振 被選為系統時鐘源
            SystemCoreClock = HSI_VALUE;
            break;
    
        case 0x04:                                                  // HSE 外部高速晶振 被選為系統時鐘源
            SystemCoreClock = HSE_VALUE;
            break;
    
        case 0x08:                                                  // PLL 被先為系統時鐘源     
            pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;                 // PLL 時鐘源及倍頻系數
            pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;       
            pllmull = ( pllmull >> 18) + 2;      
            if (pllsource == 0x00)
            {        
                SystemCoreClock = (HSI_VALUE >> 1) * pllmull;       // HSI振蕩器時鐘2分頻作為PLL時鐘輸入
            }
            else
            {                                                       // HSE作為PLL時鐘輸入                              
                if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (u32)RESET)
                {
                    SystemCoreClock = (HSE_VALUE >> 1) * pllmull;   // HSE 2分頻
                }
                else
                {
                    SystemCoreClock = HSE_VALUE * pllmull;
                }
            }
            break;

        default:
            SystemCoreClock = HSI_VALUE;
            break;
    } 
    tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];  
    SystemCoreClock >>= tmp;    
}

           

繼續閱讀