每次看到新人拿开发板跑例程,时钟配置直接复制ST的Example代码就跑,出了事也不知道哪里的问题。时钟系统是STM32的根基,根没扎好上面全歪。今天把STM32的时钟树好好捋一捋。
一、STM32F1的时钟源有哪些
STM32F103有4个时钟源:
1. HSI:8MHz内部高速RC振荡器,精度1%左右,不用外部元件但精度差
2. HSE:外部高速时钟,接晶振通常用8MHz
3. LSI:32.768kHz内部低速RC,给看门狗和RTC用
4. LSE:32.768kHz外部晶振,给RTC用
二、时钟树结构
简单说一下信号流:
HSE(8MHz) --> PLL(锁相环) --> SYSCLK(72MHz) --> AHB --> APB2(72MHz) --> USART1, SPI1, ADC(最大72MHz)
--> APB1(36MHz) --> USART2/3, SPI2, I2C, USB(48MHz需要分频)
关键点:
• SYSCLK最高72MHz(F103的极限)
• AHB时钟 = SYSCLK
• APB2最高72MHz
• APB1最高36MHz(注意!APB1不能超过36MHz)
• ADC时钟 = PCLK2 / 分频系数,最大72MHz但推荐6-9MHz
三、PLL配置(最关键的一步)
STM32F1的PLL只能锁相到HSE或HSI/2。一般我们用HSE做输入。
PLL输入时钟 = HSE / PLL预分频器 PLL输出时钟 = PLL输入时钟 × PLL倍频系数
要求:PLL输入时钟必须在1-16MHz之间,PLL输出时钟(SYSCLK)最大72MHz。
举例:HSE = 8MHz,要得到72MHz SYSCLK
• PLL预分频器 = 1(不加分频),PLL输入 = 8MHz
• PLL倍频 = 9,PLL输出 = 8MHz × 9 = 72MHz ✓
如果HSE = 12MHz呢?
• PLL预分频器 = 2,PLL输入 = 6MHz
• PLL倍频 = 12,PLL输出 = 6MHz × 12 = 72MHz ✓
四、配置步骤(以HSE=8MHz为例)
void SystemClock_Config(void)
{
// 1. 使能HSE
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
// 2. 配置Flash等待周期(72MHz需要2个等待周期)
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// 3. 配置AHB分频器(SYSCLK不分频 -> HCLK = 72MHz)
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// 4. 配置APB2分频器(HCLK不分频 -> PCLK2 = 72MHz)
RCC_PCLK2Config(RCC_HCLK_Div1);
// 5. 配置APB1分频器(HCLK 2分频 -> PCLK1 = 36MHz)
RCC_PCLK1Config(RCC_HCLK_Div2);
// 6. 配置ADC分频器(PCLK2 6分频 -> ADC_CLK = 12MHz)
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 7. 配置PLL:HSE作为输入,预分频1,倍频9
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
// 8. 使能PLL
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// 9. 选择SYSCLK源为PLL
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 10. 等待切换完成
while(RCC_GetSYSCLKSource() != 0x08);
}
注意顺序很重要!先配置Flash等待周期再升频,不然CPU访问Flash会出错。这个是很多人踩的坑。
五、USB时钟的特殊处理
如果用USB,需要USB时钟精确的48MHz。PCLK1最高36MHz不够,需要从PLL输出再分频:
// USB时钟源 = PLL输出 / 1.5 = 72MHz / 1.5 = 48MHz
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1_5);
注意:用USB的时候APB1不能超过36MHz,但USB时钟可以从PLL直接分频出来不经过APB1。
六、几个实用技巧
1. 上电默认用HSI(8MHz内部振荡器),代码里再切到HSE+PLL。这样即使外部晶振没起振也不会死机。
2. 切换时钟源的时候一定要等稳定标志置位再往下走,不然会出诡异的问题。
3. 调完时钟记得检查各个总线的时钟频率是否符合芯片手册要求。APB1不能超过36MHz,ADC不能超过72MHz,USB必须48MHz±0.2%。
4. 低功耗模式下可以关HSE和PLL只用HSI或LSI,电流能降很多。但切换回来要等振荡器稳定,这段等待时间要注意。
5. 用STM32CubeMX配置时钟树确实方便,但建议至少看懂生成的代码在干什么,不然出了问题排查起来很痛苦。
我要赚赏金
