STM32F103C8T6的系統時鐘配置成72MHZ
1. 什么是 STM32 系統時鐘
系統時鐘(System Clock)是整個 MCU(微控制器)運行的“節拍信號”,所有 CPU 指令執行、外設操作、定時器計時、總線數據傳輸等,都依賴這個時鐘頻率來同步。
在 STM32F103C8T6 中,系統時鐘的來源可以是:
HSI(內部高速時鐘,8 MHz)
HSE(外部高速晶振,一般 8 MHz 或 12 MHz)
PLL(鎖相環,可將輸入時鐘倍頻)
系統時鐘頻率越高,MCU 執行指令的速度越快,但功耗也會隨之增加。
2. 為什么要配置系統時鐘
保證程序運行速度
不同應用對速度要求不同,如果時鐘太低,運算、通訊可能會卡頓甚至超時;時鐘過高又會增加功耗和發熱。匹配外設波特率和定時精度
例如串口、I2C、SPI、PWM 等外設的時序計算都基于系統時鐘,如果系統時鐘不正確,波特率或頻率就會不準確,導致通信失敗。充分利用 MCU 性能
STM32F103 系列的最大主頻是 72 MHz,如果只用默認的 8 MHz HSI,CPU 性能會浪費很多。降低功耗或發熱(可按需降頻)
有些低功耗場景,會在運行中動態調低系統時鐘,比如從 72 MHz 切到 36 MHz。
3. 為什么常配成 72 MHz
硬件支持的最大值:STM32F103C8T6 的數據手冊寫明,最大系統主頻是 72 MHz,高于這個值會不穩定甚至損壞芯片。
外設定時方便:72 MHz 這個值和常用外設分頻系數匹配好,比如:
72 MHz / 36 / 18 / 9 / 8 / 4 等能整除,方便得到標準串口波特率和 PWM 頻率
72 MHz / 72 = 1 MHz,定時器計數方便
性能最優:72 MHz 時,ARM Cortex-M3 的執行速度最快,適合要求高實時性或計算量大的任務。
HAL庫
sys,c:
#include "sys.h"/*** @brief 配置 STM32F103C8T6 的系統時鐘為 72 MHz(或按傳入倍頻數計算)* @param plln: PLL 倍頻系數(RCC_PLL_MULx 枚舉),典型使用 RCC_PLL_MUL9 對 8MHz HSE 倍頻至 72MHz* @note 調用時機:必須在 HAL_Init() 之后調用(HAL_Init 會配置 SysTick 等基礎時基,RCC 再切換系統時鐘)* 時鐘路徑(本例典型):外部晶振 HSE(8MHz) -> PLL(×9) = 72MHz -> SYSCLK* AHB(HCLK) = 72MHz,APB1 = 36MHz(受限于最大 36MHz),APB2 = 72MHz* 該函數只做“時鐘樹切換 + 分頻設置 + Flash 等待周期設置”,不做外設時鐘開啟。*/
void stm32_clock_init(uint32_t plln)
{HAL_StatusTypeDef ret = HAL_ERROR;RCC_OscInitTypeDef rcc_osc_init = {0};RCC_ClkInitTypeDef rcc_clk_init = {0};/* ----------- 1) 配置振蕩器與 PLL 源/倍頻 ----------- */rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 選擇要配置的振蕩器類型:這里僅配置 HSE(外部高速晶振)rcc_osc_init.HSEState = RCC_HSE_ON; // HSE 狀態:開啟外部晶振(常見 8MHz)rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1; // HSE 預分頻:F1 主流型號為 DIV1 或 DIV2;8MHz 常用 DIV1rcc_osc_init.PLL.PLLState = RCC_PLL_ON; // 打開 PLL 鎖相環(用于倍頻得到高主頻)rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL 輸入源:選 HSE(外部晶振)。另一個常見選項是 HSI/2rcc_osc_init.PLL.PLLMUL = plln; // PLL 倍頻系數:典型 RCC_PLL_MUL9(8MHz ×9 = 72MHz)// 使能/配置上述振蕩器與 PLL。HAL 會依次啟動 HSE、配置 PREDIV、配置并使能 PLL,等待穩定ret = HAL_RCC_OscConfig(&rcc_osc_init);if (ret != HAL_OK){// 若失敗,通常意味著 HSE 未振蕩、參數非法或硬件異常;此處簡化為死循環,可換成 Error_Handler()while(1);}/* ----------- 2) 配置總線分頻與系統時鐘源 ----------- */// 告訴 HAL:我們要配置哪些時鐘域的參數(系統時鐘、AHB、APB1、APB2)rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系統時鐘源:切換到 PLL 輸出(上面配置的 HSE×PLLMUL)rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB(HCLK) 分頻:SYSCLK/1 = 72MHzrcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2; // APB1 分頻:HCLK/2 = 36MHz(因為 APB1 最大 36MHz)rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 分頻:HCLK/1 = 72MHz// 設置時鐘并配置 FLASH 等待周期// FLASH_LATENCY_2:當 SYSCLK 在 48–72MHz 區間,需要 2 個等待周期,保證取指正確ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2);if (ret != HAL_OK){while(1);}
}
sys.h:
#ifndef __SYS_H__
#define __SYS_H__#include "stm32f1xx.h" // HAL 頭文件,包含 RCC/FLASH/時鐘樹相關定義/*** @brief 初始化系統時鐘:選擇振蕩器、配置 PLL、切換 SYSCLK、設置各總線分頻與 Flash 等待周期* @param plln: RCC_PLL_MULx(如 RCC_PLL_MUL9),決定 PLL 倍頻系數,進而決定 SYSCLK 頻率*/
void stm32_clock_init(uint32_t plln);#endif
main.c:
#include "sys.h"int main(void)
{HAL_Init(); /* 初始化 HAL 庫:配置 NVIC 分組、SysTick 基礎時基、復位外設狀態等 */stm32_clock_init(RCC_PLL_MUL9); /* 設置系統時鐘:HSE(8MHz)→PLL×9=72MHz;同時 AHB=72MHz,APB1=36MHz,APB2=72MHz,FLASH_LATENCY=2 */while(1){// 用戶主循環:此時 SystemCoreClock≈72MHz(可用 HAL_RCC_GetHCLKFreq() 驗證)// 根據需要初始化并開啟具體外設時鐘(GPIO/USART/SPI/TIM...),并編寫應用邏輯}
}
關鍵參數為什么這樣選?還能怎么選?
下面把“每個參數的可選項、適用場景、為什么本例這么選”講清楚:
A. RCC_OscInitTypeDef
OscillatorType
可選:
RCC_OSCILLATORTYPE_HSE
/HSI
/LSE
/LSI
以及它們的組合(按位或)場景:
HSE:外部高速晶振(常見 8MHz),主系統時鐘的高精度來源
HSI:內部 8MHz RC 振蕩器,精度一般,無需外部器件
LSE:外部 32.768kHz 晶振,RTC 低功耗、長期計時
LSI:內部 40kHz RC,低功耗/獨立看門狗/RTC 備選
本例:只配置 HSE,目標是 72MHz 高性能主頻和高精度外設時序。
HSEState
可選:
RCC_HSE_ON
/RCC_HSE_OFF
/RCC_HSE_BYPASS
場景:
ON
:使用晶振BYPASS
:外部時鐘源輸入(方波/有源時鐘),不用晶振
本例:
RCC_HSE_ON
,使用 8MHz 晶振。
HSEPredivValue
(F1:DIV1 或 DIV2;部分“Connectivity line”器件支持更多分頻)常見:
DIV1
(8MHz 直入 PLL)、DIV2
(把 8MHz 變 4MHz 再進 PLL)場景:若外部源不適合直接倍頻,可先預分頻到合適范圍
本例:
DIV1
,8MHz 直接進 PLL。
PLL.PLLState
可選:
RCC_PLL_ON
/RCC_PLL_OFF
場景:需要高主頻就開;低功耗應用可關。
本例:開。
PLL.PLLSource
可選:
RCC_PLLSOURCE_HSE
或RCC_PLLSOURCE_HSI_DIV2
(F1 的 HSI 只能 /2 后進 PLL)場景:
外部晶振精準 → 選 HSE
板上沒晶振 → 選 HSI/2(精度差一點)
本例:HSE。
PLL.PLLMUL
可選:
RCC_PLL_MUL2
…RCC_PLL_MUL16
(不同子系列支持范圍略有差異)輸出:
PLLCLK = PLLSource × PLLMUL
(若用了 PREDIV,需先計算)約束:SYSCLK 最大 72MHz;APB1 最大 36MHz
本例:
RCC_PLL_MUL9
,8MHz ×9 = 72MHz,跑滿主頻。
B. RCC_ClkInitTypeDef
ClockType
選擇需要配置的域:
RCC_CLOCKTYPE_SYSCLK | HCLK | PCLK1 | PCLK2
。本例:四個都配置。
SYSCLKSource
可選:
RCC_SYSCLKSOURCE_HSI
/HSE
/PLLCLK
場景:
剛上電默認 HSI
要高頻率/高精度,切到 PLL 或 HSE
本例:
PLLCLK
(HSE×PLL)。
AHBCLKDivider
(HCLK)可選:
DIV1,2,4,8,...,512
約束:HCLK ≤ 72MHz
本例:
DIV1
,HCLK=72MHz。
APB1CLKDivider
(PCLK1)可選:
DIV1,2,4,8,16
約束:PCLK1 ≤ 36MHz(定死的)
本例:
DIV2
→ 72/2=36MHz,剛好滿頻。
APB2CLKDivider
(PCLK2)可選:
DIV1,2,4,8,16
約束:PCLK2 ≤ 72MHz
本例:
DIV1
→ 72MHz。
FLASH Latency
可選:
FLASH_LATENCY_0
(0WS)、_1
(1WS)、_2
(2WS)規則(F1 常用經驗):
SYSCLK ≤ 24MHz → 0WS
24–48MHz → 1WS
48–72MHz → 2WS
本例:
FLASH_LATENCY_2
,保證 72MHz 取指安全。
常見可選組合與“什么時候該這么做”
省硬件/低成本:不用晶振
PLLSource = HSI/2
,例如 HSI(8MHz)/2=4MHz,再 ×16=64MHz(或 ×14=56MHz)優點:省晶振、不占引腳
缺點:精度差(通信波特率誤差↑),頻率不一定整好。單片機內部晶振不穩定
適合:對精度不敏感、對成本/尺寸苛刻的項目
USB 要 48MHz 時鐘(F103 帶 USB 的型號)
典型:
HSE=8MHz → PLL×6=48MHz
給 USB系統主頻想 72MHz 時,需要選擇既能給 USB 48MHz,又能讓 SYSCLK 落在允許范圍的組合(具體看子系列對 USB 分頻器的支持)。你的 C8T6(不帶 USB 外設)一般不需要考慮。
功耗優先/中低速運行
例如:
HSE=8MHz → PLL×6=48MHz
,并把 AHB/APB 適當再分頻,或直接用 HSI=8MHz配套把
FLASH_LATENCY
降低,提高能效。
外設定時器“翻倍”行為
注意:當 APB 分頻器 ≠ 1 時,該 APB 上的 定時器時鐘會自動 ×2(比如 APB1=36MHz,則 TIM2/3/4/5 的時鐘為 72MHz)。
本例:APB1=HCLK/2=36MHz ? TIM2/3/4/5 實際計數時鐘 72MHz;APB2=1 ? TIM1 時鐘 72MHz。
這點在算 PWM/輸入捕獲/定時中斷時一定要記牢,否則頻率會算錯一倍。
驗證與輔助 API
SystemCoreClockUpdate()
/HAL_RCC_GetSysClockFreq()
/HAL_RCC_GetHCLKFreq()
/HAL_RCC_GetPCLK1Freq()
/HAL_RCC_GetPCLK2Freq()
用來打印/校驗你設置后的頻率,輔助外設分頻與波特率計算。失敗處理建議:把
while(1);
換成Error_Handler()
,在里頭打斷點或串口提示;也可以加個“超時檢測 HSE 未起振”的自檢。
易錯點與經驗貼士
APB1 不得超過 36MHz:所以當 HCLK=72MHz 時,
APB1CLKDivider
必須 >= 2。Flash 等待周期要匹配主頻:72MHz 一定要
FLASH_LATENCY_2
;否則各種詭異 HardFault。先配振蕩器,再切 SYSCLK:順序別反。
HSI 進 PLL 是 /2 后再倍頻(F1 專屬特點)。
外設波特率計算以 PCLK 為基:USART 用 PCLKx,SPI/I2C 用 PCLKx,TIM 則注意 APB≠1 時 ×2。
HAL_Init() 在前:很多人把 RCC 配在 HAL_Init() 之前,影響 SysTick/時基;建議就按現在的順序。
HSE 頻率與板卡一致:若你的板是 12MHz 晶振,
PLLMUL
要重算(如 ×6 得 72MHz),HSEPrediv
也可能需要調整。
標準庫:
sys.h:
#ifndef __SYS_H__
#define __SYS_H__#include "stm32f10x.h" /* SPL 頭文件:包含 RCC/FLASH 等外設寄存器與 API 原型 *//*** @brief 配置系統時鐘樹到 72 MHz(HSE 8MHz → PLL×9)* @param pllmul: PLL 倍頻因子(SPL 的 RCC_PLLMul_x 枚舉)* 常用值:* RCC_PLLMul_6 -> 8MHz×6 = 48MHz* RCC_PLLMul_9 -> 8MHz×9 = 72MHz(典型)* 注意:輸入源在本函數中固定為 HSE/1;若需 HSE/2、HSI/2,請改 sys.c 中的 RCC_PLLConfig。*/
void stm32_clock_init(uint32_t pllmul);#endif
sys.c:
#include "sys.h"/*** @brief 將系統時鐘配置為:HSE(8MHz) → PLL×(pllmul) → SYSCLK* 并設置 AHB/APB1/APB2 分頻與 Flash 等待周期,使之適配 72MHz 運行* @param pllmul: RCC_PLLMul_x(SPL 枚舉),典型為 RCC_PLLMul_9(8MHz×9=72MHz)* @note 調用順序建議:SystemInit()(啟動文件里自動調用)→ main() 中先執行 stm32_clock_init() → 再初始化外設* 若 HSE 未起振或配置失敗,本實現進入死循環(可替換為 Error_Handler)*/
void stm32_clock_init(uint32_t pllmul)
{ErrorStatus HSEStartUpStatus;/* --- 0) 復位 RCC 到缺省狀態(等價于上電復位后的時鐘設置) -------------------- */RCC_DeInit(); /* 清除 RCC 配置寄存器,關閉 PLL,選擇 HSI 為系統時鐘等 *//* --- 1) 使能外部高速時鐘 HSE,并等待其穩定 ------------------------------------ */RCC_HSEConfig(RCC_HSE_ON); /* 打開 HSE 晶振(8MHz,若是外部有源時鐘用 RCC_HSE_Bypass) */HSEStartUpStatus = RCC_WaitForHSEStartUp();/* 輪詢 HSERDY 標志,等待 HSE 起振穩定 */if (HSEStartUpStatus == SUCCESS){/* --- 2) Flash 預取與等待周期 ------------------------------------------------ */FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* 開啟指令預取,提高取指效率 *//** F1 經驗規則:* SYSCLK ≤ 24MHz -> FLASH_Latency_0* 24~48MHz -> FLASH_Latency_1* 48~72MHz -> FLASH_Latency_2* 我們目標 72MHz,因此設置 2 個等待周期。*/FLASH_SetLatency(FLASH_Latency_2);/* --- 3) 設置各總線分頻 ------------------------------------------------------ *//** AHB(HCLK) = SYSCLK / 1 = 72MHz* 注意:AHB 最大 72MHz*/RCC_HCLKConfig(RCC_SYSCLK_Div1);/** APB2(PCLK2) = HCLK / 1 = 72MHz* APB2 最大 72MHz,常用為 72MHz,驅動 GPIO/ADC/TIM1/USART1 等*/RCC_PCLK2Config(RCC_HCLK_Div1);/** APB1(PCLK1) = HCLK / 2 = 36MHz* 非常重要:APB1 最大只能 36MHz(TIM2/3/4/5、USART2/3、I2C/SPI2 等位于 APB1)*/RCC_PCLK1Config(RCC_HCLK_Div2);/** 可選:配置 ADC 時鐘(位于 APB2),比如 72MHz / 6 = 12MHz(≤14MHz)* 若暫不啟用 ADC,可以省略;此處給出常見寫法:*/// RCC_ADCCLKConfig(RCC_PCLK2_Div6);/* --- 4) 配置 PLL 輸入源與倍頻 ----------------------------------------------- *//** 本例選擇:PLL 源 = HSE/1(RCC_PLLSource_HSE_Div1)* 倍頻 = pllmul(RCC_PLLMul_x),常見為 x9 -> 72MHz** 若你的板子是 8MHz HSE,且希望 72MHz:RCC_PLLMul_9* 若你的板子是 12MHz HSE,要 72MHz,常用:HSE/1 ×6(RCC_PLLMul_6)* 若想用 HSI:改為 RCC_PLLSource_HSI_Div2(HSI 8MHz/2=4MHz 再倍頻)*/RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);/* --- 5) 使能 PLL 并等待鎖定 ------------------------------------------------- */RCC_PLLCmd(ENABLE); /* 打開 PLL */while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){/* 等待 PLL 鎖定,就緒后才能切換系統時鐘源到 PLL */}/* --- 6) 選擇 PLL 作為系統時鐘源 --------------------------------------------- */RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);/* 等待切換完成:讀取并確認 SYSCLK 源為 PLL */while (RCC_GetSYSCLKSource() != 0x08) /* 0x08 == RCC_CFGR_SWS_PLL */{/* 等待系統時鐘源切換到 PLL */}/* --- 7) (可選)更新 SystemCoreClock 變量 ----------------------------------- *//** 如果你的工程使用 CMSIS 的 SystemCoreClock 全局變量,并需要依賴它做延時/波特率計算,* 請在此調用 SystemCoreClockUpdate()(或手動賦值 72MHz)。*/SystemCoreClockUpdate();}else{/* HSE 起振失敗:可能晶振焊接/電容不當/參數錯誤/外部時鐘不穩等 */while (1){/* TODO: 放置錯誤指示(如點燈/串口打印),便于排查 */}}
}
main.c:
#include "sys.h"int main(void)
{/* 對于 SPL 工程,SystemInit() 一般在啟動文件(startup_xxx.s)復位后自動調用,里面會把時鐘先配置到缺省狀態(HSI)。我們在這里再切換到目標時鐘。 */stm32_clock_init(RCC_PLLMul_9); /* HSE=8MHz → PLL×9 = 72MHzAHB=72MHz, APB1=36MHz, APB2=72MHz, Flash Latency=2 *//* 之后再去初始化 GPIO、USART、SPI、TIM 等具體外設時鐘和功能 */while (1){/* 用戶主循環 */}
}
說明與可選項
HSE 源的三種形態
RCC_HSE_ON
:外部無源晶振(常見 8 MHz)RCC_HSE_Bypass
:外部有源時鐘模塊/方波輸入(不用晶振本體)RCC_HSE_OFF
:關閉 HSE(低功耗或僅用 HSI 場景)
PLL 輸入源的選擇
RCC_PLLSource_HSE_Div1
:HSE 直接進 PLLRCC_PLLSource_HSE_Div2
:HSE/2 進 PLL(早期/特殊板可能需要)RCC_PLLSource_HSI_Div2
:HSI/2 進 PLL(省晶振但精度差)
常用倍頻舉例(基于 HSE=8 MHz)
48 MHz:
RCC_PLLMul_6
(USB 常用 48 MHz 基準,但 F103C8 本體無 USB 設備控制器)56 MHz:
RCC_PLLMul_7
72 MHz:
RCC_PLLMul_9
(滿頻、最常用)
APB 定時器“×2”規則
當 APBx 分頻器 ≠ 1 時,該總線上的 定時器時鐘 = PCLKx × 2。
本配置:APB1=HCLK/2=36 MHz ? TIM2/3/4/5 實際計數時鐘 72 MHz
APB2=HCLK/1=72 MHz ? TIM1 計數時鐘 72 MHz
做 PWM、輸入捕獲、定時中斷頻率計算務必考慮這條規則。
Flash 等待周期
≤24 MHz:Latency 0
24~48 MHz:Latency 1
48~72 MHz:Latency 2
72 MHz 必須設FLASH_Latency_2
,否則易 HardFault 或運行不穩。
若你的板是 12 MHz 晶振
把RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6);
→ SYSCLK=72 MHz。
或 12 MHz/2 × 12(Connectivity Line 支持更細分頻/倍頻配置,普通 F103C8 無 CFGR2 多預分頻選項)。
常見故障與排查
卡死在
RCC_WaitForHSEStartUp()
:HSE 未起振。檢查晶振焊接/負載電容/是否應使用 Bypass。切到 PLL 后跑飛或 HardFault:多半是 Flash Latency 未正確設置 或 APB1 超頻。
外設時序不準:確認
SystemCoreClockUpdate()
是否調用、波特率計算基于正確的 PCLKx;定時器是否“×2”。