STM32 修改系统主频

本文介绍了STM32修改系统主频的方法。先解除只读文件属性,接着分析了system_stm32f10x.c文件中相关函数的作用及配置过程,如SetSysClockTo72()函数。还给出代码示例,最后强调修改主频后要重新匹配计时计算,一般不轻易修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32 修改系统主频

解除只读(修改只读文件)

  • 打开工程文件夹,找到对应只读文件在这里插入图片描述

  • 右键->属性->取消勾选只读属性->应用->确定在这里插入图片描述

  • 如果想整个文件夹或者整个工程的文件都取消只读,则需要对整个文件夹进行操作

    • 选中对应的工程文件夹在这里插入图片描述

    • 右键->属性->取消勾选只读(仅应用于文件夹中的文件)->应用->勾选将更改应用于此文件夹、子文件夹和文件->确定

      在这里插入图片描述

在这里插入图片描述

  • 横线表示文件夹里面的部分文件或者文件夹有不是只读的。要解决这个问题要打开文件夹里面的文件都要设置成只读。 有时候会出现问他文件无法设置只读文件,这是由于是系统文件或者权限不够,授权即可完成。在这里插入图片描述

system_stm32f10x.c

  • SystemInit()首先启动HSI,之后执行各种恢复缺省配置,最后调用SetSysClock()
    • SetSysClock()是一个分配函数,根据我们前面解除注释的宏定义,选择执行不同的配置函数,如SetSysClockTo72(),SetSysClockTo56(),SetSysClockTo48()等
      • 最后在SetSysClockTo72()等这些函数中,才是真正的配置,比如SetSysClockTo72()的配置是,选择HSE作为锁相环输入,之后锁相环进行9倍频,再选择锁相环输出作为主频,这样主频就是72M了。
  • SystemInit()

  • 用于配置时钟树

  • 在复位后,执行main( )函数之前,在启动文件里自动调用

  • SystemCoreClock 表示主频频率的值

  • SystemCoreClockUpdate( ) 用于更新SystemCoreClock ,因为这个变量只有最开始的一次赋值,之后如果改变了主频频率,这个值不会自动跟着变换,所以我们就需要调用以下这个函数,根据当前时钟树的配置,更新以下上面这个变量。

  • SystemInit() ->SetSysClock()(分配函数)->SetSysClockTo72()
//如果使用的是VL这些设备,也就是超值系列,那可选主频只有两个,HSE的8M和24M
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) //#if,预编译,用来兼容不同型号的设备
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000 //解除对应的注释来选择想要的系统主频
#endif
//根据定义的不同宏定义,选择执行不同的配置函数
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif
}

SetSysClockTo72()

  • 第一步,使能HSE,外部高速时钟
  • 第二步,等待HSERDY或者超时退出
  • 第三步,根据HSERDY标志位,给HSEState置1或0
  • 第四步,如果HSEState等于1,表示晶振启动成功,进入if语句
    • 配置FLASH的等待
    • 配置HCLK(AHB)、PCLK2(APB2)和PCLK1(APB1)的分频器
    • 配置锁相环(PLL),HSE是8M,锁相环选择9倍频,最终锁相环输出就是72M
    • 使能锁相环,等待锁相环准备就绪
    • 选择锁相环的输出作为系统时钟并等待锁相环称为系统时钟
  • 选择外部8M晶振作为锁相环输入,锁相环执行9倍频,输出的72M,选择为SYSCLK
  • 代码最后的else{},如果HSE启动失败,应用程序会有错误的时钟,用户可以在其中加一些代码解决这个错误,如果HSE没接或者HSE坏了,那么上面代码都不会执行,程序默认会使用最开始启动的HSI,内部8M时钟,作为系统主频。所以如果发现主频变成了8M,那可能就是外部晶振有问题了。可以在else{}里加一些代码,如果确实执行到了else里面,那就说明确实是HSE有问题。
static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

#ifdef STM32F10X_CL //互联型

#else    
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */

    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */
  }
}
#endif

代码示例

main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
int main(void)
{
    OLED_Init();
    OLED_ShowString(1,1,"SYSCLK:");
    OLED_ShowNum(1,8,SystemCoreClock,8);//显示当前系统主频

    
    while(1)
    {
        OLED_ShowString(2,1,"Running");
        Delay_ms(500);
        OLED_ShowString(2,1,"       ");
        Delay_ms(500);
    }
}

system_stm32f10x.c(部分)

//如果使用的是VL这些设备,也就是超值系列,那可选主频只有两个,HSE的8M和24M
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) //#if,预编译,用来兼容不同型号的设备
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000 //解除对应的注释来选择想要的系统主频
#endif

注意事项

  • 修改主频后,有很多涉及计时的计算都要重新匹配一下,比如Delay_us()函数,默认使用的是72M主频,所以在72M主频下,Dealy的时间是正确的,降低主频后,Delay的时间不能自适应变化,如果你想在任何主频下,Delay的时间都是正确的,可以用SystemCoreClock 来做自适应,把这个变量也带入计算即可。
  • 修改主频是一个牵一发而动全身的事情,改完之后,很多涉及精准计时的计算,都要做好匹配的工作,所以一般情况下,不轻易修改主频

Delay_us()

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}
### 动态调整 STM32 主频的方法 STM32主频由其内部的时钟系统控制,主要通过配置 PLL(Phase-Locked Loop)、HSE(High-Speed External Clock)、HSI(High-Speed Internal Clock)以及 LSI/LSE 来实现。如果需要在运行过程中动态调整主频,则可以通过重新配置这些时钟源来完成。 以下是具体的操作方式: #### 配置 RCC 寄存器 RCC(Reset and Clock Control)模块负责管理 STM32 的时钟树结构。要动态改变主频,通常需要操作以下几个寄存器: - **RCC_CFGR**:用于设置系统的时钟源和分频系数。 - **RCC_CR**:用于使能或禁用 HSE/HSI 和 PLL。 - **RCC_PLLCFGR**:用于配置 PLL 输入频率、倍频因子以及其他参数。 为了安全地更改主频,需遵循以下原则[^1]: - 始终备份当前的时钟配置状态以便恢复。 - 确保新的时钟配置不会违反外设的工作范围。 - 使用 HAL 库或者 LL 库可以简化复杂的寄存器操作过程。 #### 示例代码 下面是一个基于 HAL 库的示例程序,展示如何动态切换到不同的主频: ```c #include "stm32f1xx_hal.h" void ChangeSystemClock(uint32_t pllm, uint32_t pllsrc, uint32_t plln, uint32_t pllp) { RCC_OscInitTypeDef OscInitStruct; RCC_ClkInitTypeDef ClkInitStruct; // Disable interrupts to avoid conflicts during clock switching __disable_irq(); // Backup current configuration (optional but recommended) HAL_RCC_GetOscConfig(&OscInitStruct); HAL_RCC_GetClockConfig(&ClkInitStruct); // Configure the new PLL settings OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI; OscInitStruct.PLL.PLLMUL = pllm; // Set multiplier value OscInitStruct.PLL.PLLSource = pllsrc; // Select source as HSE or HSI if (HAL_RCC_OscConfig(&OscInitStruct) != HAL_OK) { Error_Handler(); } // Update system clock with updated values ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { // Adjust flash latency accordingly Error_Handler(); } // Re-enable interrupts after successful change __enable_irq(); } // Example usage: Switching to a different frequency dynamically int main(void) { HAL_Init(); // Initialize HAL Library SystemClock_Config(); // Initial default clock setup while (1) { // Dynamically adjust the system clock every second ChangeSystemClock(RCC_PLL_MUL9, RCC_PLL_SOURCE_HSE, 8, RCC_PLLP_DIV2); // Example setting HAL_Delay(1000); } } ``` 上述代码展示了如何利用 `ChangeSystemClock` 函数动态修改主频。需要注意的是,每次调用此函数前应确认新设定是否满足目标应用的需求,并适当调整闪存延迟 (`FLASH LATENCY`) 参数以匹配更高的工作频率。 #### 注意事项 - 不同型号的 STM32 支持的最大主频不同,请查阅对应的数据手册获取准确信息。 - 修改主频可能会影响某些依赖精确定时的功能(如 UART 波特率),因此需要同步更新相关初始化代码。 - 如果使用外部晶振作为时钟源,确保该晶振已稳定启动后再启用 PLL。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YRr YRr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值