目录
- 前言
- 一、官方标准库是如何改变时钟的?
- 二、修改系统时钟
前言
在我们常用的STM32 Blue Pill(如下图所示)上,一般在学习时都会使用默认的72Mhz晶振。但当我们自己设计电路板时,如果受到尺寸限制,不想使用外部晶振,可以使用STM32自带的内部8Mhz晶振。这篇文章主要介绍了如何修改标准库函数以选择内部的高速时钟HSI。
一、官方标准库是如何改变时钟的?
官方标准库system_stm32f10x.c文件中给定的系统时钟有以下几种:
程序在进入main函数之前,会先进入相应启动函数的文件中,文件名称startup_stm32f10x_ld.s(小容量产品),有下列语句:
SystemInit()就是进行系统时钟初始化的函数,在system_stm32f10x.c文件下。
我们先看一下该函数到底进行了那些操作。
对于小容量产品,有宏定义STM32F10X_LD。为简洁起见,去掉SystemInit()函数中没有执行的部分,也就是不满足宏定义的语句。
void SystemInit (void)
{
//初始化时钟
//设置位0 HSION为1,使能内部 8Mhz 高速时钟 HSI
RCC->CR |= (uint32_t)0x00000001;
RCC->CFGR &= (uint32_t)0xF8FF0000;
//系统时钟切换00:HSI作为系统时钟
//HPRE(AHB频分频) 设置不分频
//PPRE1:低速APB1预分频,设置不分频
//PPRE2:高速APB2预分频,设置不分频
//ADCPRE:ADC预分频,设置PCLK2 2分频后作为ADC时钟
//MCO:微控制器时钟输出,设置为没有时钟输出
//PLLON、CSSON、HSEON置0
RCC->CR &= (uint32_t)0xFEF6FFFF;
//复位 HSEBYP
RCC->CR &= (uint32_t)0xFFFBFFFF;
//复位PLLSRC, PLLXTPRE, PLLMUL, USBPRE/OTGFSPRE
RCC->CFGR &= (uint32_t)0xFF80FFFF;
//关闭所有中断,清除保留位
RCC->CIR = 0x009F0000;
//配置系统时钟频率,HCLK,PCLK2,PCLK1的预分频
SetSysClock();
}
函数中除了最后一条语句基本都是操作了RCC中的相关寄存器,最后一条SetSysClock()则是设置了系统时钟。
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
}
由于已经宏定义了SYSCLK_FREQ_72MHz,因此该函数最终执行的是SetSysClockTo72()。
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/*开启HSE振荡器 */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
//等待HSE稳定,StartUpCounter用于超时处理判断标志
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY; //HSIRDY由硬件置1表示内部8MHz振荡器就绪
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
//若HSE稳定,HSEStatus = 0x01
if ((RCC->CR & RCC_CR_HSERDY) != RESET) //RESET = 0
{
HSEStatus = (uint32_t)0x01;
}
else //HSE超时了还没有稳定
{
HSEStatus = (uint32_t)0x00;
}
// HSE启动成功
if (HSEStatus == (uint32_t)0x01)
{
// 使能Flash与存储缓冲区,flash设置相关,查看闪存编程手册
FLASH->ACR |= FLASH_ACR_PRFTBE;
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);//清除LATENCY位
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
// 不分频SYSCLK ,HCLK = SYSCLK,即AHB不分频
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//APB2不分频,即PCLK2 = HCLK
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
//APB1二分频,即PCLK1 = HCLK/2
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
//把PLLSRC,PLLXTPRE,PLLMULL位清0
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
//设置HSE时钟作为PLL输入时钟,PLL 9倍频输出
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
//PLL使能
RCC->CR |= RCC_CR_PLLON;
//等待PLL时钟就绪
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
//清除SW位
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
//设置PLL输出作为系统时钟
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
//等待系统时钟切换到PLL
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{
//如果HSE启动失败,应用将会获得错误的时钟配置,用户可以在此处编写代码处理这种错误
}
}
到此STM32的时钟就配置完成了,按照默认的配置,我们可以得到如下时钟设置:
- SYSCLK = 72Mhz
- AHB不分频,HCLK = 72Mhz
- APB1二分频,PCLK1 = 36Mhz
- APB2不分频,PCLK2 = 72Mhz
二、修改系统时钟
了解了官方时钟配置的方法,我们就可以任意修改系统时钟了。这里以内部HIS时钟为例,介绍设置系统时钟为64Mhz的方法。方法很简单,将SystemInit()中的内容替换为如下程序:
void SystemInit(void)
{
RCC_DeInit();//将外设 RCC寄存器重设为缺省值,复位
RCC_HSICmd(ENABLE);//使能HSI
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET); //等待HSI使能成功
//使能Flash与存储缓冲区,flash设置相关,查看闪存编程手册
FLASH->ACR |= FLASH_ACR_PRFTBE;
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); //清除LATENCY位
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置AHB的分频系数
RCC_PCLK1Config(RCC_HCLK_Div2); //设置APB1的分频系数
RCC_PCLK2Config(RCC_HCLK_Div1); //设置APB2的分频系数
//设置 PLL 时钟源及倍频系数
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16);
RCC_PLLCmd(ENABLE);//如果PLL被用于系统时钟,那么它不能被失能
//等待指定的 RCC 标志位设置成功 等待PLL初始化成功
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//设置系统时钟(SYSCLK) 设置PLL为系统时钟源
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//选择想要的系统时钟
//等待PLL成功用作于系统时钟的时钟源
// 0x00:HSI 作为系统时钟
// 0x04:HSE作为系统时钟
// 0x08:PLL作为系统时钟
while(RCC_GetSYSCLKSource() != 0x08);//需与被选择的系统时钟对应起来
}
//设置的系统时钟 SYSCLK 为 64Mhz
每条语句的具体作用通过注释就可以了解,这里主要说明一下关于FLASH的配置语句。
FLASH->ACR |= FLASH_ACR_PRFTBE;
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); //清除LATENCY位
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
我们可以从STM32F10x闪存编程手册中找到该寄存器。
最后三个位的设置需要根据系统时钟来配置。