为什么时钟配置是第一道坎
STM32 上电默认跑内部 8MHz HSI。不配时钟,定时器不准、串口乱码、外设不工作。理解时钟树是 STM32 开发的基础。
一、时钟源
STM32F103 有 5 个时钟源:
| 时钟源 |
频率 |
精度 |
用途 |
| HSI |
8MHz |
差(±1%,温漂大) |
默认启动,调试 |
| HSE |
4~16MHz |
高(晶振 ppm 级) |
正式产品必用 |
| PLL |
最高 72MHz |
取决于输入源 |
系统时钟 SYSCLK |
| LSI |
~40KHz |
差 |
IWDG 独立看门狗 |
| LSE |
32.768KHz |
高 |
RTC 实时时钟 |
二、时钟树(F103 完整路径)

关键规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1. SYSCLK = HSE / PLLXTPRE × PLLMUL 例:8MHz / 1 × 9 = 72MHz
2. HCLK = SYSCLK / AHB_Prescaler 例:72MHz / 1 = 72MHz
3. PCLK1 = HCLK / APB1_Prescaler ≤ 36MHz(F103 限制) 例:72MHz / 2 = 36MHz
4. PCLK2 = HCLK / APB2_Prescaler ≤ 72MHz 例:72MHz / 1 = 72MHz
5. 定时器时钟 = { if APB prescaler == 1 → PCLK else → PCLK × 2 } 例:APB1=36M(/2) → TIM2~7 = 36M×2 = 72MHz
|
三、HAL 库时钟配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; HAL_RCC_OscConfig(&RCC_OscInitStruct);
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; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); }
|
CubeMX 图形化配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Pinout → RCC: HSE: Crystal/Ceramic Resonator LSE: Disable
Clock Configuration: HSE: 8 MHz PLL Source: HSE PLL Mul: ×9 SYSCLK: 72 MHz ← 最终系统时钟 HCLK: 72 MHz APB1: 36 MHz (max for F103) APB2: 72 MHz
右下角自动计算各外设时钟,确保不超限(红色=超限)
|
四、时钟输出验证(MCO 引脚)
1 2 3 4 5 6 7 8 9 10 11 12
| void MCO_Config(void) { GPIO_InitTypeDef gpio = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); gpio.Pin = GPIO_PIN_8; gpio.Mode = GPIO_MODE_AF_PP; gpio.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &gpio);
HAL_RCC_MCOConfig(RCC_MCO, RCC_MCO1SOURCE_SYSCLK, RCC_MCODIV_1); }
|
五、各外设时钟速查
| 外设 |
挂在哪个总线 |
时钟来源 |
典型频率 |
| SYSTICK |
Cortex |
HCLK/8 或 HCLK |
9MHz 或 72MHz |
| USART1 |
APB2 |
PCLK2 |
72MHz |
| USART2/3 |
APB1 |
PCLK1 |
36MHz |
| SPI1 |
APB2 |
PCLK2 |
72MHz |
| SPI2 |
APB1 |
PCLK1 |
36MHz |
| I2C1/2 |
APB1 |
PCLK1 |
36MHz |
| TIM1/8 |
APB2 |
PCLK2×2 = 72M×2 |
144MHz |
| TIM2~7 |
APB1 |
PCLK1×2 = 36M×2 |
72MHz |
| ADC |
APB2 |
PCLK2 (÷2~8) |
最大 14MHz |
| USB |
— |
专用 48MHz |
必须 48MHz |
六、常见配置错误
错误 1:波特率不准
1 2 3 4 5 6 7 8
| USART1: 波特率 = PCLK2 / (16 × USARTDIV) = 72MHz / (16 × 39.0625) = 115200
如果误配 PCLK2 为 36MHz: = 36MHz / (16 × 19.53125) = 115200... 不对! → 实际输出 115384,误差 0.16% → 可能乱码
规则:波特率误差 > 2% 就会丢字节。
|
错误 2:定时器周期不准
1 2 3 4 5
| TIM2: 定时频率 = (PCLK1 × 2) / (PSC+1) / (ARR+1) = 72MHz / 7200 / 10000 = 1Hz ✓
如果忘记 APB1 的 ×2 规则: = 36MHz / 7200 / 10000 = 0.5Hz ✗ 差了一倍!
|
错误 3:忘记配 FLASH_LATENCY
1 2
| SYSCLK > 48MHz 必须 FLASH_LATENCY = 2 否则:程序随机 HardFault,时好时坏。
|
错误 4:USB 时钟不准
1 2 3 4
| USB 需要精确 48MHz。 HSE=8MHz ×9=72MHz ×(2/3)=48MHz → OK HSE=12MHz ×6=72MHz → USB 只能 72/1.5=48 → OK HSE=8MHz ×8=64MHz → USB 只能 64/1.5≠48 → 不行!
|
七、外设时钟开关(省电)
1 2 3 4 5 6 7 8 9
| __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); __HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_ADC1_CLK_DISABLE(); __HAL_RCC_SPI2_CLK_DISABLE();
|
八、调试时钟问题的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1. 用示波器测 MCO (PA8) 输出确认 SYSCLK → 72MHz 应该是稳定方波
2. 用定时器 + 输出比较翻转引脚测频率 → TIM2_CH1(PA0) 输出 1KHz PWM → 示波器测
3. 串口输出 SystemCoreClock 变量 → printf("SYSCLK=%lu\r\n", SystemCoreClock);
4. CubeMX Clock Configuration 红色警告 → 任何红色数字 = 超限,必须调整分频
5. 逻辑分析仪捕获 I2C/SPI 时钟线频率 → 频率与实际配置不符 = 时钟树计算有误
|