STM32时钟树之编程实践(补充)
本文介绍了STM32单片机时钟树编程的关键内容。系统上电默认使用8MHz HSI时钟,通过SystemInit()可提升至72MHz。详细阐述了时钟树配置的四大步骤:开启HSE时钟、配置PLL锁相环、设置分频系数、选择系统时钟源。同时说明了Flash指令预取的配置方法,并提供了LED闪烁测试的核心代码。文中既包含寄存器级操作也涉及标准库函数的使用,为STM32时钟系统配置提供了完整的实现方案。
初始:SYSCLK来自HSI 8MHz,分频系数:AHB APB1 APB2 均为/1
内核从HCLK获取时钟。
内核时钟速度决定程序执行速度。
默认情况下,一句代码执行速度约6个时钟周期。
2、标准库启动代码
在启动文件也就是汇编文件中,可以找到单片机上电启动首先执行的内容如下:

先执行SystemInit(),在执行main函数。

其中,SystemInit()中做好了时钟树编程,将时钟频率提升至72MHz,以极大发挥出单片机的性能。

HSE->锁相环->AHB->APB1(/1) APB2(/2)
所以想要使用上电默认的时钟树,则需要注释汇编中的SystemInit()执行部分。

使得系统时钟编程默认时钟即均8MHz。
由于一条语句执行约6个时钟周期,故循环666666次则可得到比较精准的500ms,以此可实现简易的LED闪烁。

3、时钟树编程接口

RCC_开头:Reset And Clock Controller,复位和时钟控制器。
灰色:可开关,默认关
绿色:默认开启
PLL配置:时钟来源、倍频系数
SYSCLK配置:配置系统时钟SYSCLK来源
HCLK配置:分频系数
PCLK1配置:分频系数
PCLK2配置:分频系数
RCC标志位状态获取:查询某些状态用于判断,原因是开关需消耗时间。
获取SYSCLK来源:获取当前SYSCLK时钟来源,用于判断切换情况。
4、配置时钟树

四大步骤:
(1)开启HSE时钟
(2)配置PLL锁相环参数:HSE、9倍频,并启动
(3)分频器配置:AHB APB1 APB2
(4)选择系统时钟SYSCLK来源

(#1)开启HSE部分
对于寄存器配置,即CR寄存器中。



对于标准库配置则下述接口。

(#2)配置并开启PLL锁相环
对于寄存器配置,CFGR寄存器和CR寄存器中。





对于标准库配置,如下述接口。

(#3)配置AHB APB1 APB2分频器
对于寄存器配置,在CFGR寄存器中。




对于标准库配置,使用下述函数接口。

(#4)选择SYSCLK来源
使用寄存器配置,在CFGR寄存器中



使用标准库配置,使用下述函数接口。

5、配置Flash指令预取

赶不上Flash速度。
利用缓冲区:提前准备Flash数据给内核
指令预取开启条件:SYSCLK频率<=8MHz,所以需要在最初开启更为合适。
设置访问延时:72MHz需要2个周期的访问延迟。
关于Flash的配置,还不知道如何查阅相关寄存器描述,但在STM32系统文件中确实存在FLASH的寄存器结构体定义及配置内容。

6、本次测试核心源代码
/*
* @Descripttion: 时钟树编程
* @Author: JaRyon
* @version:
* @Date: 2026-01-22 19:50:52
*/
#include "stm32f10x.h"
// 系统时钟初始化配置函数(需要先注释掉原先汇编启动文件中的SystmInit()执行逻辑)
void System_ClockInit(void);
int main(void)
{
System_ClockInit();
// led闪烁观察现象 PA2
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
GPIOA->CRL |= GPIO_CRL_MODE2;
GPIOA->CRL &= ~GPIO_CRL_CNF2;
// 默认熄灭
GPIOA->ODR |= GPIO_ODR_ODR2;
while (1)
{
GPIOA->ODR &= ~GPIO_ODR_ODR2;
// 一条语句执行约6个时钟周期,一个时钟周期8MHz即1/8us
// 故500ms即500000us,需要执行500000/6*8≈666666次
for (uint32_t i = 0; i < 666666; i++);
GPIOA->ODR |= GPIO_ODR_ODR2;
for (uint32_t i = 0; i < 666666; i++);
}
}
// 72MHz
void System_ClockInit(void)
{
// 0. 开启Flash指令预取与访问延迟
// 0.1 解锁Flash寄存器(STM32F1修改ACR必须先解锁)
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
// 0.2 设置访问延迟与Flash预取
FLASH->ACR &= ~(0x1F); // 清0相关位
FLASH->ACR |= (FLASH_ACR_LATENCY_2); // 2个等待周期
FLASH->ACR |= FLASH_ACR_PRFTBE; // 使能预取缓冲区
// 0.3 等待预取缓冲区就绪
while (!(FLASH->ACR & FLASH_ACR_PRFTBS));
// 1. 开启HSE时钟
RCC->CR |= RCC_CR_HSEON;
// 等待HSE是否稳定
while ((RCC->CR & RCC_CR_HSERDY) == 0);
// 2. 配置PLL锁相环
// 2.1 选择PLL输入源:HSE 不分频
RCC->CFGR |= RCC_CFGR_PLLSRC;
RCC->CFGR &= ~RCC_CFGR_PLLXTPRE;
// 2.2 设置倍频系数:x9
RCC->CFGR &= ~RCC_CFGR_PLLMULL;
RCC->CFGR |= RCC_CFGR_PLLMULL9;
// 2.3 开启PLL锁相环
RCC->CR |= RCC_CR_PLLON;
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// 3. 设置总线分频系数
RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2);
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB DIV1
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 DIV2
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2 DIV1
// 4. 配置系统时钟源:使用PLL来的
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
更多推荐



所有评论(0)