当前位置: 首页 > news >正文

[标准库]STM32F103R8T6 标准库配置RCC时钟和超频

前言

这篇博客总结一下学习到的配置时钟的方法。

从启动文件来看,MCU复位之后,执行到SystemInit()这个函数之后,会进入系统初始化设置,比如根据当前的MCU型号进入不同的条件编译语句,再配置相应的寄存器初始值,并且让MCU的时钟运行起来。
在这里插入图片描述
这个SystemInit()函数的定义在system_stm32f10x.c(主要用来配置时钟)这个文件中,内容如下:

void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

比如上面的第一步 RCC->CR |= (uint32_t)0x00000001; 这一步是把RCC时钟配置成默认的复位状态,开启内部高速时钟,后面还有很多初始化语句,由于教程没讲,我暂且不管这些语句,不确定后续如果使用LL库编程是否需要自己初始化这些寄存器的值,这里先放过去。

文件到最后调用了一个 SetSysClock(); 函数用于设置时钟频率。这个函数里面根据不同的宏来配置系统的时钟频率。宏定义在system_stm32f10x.c的头部:

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #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

我这里是默认使能了配置频率到72Mhz的宏定义。

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
 
 /* If none of the define above is enabled, the HSI is used as System clock
    source (default after reset) */ 
}

根据上面宏定义的设置,这里调用的是 SetSysClockTo72();这个函数,内容如下:(由于篇幅太长,我只挑我使用的R8T6即普通型的配置来说、互联型M5、M7用CL那部分条件编译的语句)

__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;
    
	/*  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);
    
	/* 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 */
  }

这部分是普通型的即M0、M3、M4的时钟初始化语句,重新用std库写一个时钟配置函数放在bsp.rcc.c文件中,内容如下:

void rcc_systempclock_init(uint32_t RCC_PLLMul_x)
{
    ErrorStatus HSEStatus;
	
	RCC_DeInit();						//把RCC寄存器复位成默认值

    RCC_HSEConfig(RCC_HSE_ON);          //开启HSE(外部时钟输入)

    HSEStatus = RCC_WaitForHSEStartUp();

    if(HSEStatus == SUCCESS)
    {
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);        //使能预取指
        FLASH_SetLatency(FLASH_Latency_2);      //设置预取指的间隔时间  2个时钟间隔

        RCC_HCLKConfig(RCC_SYSCLK_Div1);        //设置AHB分频器  设置1分频 
        RCC_PCLK1Config(RCC_HCLK_Div2);         //设置APB1分频器 因为APB1最高速度只能到36MHz 输入APB1的时钟经过AHB分频器处理后还是72MHz 所以这里要进行二分频
        RCC_PCLK2Config(RCC_HCLK_Div1);         //设置APB2分频器 因为APB2最高速度为72MHz 所以这里设置为1分频就行

        RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_x);     //选择PLL锁相环倍频器的时钟输入为 HSE的1分频输入  传参(倍频倍数)进来
        RCC_PLLCmd(ENABLE);                     //开启PLL倍频器

        // 等待 PLL 倍频时钟 稳定
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);     //获取RCC模块中 PLL倍频时钟是否设置成功 标志位  

        /* Select PLL as system clock source */
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != (uint32_t)0x08);
    }
    else                                //如果HSE启动失败  在这个区域添加错误处理代码
    {
		while(1);
    }
}

其中的语句与上面那个官方写的SetSysClockTo72();函数是一样的,只不过这里是使用std库去替代操作寄存器。后续用到的时候可以对照着看看。

自己重写的这个rcc_systempclock_init(uint32_t RCC_PLLMul_x);函数,形参传进去的是倍频的倍数。
打开时钟树来推导,比如我选择外部晶振的高速时钟HSE输入,经过PLLXTPRE寄存器1分频输入到PLLSRC寄存器,选择HSE分频后的时钟信号作为倍频器的输入,经过倍频器倍频9倍出来的就是72MHz的正弦波信号,再把SYSCLK这个寄存器选择输入源为倍频之后的72MHz正弦波信号,进入到AHB分频器,选择1分频,然后就能作为APH1和APH2的输入信号了,其中APH1和APH2这两条时钟线上又挂载了很多外设,不同的外设又有不同的分频器,因为某些外设的时钟输入不能太高。

写好这个函数之后,在main函数进入while循环之前调用一下,rcc_systempclock_init(RCC_PLLMul_10),这样就是10倍频,出来的时钟频率就是80MHz的,可以使用MCO通过PA8引脚输出时钟波形出来,通过判断波形的频率我们就知道是否倍频成功了。

在bsp_rcc.c文件中添加函数原型:

void MCO_GPIO_init()
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA,&GPIO_InitStruct);
}

这句就是初始化PA8为MCO输出引脚,基本的配置跟配置LED的输出IO差不多,只不过GPIO_Mode 要配置为复用推挽输出。

完成上述的设置之后,main函数的内容如下:

int main(void)
{
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	rcc_systempclock_init( RCC_PLLMul_10 );
	
	MCO_GPIO_init();
	
	while(1)
	{
		
	}
}

在PA8引脚上挂一个示波器,因为时钟的波形是80MHz的,所以示波器的带宽要为测试波形的7倍,最起码要500MHz带宽的示波器,同时表笔也要选择200MHz以上的表笔,打到x10档,才能捕捉到MCO输出出来的正弦波信号。

这是最简单的检查是否超频成功的方法,其实还有很多其他方法,比如开一个中断计时1ms,观察超频之后时间缩短是否符合设计预期,就知道是否超频成功了。

我这边最高是倍频16倍,最终时钟输入频率是128MHz的正弦波,并且用示波器捕捉测试过符合预期。不过ST官方推荐的稳定运行频率是72MHz,而且APB1和APB2有最高的时钟频率限制,所以一般不会超频使用。

相关文章:

  • 地方网站 o2o/百度seo学院
  • wordpress能大网站主题/唐山建站公司模板
  • 怎样做网站上的语种链接/视频号关键词搜索排名
  • wordpress音频播放列表/广州网站建设推荐
  • 策划网站设计/郑州seo顾问阿亮
  • 北京住总第三开发建设有限公司网站/网站优化工具
  • Golang -- openwechat发送消息、自动回复
  • 《MySQL高级篇》十一、事务基础知识
  • 跟我学c++高级篇——模板元编程之四Wrapper
  • 业务逻辑漏洞
  • Open3D 点云最小二乘法拟合空间直线(Python版本)
  • 2023 年 10 大最佳 GIS 软件
  • 领域适应(Domain Adaptation)概念与tensorflow2简单实现
  • vite中如何更优雅的使用css
  • C# 目录
  • 【算法基础】1.5 前缀和与差分
  • JS入门到精通 正则详解(11)
  • 第五届字节跳动青训营 前端进阶学习笔记(二)JavaScript编码规范