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

news2025/1/11 20:43:09

前言

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

从启动文件来看,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有最高的时钟频率限制,所以一般不会超频使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql之一条mysql语句时如何执行的

请把连接器的功能说明一下? 连接器负责建立客户端和mysql服务器之间的连接.当客户端在中断输入连接命令(mysql -h -u -p),传输层使用tcp协议,通过三次握手建立连接. 如果mysql服务服务没有启动,会报错: 如果mysql服务正常启动,完成三次握手,在传输层建立连接后,会进行密码验证…

【云原生】k8s 一键部署(ansible)

文章目录一、概述二、Ansible 部署1)开启记录日志2)去掉第一次连接ssh ask确认3)配置hosts三、开始编排 ansible playbook1)创建目录2)节点初始化3)安装 docker4)安装 k8s 相关组件5&#xff09…

2022 年 12 月区块链操作系统的开发回顾

正在寻找区块链操作系统组件的最新进展?你找对地方了!正如在我们的路线图文章中所描述的那样,我们一直在朝着定期且频繁的更新方向发展着,以便让我们的社区能够及时的了解到我们取得的进展,以及将区块链操作系统提升到…

新年喜报 再添殊荣 加速科技荣获浙江省“专精特新”企业认定

新年伊始,杭州市经济和信息化局公布了2022年度浙江省专精特企业名单。杭州加速科技有限公司(以下简称“加速科技”)凭借在技术创新、产品研发、精细化程度、经营能力、拓展潜力等多方面的优势,荣获浙江省“专精特新”企业荣誉称号…

一个人,一座城,你到底在乎什么?Python 爬虫告诉你!

大家好,我是安果!有时候,我们想知道生活在这座城市的人每天交流的事情,然后对数据进行一些分析,方便我们更好地了解城市的特征及居民的需求以重庆为例,最火爆的论坛是购物狂,每天都有大量的帖子…

求解带不确定事件的FJSP的多目标强化学习框架

文献:Hao Wang, Junfu Cheng, Chang Liu, Yuanyuan Zhang, Shunfang Hu, Liangyin Chen,Multi-objective reinforcement learning framework for dynamic flexible job shop scheduling problem with uncertain events,Applied Soft Computing,Volume 131,2022,1097…

超级详细的python知识点及练习题(附答案)

今天咱们继续来学习python的小知识吖,上一次木有看的同学请看:python8大核心语句 作者:阿玥的小东东 学习:python,正在学习c 主页:阿玥的小东东 目录 1.复习及易错,快来学习!&#…

基于python手撕实现BP 神经网络实现手写数字识别

本项目使用python实现全连接网络和梯度优化 方向传播并且实现了 手写数字识别项目: 神经网络 model 先介绍个三层的神经网络,如下图所示输入层(input layer)有三个 units( 为补上的 bias,通常设为

线程池ThreadPoolExecutor源码解析

参考视频 首先回顾一下创建线程等的三种方式 第一个是直接继承Thread类,重写run方法,这个其实内部也是继承了Runnable接口重写run方法。 比如: public class MyThread extends Thread{Overridepublic void run() {System.out.println("…

论文分享-《基于数据驱动多输出 ARMAX 建模的高炉十字测温中心温度》

1.简介 最近在学习研究NARMAX,故也分享下自己看的一篇论文。 2018 年 3 月 的《基于数据驱动多输出 ARMAX 建模的高炉十字测温中心温度》。主要是采用NARMAX模型进行预测,多输入多输出,有5个输出,预测中心五个点位的温度。下面讲…

计算机 - - - 局域网共享文件夹,局域网传输文件(待完善)

win10局域网共享文件夹 A电脑: 共享文件夹的电脑 B电脑: 访问共享文件夹的电脑 操作完成后, B电脑可以下载A电脑中的文件, B电脑可以修改删除, B电脑可以上传B电脑的文件到A电脑. A电脑 找到要共享的文件夹, 例如我要共享文档(E:), 我要把文档(E:)中的所有文件都让B电脑访问…

Python - 数据容器str(字符串)

目录 字符串的定义 字符串的常用操作 查找特定字符串的下标索引值 index 字符串的替换 replace 字符串的分割 split 字符串的规整操作 strip 统计字符串中某字符串的出现次数 count 统计字符串的长度 len 字符串切片 [起始下标:结束下标:步长] 字符串的定义 和其它容器…

银行案例分析:识别个人贷款客户画像,实现精准营销与风险防范

作为商业银行最主要的业务活动,也是收益最大的活动,贷款于银行的重要性不言而喻。又由于个人贷款是银行贷款不可或缺的一部分,那么了解个人贷款客户画像就有助于银行对客户进行精准销售和风险识别。 # 选手介绍 #张昊泽:亚利桑那州…

Pycharm入门搭建Django 项目

一、环境准备 1、pycharm版本 2、python版本 二、创建项目 击左上角的 File -> New Project 点击Create创建完成之后页面等待下载环境 查看Django的版本 python -m django --version 启动项目 python manage.py runserver 三、后记 在启动 Django 项目的时候我发现控制台…

【PaaS】分享一家最近发现的宝藏Paas厂家

目录 一、结识独自开 二、独自开的介绍 三、独自开的需求 四、独自开注册流程 五、神仙公司独自开 一、结识独自开 算是机缘巧合,我被C站白佬拉入了他的聊天群,群内均是来自于CSDN的不同领域的优质作者,其中不乏相关领域工作多年的老工程…

第一层:封装

文章目录前言类和对象封装class权限publicprotectedprivatestruct和class的区别封装的好处封装的用法学完封装,突破第一层🎉welcome🎉 ✒️博主介绍:一名大一的智能制造专业学生,在学习C/C的路上会越走越远&#xff0c…

【电子学会】2022年12月图形化四级 -- 金牌百分比

金牌百分比 计算金牌榜前十的国家获得的金牌总数占金牌总数的百分比。金牌榜前十的国家获得的金牌总数占金牌总数的百分比等于(金牌榜前十国家的金牌总数本届冬奥会金牌总数)100,并将这个数向下取整。 1. 准备工作 (1)保留舞台默认背景及角色小猫; (2)建立变量“金牌…

YOLOALL 一文了解YOLO各版本答案

来源:投稿 作者:ΔU 编辑:学姐 YoloAll项目简介 相信了解YOLO的小伙伴们一定都有这样的困扰,目前YOLO各个版本数量非常多,不知道在实际场景中应该选择哪个YOLO版本。甚至有时为了比较两个不同版本的YOLO的效果&#x…

python标准库xmlrpc 之RPC远程方法调用

💖💖💖养成每日阅读好习惯, 每天进步, 超越昨天的自己💖💖💖 愿景:输出体系化编程知识与技巧,助力软件行业发展与从业者学习减负,让编程产生更大价值。 🔎&am…

【linux】之网络安全

防火墙作用 在计算机领域,防火墙是用于保护信息安全的设备,其会依照用户定义的规则,允许或限制数据的传输。 用于保护内网安全的一种设备依据规则进行防护用户定义规则允许或拒绝外部用户访问防火墙分类 逻辑上划分,防火墙可以大体分为主机防火墙和网络防火墙 主机防火墙:…