本篇,用自問自答,記錄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;
}