目录

  • 前言
  • 一、官方标准库是如何改变时钟的?
  • 二、修改系统时钟



前言

在我们常用的STM32 Blue Pill(如下图所示)上,一般在学习时都会使用默认的72Mhz晶振。但当我们自己设计电路板时,如果受到尺寸限制,不想使用外部晶振,可以使用STM32自带的内部8Mhz晶振。这篇文章主要介绍了如何修改标准库函数以选择内部的高速时钟HSI。

system32 文件无法更换_系统时钟

一、官方标准库是如何改变时钟的?

官方标准库system_stm32f10x.c文件中给定的系统时钟有以下几种:

system32 文件无法更换_system32 文件无法更换_02


程序在进入main函数之前,会先进入相应启动函数的文件中,文件名称startup_stm32f10x_ld.s(小容量产品),有下列语句:

system32 文件无法更换_stm32_03


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闪存编程手册中找到该寄存器。

system32 文件无法更换_系统时钟_04


最后三个位的设置需要根据系统时钟来配置。