引言

       本次基于铁头山羊up介绍的基于标准库进行的时钟树编程内容,完成笔者一贯的寄存器方式开发程序编写时钟树配置的测试程序,完整测试源码见文章末尾


1、时钟树初始:初始上电时的时钟状态。

初始: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); 
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐