STM32 教學系列 第 2 節:時脈控制講解 🎯 學習目標 理解 STM32F446 的時脈架構 - 掌握振盪器、倍頻器、分頻器的概念 學會配置 Clock Tree - 透過 CubeMX 設定各組態的時脈分配 實現多時脈配置實驗 - 透過改變時脈速度觀察 LED 閃爍頻率變化 🎓 時脈系統基礎概念 為什麼需要時脈? STM32 微控制器內部的每個元件(CPU、外設、計時器)都需要時脈信號來同步運作。時脈越快,處理速度越快,但功耗也越高。 STM32F446 時脈來源 STM32F446 提供 3 種時脈源 : 時脈源 頻率 精度 功耗 用途 HSI (內部高速振盪器) 16 MHz ±2% 低 備用時脈、低功耗模式 HSE (外部高速振盪器) 8 MHz ±1% 中等 主系統時脈(需晶振) LSI (內部低速振盪器) 32 kHz ±2% 很低 看門狗、RTC PLL 倍頻原理 PLL (Phase-Locked Loop) 是一個電路,能將低頻時脈信號倍頻至高頻。例如: 輸入:8 MHz(HSE) 倍數:21 輸出:8 MHz × 21 = 168 MHz STM32F446 的最大系統時脈為 180 MHz ,但實驗中常用 168 MHz (更穩定)。 分頻器作用 系統時脈(168 MHz)太快,不適合直接提供給所有外設。因此使用分頻器降低特定外設的時脈: 分頻器 功能 典型設定 AHB (HCLK) CPU 和內存時脈 168 MHz / 1 = 168 MHz APB1 (PCLK1) 低速外設(UART、I2C) 168 MHz / 4 = 42 MHz APB2 (PCLK2) 高速外設(SPI、ADC) 168 MHz / 2 = 84 MHz 🛠️ Clock Tree 深度解析 Clock Tree 的流程圖 ┌─────────────────────────────────────────────────┐ │ 時脈源選擇 │ │ ├─ HSI (16 MHz) - 內部振盪器 │ │ ├─ HSE (8 MHz) - 外部晶振 │ │ └─ LSI (32 kHz) - 內部低速振盪器 │ └────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ PLL 倍頻(選擇性) │ │ 主要參數:PLLM / PLLN / PLLP / PLLQ │ │ 目標:8 MHz → 168 MHz │ └────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 系統時脈選擇(SYSCLK) │ │ ├─ HSI (16 MHz) │ │ ├─ HSE (8 MHz) │ │ └─ PLL (168 MHz) ← 常用 │ └────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ AHB 分頻器 │ │ 168 MHz / 1 = 168 MHz (HCLK) │ └────────────┬────────────────────────────────────┘ │ ┌────────┼────────┐ ▼ ▼ ▼ APB1 APB2 其他外設 /4 /2 ↓ ↓ 42MHz 84MHz PLL 倍頻計算 STM32F446 PLL 配置公式: f_VCO = (f_HSE / PLLM) × PLLN f_PLLCLK = f_VCO / PLLP f_USB = f_VCO / PLLQ 常用配置(HSE = 8 MHz): PLLM = 8 (預分頻:8 MHz / 8 = 1 MHz) PLLN = 168 (倍頻:1 MHz × 168 = 168 MHz) PLLP = 2 (主時脈分頻:168 MHz / 2 = 84 MHz) ❌ 這會得 84 MHz PLLP = 2 (若要 168 MHz,應設定 PLLN = 336, PLLP = 2) 更精確的配置(目標 168 MHz): PLLM = 8 (預分頻:8 MHz / 8 = 1 MHz) PLLN = 336 (倍頻:1 MHz × 336 = 336 MHz) PLLP = 2 (主時脈分頻:336 MHz / 2 = 168 MHz) ✅ ⚙️ CubeMX 時脈配置步驟 步驟 1:開啟時脈配置界面 開啟之前的專案或建立新專案 雙擊 .ioc 檔案開啟 CubeMX 點擊 Clock Configuration 標籤 步驟 2:配置 HSE(8 MHz 晶振) 在「System Clock Mux」右側,點擊 HSE 下拉選單 選擇 Crystal/Ceramic Resonator 確認 HSE 頻率顯示為 8 MHz 步驟 3:配置 PLL 找到 PLL 配置區塊 設定以下參數: PLLM : 8 PLLN : 336 PLLP : 2 (對應頻率應顯示 168 MHz) PLLQ : 7 (用於 USB,通常保持預設) 若 PLLN 改為 336 後系統時脈仍未達 168 MHz,檢查 PLLP 是否為 2 步驟 4:選擇 PLL 作為系統時脈源 在「System Clock Mux」中,選擇 PLLCLK 確認 SYSCLK 顯示 168 MHz 步驟 5:配置 AHB 分頻器 在「System Clock Mux」下方,找到 AHB Prescaler 設定為 1 (不分頻,HCLK = 168 MHz) 步驟 6:配置 APB 分頻器 APB1 Prescaler 設為 4 結果:168 MHz / 4 = 42 MHz (適合 UART、I2C) APB2 Prescaler 設為 2 結果:168 MHz / 2 = 84 MHz (適合 SPI、ADC) 步驟 7:驗證配置 ✅ 查看 CubeMX 下方的總結,應顯示: SYSCLK: 168 MHz HCLK: 168 MHz PCLK1: 42 MHz PCLK2: 84 MHz 💻 完整程式碼 - 時脈配置驗證 main.c - 多時脈驗證程式 /* STM32 Lesson 02 - Clock Configuration Verification * 功能:測試不同的時脈配置,透過 LED 閃爍頻率變化來驗證 * 難度:中級 */ #include "main.h" #include "gpio.h" /* 私有變數和函數 */ void SystemClock_Config(void); static void MX_GPIO_Init(void); void LED_Blink(uint16_t delay_ms, uint8_t count); /* 全域變數:存儲當前系統時脈速度 */ uint32_t SystemCoreClock = 168000000; /* 168 MHz */ int main(void) { HAL_Init(); /* 配置系統時脈為 168 MHz */ SystemClock_Config(); /* 初始化 GPIO */ MX_GPIO_Init(); /* 驗證系統時脈 */ while (1) { /* 第一階段:快速閃爍 (5 次,100ms 間隔) - 表示 168 MHz */ LED_Blink(100, 5); HAL_Delay(1000); /* 暫停 1 秒 */ /* 第二階段:中速閃爍 (3 次,200ms 間隔) - 表示配置完成 */ LED_Blink(200, 3); HAL_Delay(1000); /* 第三階段:慢速閃爍 (2 次,500ms 間隔) - 表示系統運行 */ LED_Blink(500, 2); HAL_Delay(2000); } } /** * @brief LED 間隔閃爍函數 * @param delay_ms: 每次亮/暗的延遲時間(毫秒) * @param count: 閃爍次數 */ void LED_Blink(uint16_t delay_ms, uint8_t count) { for (uint8_t i = 0; i < count; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); /* LED 亮 */ HAL_Delay(delay_ms); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); /* LED 暗 */ HAL_Delay(delay_ms); } } /** * @brief 系統時脈配置函數 * 配置 HSE (8 MHz) → PLL 倍頻至 168 MHz */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /* 配置電源調節器 */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /* 配置 RCC 振盪器 */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; /* 預分頻:8 MHz / 8 = 1 MHz */ RCC_OscInitStruct.PLL.PLLN = 336; /* 倍頻:1 MHz × 336 = 336 MHz */ RCC_OscInitStruct.PLL.PLLP = 2; /* 主輸出分頻:336 MHz / 2 = 168 MHz */ RCC_OscInitStruct.PLL.PLLQ = 7; /* USB 時脈:336 MHz / 7 = 48 MHz */ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /* 配置時脈分頻器 */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /* HCLK = 168 MHz */ RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; /* PCLK1 = 42 MHz */ RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; /* PCLK2 = 84 MHz */ /* Flash 延遲設定(高速時脈需要更多等待週期) */ if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } /* 配置 SysTick 用於系統計時 */ HAL_SYSTICK_Config(SystemCoreClock / 1000); /* 1ms 中斷一次 */ } /** * @brief GPIO 初始化函數 */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } /** * @brief 系統錯誤處理 */ void Error_Handler(void) { while (1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); /* 快速閃爍表示錯誤 */ HAL_Delay(100); } } 🔍 測試與除錯 預期結果 ✅ LED 閃爍模式序列 : 5 次快速閃爍 (100ms)→ 系統時脈 168 MHz 正常 暫停 1 秒 3 次中速閃爍 (200ms)→ 時脈配置完成 暫停 1 秒 2 次慢速閃爍 (500ms)→ 系統穩定運行 暫停 2 秒後重複 常見問題與解決 問題 原因 解決方案 LED 不閃爍 PLL 配置失敗 檢查 CubeMX Clock Configuration,確認 PLLN 為 336,PLLP 為 2 閃爍節奏不對 HAL_Delay 計算錯誤 確認 SysTick 配置正確,系統時脈應為 168 MHz 編譯警告 「implicit conversion」 時脈值類型不匹配 確認 SystemCoreClock 變數型別為 uint32_t 程式掛起 Flash 延遲不足 檢查 FLASH_LATENCY_5 是否設定,高速時脈需更多等待週期 驗證時脈速度 方法 1:UART 輸出驗證(後續課程) 透過串口列印 SystemCoreClock 值 方法 2:邏輯分析儀 測量 PA5 的方波頻率,應為固定值 方法 3:計算驗證 若 HAL_Delay(100) 實際延遲 100ms,時脈正確 ✅ 📚 深入理解 Flash 存取延遲(FLASH_LATENCY) 快閃記憶體存取速度是有限的。當系統時脈增加時,需要增加等待週期(Latency)。 系統時脈 建議 LATENCY 0-30 MHz 0 31-60 MHz 1 61-90 MHz 2 91-120 MHz 3 121-150 MHz 4 151-168 MHz 5 📱 高階實驗 挑戰 1:低功耗時脈配置 修改代碼,使用 HSI (16 MHz) 而非 PLL: /* 使用 HSI 代替 PLL(低功耗模式) */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF; /* 關閉 PLL */ RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; 觀察 LED 閃爍速度是否變慢(因為系統時脈從 168 MHz 降至 16 MHz) 挑戰 2:動態時脈切換 實現在運行時在不同時脈間切換的函數 🔗 延伸學習 下節預覽 - 第 3 節:GPIO 進階(按鍵、中斷、去彈跳) 第 3 節將學習: GPIO 輸入配置 - 按鍵檢測原理 中斷系統(EXTI) - 事件驅動編程 軟體去彈跳 - 解決按鍵抖動問題 相關資源 📘 STM32F446 Reference Manual - RCC章節 🔗 PLL 倍頻計算器(線上工具) ✨ 時脈控制掌握完成!下節進入 GPIO 進階應用 🚀