01.自己动手配置时钟树
在上一期,我们已经成功搭建了一个STM32F103的工程模板,基于BSP-SYSTEM-HARDWARE-APP四层架构实现,这一期,我们将要自己动手配置时钟树。
一、为什么要配置时钟树?
为什么需要自己动手配置时钟树?因为时钟是单片机运行的基础,时钟配置不正确的话,单片机运行会产生各种各样的问题,为了避免后期踩坑,前期加强对单片机时钟树的理解是必要的。
时钟是单片机的“心跳”。所有的 CPU 内核、外设、总线、定时器 都依赖时钟运行。
如果时钟配置错误,常见问题包括:
- 串口波特率不对(通信失败)
- SysTick 延时不准(RTOS 任务调度混乱)
- 外设(ADC、PWM、CAN、USB 等)速率异常
- MCU 超频 / 欠频运行,导致死机或性能不足
因此,正确配置时钟树是稳定运行的基础。
二、STM32F103 时钟树结构概览
STM32F103 的时钟系统主要由以下部分组成:
- 时钟源
- HSI:高速内部时钟(8 MHz)
- HSE:高速外部时钟(常用 8 MHz 或 16 MHz 晶振)
- PLL:锁相环,可倍频(最大输出 72 MHz)
- 总线时钟
- SYSCLK:系统时钟(由 HSI/HSE/PLL 选择)
- AHB:高级总线(驱动 CPU、DMA、SRAM)
- APB1:低速外设总线(USART2/3、I2C、CAN 等)
- APB2:高速外设总线(GPIO、USART1、ADC 等)
- 外设时钟
- 每个外设的工作频率由对应的 APB1/APB2 分频决定。
三、目标时钟配置
在 STM32F103 中,常见的几种系统时钟配置方案如下:
- 使用外部 8MHz 晶振 HSE → PLL → 72MHz SYSCLK
- 最常用方案,外部晶振精度高,系统稳定。
- 使用外部 16MHz 晶振 HSE → PLL → 72MHz SYSCLK
- 一些开发板(比如野火、正点原子)如采用 16MHz 晶振,同样需要通过 PLL 倍频到 72MHz。
- 使用内部 HSI 8MHz → PLL → 64MHz SYSCLK
- 不依赖外部晶振,成本低,但内部 RC 振荡器精度有限,适合对时钟要求不高的场景。
- 使用内部 HSI 8MHz 直通 SYSCLK
- 系统直接运行在 8MHz,最低功耗,通常用于低速调试。
四、系统时钟配置代码实现
我们在 System 层编写了 sys.c 模块,用于统一管理系统时钟。
代码结构如下:
void systemclock_config(void)
{
//USE_SYSTEM_CLOCK这个宏定义可在bsp_config.h文件中定义
if (USE_SYSTEM_CLOCK == 64000000)
{
systemclock_hsi_64m_hclk1();
}
else if (USE_SYSTEM_CLOCK == 8000000)
{
systemclock_hsi_8m();
}
else
{
systemclock_hse_72m();
}
}
通过配置 USE_SYSTEM_CLOCK 宏,我们就可以灵活切换不同的时钟方案,而不用修改底层实现。
关键实现思路
1.HSE 配置并倍频到 72MHz
void systemclock_8m_hse_32k_lsi_72m_hclk1(void)
{
// 复位RCC寄存器,恢复默认值
RCC_DeInit();
// 开启外部高速晶振 HSE
RCC_HSEConfig(RCC_HSE_ON);
// 等待 HSE 启动稳定
while (RCC_WaitForHSEStartUp() == ERROR);
// 使能 Flash 预取缓存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// 设置 Flash 延时周期:72MHz 时需设置为 2
FLASH_SetLatency(FLASH_Latency_2);
// AHB = HCLK = SYSCLK / 1 = 72MHz
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB1 = PCLK1 = HCLK /2 = 36MHz
RCC_PCLK1Config(RCC_HCLK_Div2);
// APB2 = PCLK2 = HCLK = 72MHz
RCC_PCLK2Config(RCC_HCLK_Div1);
// PLLCLK = HSE * 9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
// 开启PLL
RCC_PLLCmd(ENABLE);
// 等待PLL就绪 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// 选择PLL作为系统时钟源
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//等待PLL作为系统时钟源
while(RCC_GetSYSCLKSource() != 0x08);
//更新系统时钟
SystemCoreClockUpdate();
}
void systemclock_16m_hse_32k_lsi_72m_hclk1(void)
{
// 复位RCC寄存器,恢复默认值
RCC_DeInit();
// 开启外部高速晶振 HSE
RCC_HSEConfig(RCC_HSE_ON);
// 等待 HSE 启动稳定
while (RCC_WaitForHSEStartUp() == ERROR);
// 使能 Flash 预取缓存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// 设置 Flash 延时周期:72MHz 时需设置为 2
FLASH_SetLatency(FLASH_Latency_2);
// AHB = HCLK = SYSCLK / 1 = 72MHz
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// APB1 = PCLK1 = HCLK /2 = 36MHz
RCC_PCLK1Config(RCC_HCLK_Div2);
// APB2 = PCLK2 = HCLK = 72MHz
RCC_PCLK2Config(RCC_HCLK_Div1);
// PLLCLK = HSE * 9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);
// 开启PLL
RCC_PLLCmd(ENABLE);
// 等待PLL就绪 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// 选择PLL作为系统时钟源
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//等待PLL作为系统时钟源
while(RCC_GetSYSCLKSource() != 0x08);
//更新系统时钟

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



