STM32 修改系统主频
解除只读(修改只读文件)
-
打开工程文件夹,找到对应只读文件
-
右键->属性->取消勾选只读属性->应用->确定
-
如果想整个文件夹或者整个工程的文件都取消只读,则需要对整个文件夹进行操作
-
选中对应的工程文件夹
-
右键->属性->取消勾选只读(仅应用于文件夹中的文件)->应用->勾选将更改应用于此文件夹、子文件夹和文件->确定
-
- 横线表示文件夹里面的部分文件或者文件夹有不是只读的。要解决这个问题要打开文件夹里面的文件都要设置成只读。 有时候会出现问他文件无法设置只读文件,这是由于是系统文件或者权限不够,授权即可完成。
system_stm32f10x.c
- SystemInit()首先启动HSI,之后执行各种恢复缺省配置,最后调用SetSysClock()
- SetSysClock()是一个分配函数,根据我们前面解除注释的宏定义,选择执行不同的配置函数,如SetSysClockTo72(),SetSysClockTo56(),SetSysClockTo48()等
- 最后在SetSysClockTo72()等这些函数中,才是真正的配置,比如SetSysClockTo72()的配置是,选择HSE作为锁相环输入,之后锁相环进行9倍频,再选择锁相环输出作为主频,这样主频就是72M了。
- SetSysClock()是一个分配函数,根据我们前面解除注释的宏定义,选择执行不同的配置函数,如SetSysClockTo72(),SetSysClockTo56(),SetSysClockTo48()等
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; //关闭定时器
}