_STM32关于CPU超频的参考_HAL

MCU: STM32F407VET6

官方最高稳定频率:168MHz

工具:STM32CubeMX

        本篇仅仅只是提供超频(默认指的是主频)的简单方法,并未涉及STM32超频极限等问题。原理很简单,通过设置锁相环的倍频系数达到不同的频率,从而实现超频。需要注意的是,运行时切换频率不能直接修改,因为此时用的HSE时钟是经由锁相环产生的,直接修改锁相环会出现问题。解决办法是,切换为HSI并关闭锁相环,然后重新配置锁相环,最后再重新切换HSE。

        最后切记,超频有风险!!

一、时钟配置

        使用STM32CubeMX可以很方便地配置时钟树,配置时钟树时需要先知道开发板所用的外部晶振频率。从下图可知晓,使用的外部晶振为12MHz

然后经由STM32CubeMX自动配置时钟树,从这里可以看到锁相环里能看到M、N、P三个参数,其中N是倍频系数,最高可达432。这里我们目标是配置为168MHz,即官方标称频率

        使用STM32CubeMX生成代码后,我们可以在Core/Src目录下找到main.c中的SystemClock_Config函数。从代码中可以轻易看到,PLLM、PLLN、PLLP就是前面看到的M、N、P三个参数。同时由于外部晶振频率为12MHz,自动配置过程中,M和P分频系数分别为6和2恰好可以把12MHz分频为1MHz,使得倍频系数即为主频频率(MHz)。

        后面超频时,利用的就是下面代码。

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 6;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

二、超频

         根据前面所言,运行时直接修改所用的时钟会发生错误,所以需要切换时钟源后再修改。下面两个函数用于切换时钟源

void SystemClock_SwitchToHSI(void)
{
    // 将系统时钟切换到 HSI
    __HAL_RCC_SYSCLK_CONFIG(RCC_SYSCLKSOURCE_HSI);

    // 等待时钟切换完成
    while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_HSI) {}
}




void SystemClock_SwitchToPLL(void)
{
    // 将系统时钟切换到 PLL
    __HAL_RCC_SYSCLK_CONFIG(RCC_SYSCLKSOURCE_PLLCLK);

    // 等待时钟切换完成
    while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_PLLCLK) {}
}

接下来,在系统时钟配置函数的基础上,修改代码。在配置PLL前,先禁用全局中断,防止时钟中断等影响配置过程,然后切换时钟源为HSI并禁用锁相环

    // 禁用全局中断
    __disable_irq();

    // 切换到 HSI
    SystemClock_SwitchToHSI();

    // 禁用 PLL
    __HAL_RCC_PLL_DISABLE();

        然后是配置PLL过程,此时参考系统时钟初始化代码,其中plln是传入的形参变量(锁相环倍频系数)。需要注意的是主频频率提高后,Flash的等待周期要相应延长,原为FLASH_LATENCY_5,这里简单判断了一下,如果大于168MHz,就延长为FLASH_LATENCY_7

    // 配置 PLL
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 6;       // HSE 分频系数
    RCC_OscInitStruct.PLL.PLLN = plln;    // PLL 倍频系数
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // PLLP 分频系数
    RCC_OscInitStruct.PLL.PLLQ = 4;       // PLLQ 分频系数

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {

        Error_Handler();
    }

    // 配置系统时钟
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    // 设置 Flash 等待周期
    uint32_t flash_latency = (plln <= 168) ? FLASH_LATENCY_5 : FLASH_LATENCY_7;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, flash_latency) != HAL_OK)
    {
        Error_Handler();
    }

最后是切换为PLL,并重新启用全局中断

    // 切换到 PLL
    SystemClock_SwitchToPLL();

    // 重新启用全局中断
    __enable_irq();

        完整代码如下,再次提醒,此代码是以外部晶振为12MHz的前提下有STM32CubeMX生成的,使用时需根据自身情况修改

/**
 * @brief 设置系统时钟频率
 * @param plln 锁相环倍频
 * @details 基于外部晶振为12MHz的配置
 */
void SystemClock_SetFrequency(uint32_t plln)
{
    // 禁用全局中断
    __disable_irq();

    // 切换到 HSI
    SystemClock_SwitchToHSI();

    // 禁用 PLL
    __HAL_RCC_PLL_DISABLE();

    // 配置 PLL
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 6;       // HSE 分频系数
    RCC_OscInitStruct.PLL.PLLN = plln;    // PLL 倍频系数
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // PLLP 分频系数
    RCC_OscInitStruct.PLL.PLLQ = 4;       // PLLQ 分频系数

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {

        Error_Handler();
    }

    // 配置系统时钟
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    // 设置 Flash 等待周期
    uint32_t flash_latency = (plln <= 168) ? FLASH_LATENCY_5 : FLASH_LATENCY_7;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, flash_latency) != HAL_OK)
    {
        Error_Handler();
    }

    // 切换到 PLL
    SystemClock_SwitchToPLL();

    // 重新启用全局中断
    __enable_irq();
}

        此时可另写两个函数,其一为默认配置,以便能从超频状态返回标准状态;其二为超频状态,建议不要设太高,推荐200~216MHz。提高主频后,也会提高其他时钟线,进而会对外设产生影响。此时需要注意一些如USB、以太网等对频率要求很高的外设,需要重新设置参数。

/**
  * @brief  超频
  * @param  无
  * @retval 无
  * @details 通过超频可以提升至 200MHz~216MHz,这里默认使用216MHz
  */

void SystemClock_Overclock()
{
    SystemClock_SetFrequency(216);
}
/**
 * @brief 默认系统时钟初始化
 * @details 最高稳定频率为168MHz
 */
void SystemClock_DefaultConfig()
{
    SystemClock_SetFrequency(168);
}

        HAL中,对定时器的频率设置是依赖于全局变量SystemCoreClock,由HAL_RCC_ClockConfig来更新,使用HAL_RCC_GetPCLK1Freq等函数可以实现自动纠正频率,下面代码可以作为参考

#include "stm32f4xx_hal.h"

/**
  * @brief  设置定时器频率
  * @param  htim: 定时器句柄(如 &htim2)
  * @param  target_freq: 目标频率(单位:Hz)
  * @retval HAL_StatusTypeDef: 成功返回 HAL_OK,失败返回 HAL_ERROR
  */
HAL_StatusTypeDef Timer_SetFrequency(TIM_HandleTypeDef *htim, uint32_t target_freq)
{
    uint32_t timer_clock_freq; // 定时器时钟源频率
    uint32_t psc_value;        // 预分频器值

    // 获取 APB 总线时钟频率
    if (htim->Instance == TIM2 || htim->Instance == TIM3 || htim->Instance == TIM4 || htim->Instance == TIM5 ||
        htim->Instance == TIM9 || htim->Instance == TIM10 || htim->Instance == TIM11)
    {
        // APB1 定时器
        timer_clock_freq = HAL_RCC_GetPCLK1Freq();
        if (RCC->CFGR & RCC_CFGR_PPRE1_2) // 检查 APB1 预分频器
        {
            timer_clock_freq *= 2; // 如果预分频器不为 1,时钟频率乘以 2
        }
    }
    else
    {
        // APB2 定时器
        timer_clock_freq = HAL_RCC_GetPCLK2Freq();
        if (RCC->CFGR & RCC_CFGR_PPRE2_2) // 检查 APB2 预分频器
        {
            timer_clock_freq *= 2; // 如果预分频器不为 1,时钟频率乘以 2
        }
    }

    // 检查目标频率是否有效
    if (target_freq == 0 || target_freq > timer_clock_freq)
    {
        return HAL_ERROR; // 目标频率无效
    }

    // 计算预分频器值
    psc_value = (timer_clock_freq / target_freq) - 1;

    // 检查预分频器值是否超出范围
    if (psc_value > 0xFFFF)
    {
        return HAL_ERROR; // 预分频器值超出 16 位范围
    }

    // 设置定时器预分频器
    __HAL_TIM_SET_PRESCALER(htim, psc_value);

    return HAL_OK;
}

其参考的是STM32CubeMX生成的“系统”定时器(这里使用的是TIM7)的初始化代码

/*用于配置供HAL使用基础时钟,频率为1KHz*/
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    RCC_ClkInitTypeDef clkconfig;
    uint32_t uwTimclock, uwAPB1Prescaler = 0U;

    uint32_t uwPrescalerValue = 0U;
    uint32_t pFLatency;
    HAL_StatusTypeDef status;

    /* Enable TIM7 clock */
    __HAL_RCC_TIM7_CLK_ENABLE();
    /* Get clock configuration */
    HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
    /* Get APB1 prescaler */
    uwAPB1Prescaler = clkconfig.APB1CLKDivider;
    /* Compute TIM7 clock */
    if (uwAPB1Prescaler == RCC_HCLK_DIV1)
    {
        uwTimclock = HAL_RCC_GetPCLK1Freq();
    } else
    {
        uwTimclock = 2UL * HAL_RCC_GetPCLK1Freq();
    }

    /* Compute the prescaler value to have TIM7 counter clock equal to 1MHz */
    uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);

    /* Initialize TIM7 */
    htim7.Instance = TIM7;

    /* Initialize TIMx peripheral as follow:

    + Period = [(TIM7CLK/1000) - 1]. to have a (1/1000) s time base.
    + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
    + ClockDivision = 0
    + Counter direction = Up
    */
    htim7.Init.Period = (1000000U / 1000U) - 1U;
    htim7.Init.Prescaler = uwPrescalerValue;
    htim7.Init.ClockDivision = 0;
    htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
// 考虑到LVGL初始化会死循环

    status = HAL_TIM_Base_Init(&htim7);
    if (status == HAL_OK)
    {
        /* Start the TIM time Base generation in interrupt mode */
        status = HAL_TIM_Base_Start_IT(&htim7);
        if (status == HAL_OK)
        {
            /* Enable the TIM7 global Interrupt */
            HAL_NVIC_EnableIRQ(TIM7_IRQn);
            /* Configure the SysTick IRQ priority */
            if (TickPriority < (1UL << __NVIC_PRIO_BITS))
            {
                /* Configure the TIM IRQ priority */
                HAL_NVIC_SetPriority(TIM7_IRQn, TickPriority, 0U);
                uwTickPrio = TickPriority;
            } else
            {
                status = HAL_ERROR;
            }
        }
    }

    /* Return function status */
    return status;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

若依框架--数据字典设计使用和前后端代码分析

RY的数据字典管理: 字典管理是用来维护数据类型的数据&#xff0c;如下拉框、单选按钮、复选框、树选择的数据&#xff0c;方便系统管理员维护。减少对后端的访问&#xff0c;原来的下拉菜单点击一下就需要对后端进行访问&#xff0c;现在通过数据字典减少了对后端的访问。 如…

openEuler 22.04使用yum源最快速度部署k8s 1.20集群

本文目的 openEuler的官方源里有kubernetes 1.20&#xff0c;使用yum源安装是最快部署一个k8s集群的办法 硬件环境 主机名系统架构ipmasteropenEuler release 22.03 (LTS-SP2)arm192.168.3.11edgeopenEuler release 22.03 (LTS-SP2)arm192.168.3.12deviceopenEuler release 22.…

使用宝塔面板,安装 Nginx、MySQL 和 Node.js

使用ssh远程链接服务器 在完成使用ssh远程链接服务器后 可使用宝塔面板&#xff0c;安装 Nginx、MySQL 和 Node.js 宝塔网站 一、远程链接服务器 二、根据服务器系统安装宝塔 wget -O install.sh https://download.bt.cn/install/install_lts.sh && sudo bash inst…

Linux第一课:c语言 学习记录day06

四、数组 冒泡排序 两两比较&#xff0c;第 j 个和 j1 个比较 int a[5] {5, 4, 3, 2, 1}; 第一轮&#xff1a;i 0 n&#xff1a;n个数&#xff0c;比较 n-1-i 次 4 5 3 2 1 // 第一次比较 j 0 4 3 5 2 1 // 第二次比较 j 1 4 3 2 5 1 // 第三次比较 j 2 4 3 2 1 5 // …

油猴支持阿里云自动登陆插件

遇到的以下问题&#xff0c;都已在脚本中解决&#xff1a; 获取到的元素赋值在页面显示&#xff0c;但是底层的value并没有改写&#xff0c;导致请求就是获取不到数据元素的加载时机不定&#xff0c;尤其是弱网情况下&#xff0c;只靠延迟还是有可能获取不到&#xff0c;且登陆…

什么是卷积网络中的平移不变性?平移shft在数据增强中的意义

今天来介绍一下数据增强中的平移shft操作和卷积网络中的平移不变性。 1、什么是平移 Shift 平移是指在数据增强&#xff08;data augmentation&#xff09;过程中&#xff0c;通过对输入图像或目标进行位置偏移&#xff08;平移&#xff09;&#xff0c;让目标在图像中呈现出…

android framework.jar 在应用中使用

在开发APP中&#xff0c;有时会使用系统提供的framework.jar 来替代 android.jar, 在gradle中配置如下&#xff1a; 放置framework.jar 依赖配置 3 优先级配置 gradle.projectsEvaluated {tasks.withType(JavaCompile) {Set<File> fileSet options.bootstrapClasspat…

7.STM32F407ZGT6-RTC

参考&#xff1a; 1.正点原子 前言&#xff1a; RTC实时时钟是很基本的外设&#xff0c;用来记录绝对时间。做个总结&#xff0c;达到&#xff1a; 1.学习RTC的原理和概念。 2.通过STM32CubeMX快速配置RTC。 27.1 RTC 时钟简介 STM32F407 的实时时钟&#xff08;RTC&#xf…

如何开启苹果手机(IOS)系统的开发者模式?

如何开启开发者模式&#xff1f; 一、打开设置二、隐私与安全性三、找到开发者模式四、开启开发者模式------------------------------------------------------------如果发现没有开发者模式的选项一、电脑下载爱思助手二、连接手机三、工具箱——虚拟定位——打开虚拟定位——…

day06_Spark SQL

文章目录 day06_Spark SQL课程笔记一、今日课程内容二、DataFrame详解&#xff08;掌握&#xff09;5.清洗相关的API6.Spark SQL的Shuffle分区设置7.数据写出操作写出到文件写出到数据库 三、Spark SQL的综合案例&#xff08;掌握&#xff09;1、常见DSL代码整理2、电影分析案例…

stable diffusion 量化学习笔记

文章目录 一、一些tensorRT背景及使用介绍1&#xff09;深度学习介绍2&#xff09;TensorRT优化策略介绍3&#xff09;TensorRT基础使用流程4&#xff09;dynamic shape 模式5&#xff09;TensorRT模型转换 二、实操1&#xff09;编译tensorRT开源代码运行SampleMNIST 一、一些…

Python生日祝福烟花

1. 实现效果 2. 素材加载 2个图片和3个音频 shoot_image pygame.image.load(shoot(已去底).jpg) # 加载拼接的发射图像 flower_image pygame.image.load(flower.jpg) # 加载拼接的烟花图 烟花不好去底 # 调整图像的像素为原图的1/2 因为图像相对于界面来说有些大 shoo…

primitive 编写着色器材质

import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体&#xff0c;Cesium.RectangleGeometry&#xff1a;几何体&#xff0c;Rectangle&#xff1a;矩形 let rectGeometry new…

【Linux-多线程】-线程安全单例模式+可重入vs线程安全+死锁等

一、线程安全的单例模式 什么是单例模式 单例模式是一种“经典的&#xff0c;常用的&#xff0c;常考的”设计模式 什么是设计模式 IT行业这么火&#xff0c;涌入的人很多.俗话说林子大了啥鸟都有。大佬和菜鸡们两极分化的越来越严重&#xff0c;为了让菜鸡们不太拖大佬的后…

C语言程序环境和预处理详解

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令 #define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令 #include 预处理指令 #undef 条件编译 程序的翻译环境和执行环…

pytorch torch.isclose函数介绍

torch.isclose 是 PyTorch 中用于比较两个张量是否“近似相等”的函数。它主要用于判断两个张量的对应元素在数值上是否接近&#xff08;考虑了浮点数精度的可能误差&#xff09;。 函数定义 torch.isclose(input, other, rtol1e-05, atol1e-08, equal_nanFalse)参数说明 inpu…

springboot整合h2

在 Spring Boot 中整合 H2 数据库非常简单。H2 是一个轻量级的嵌入式数据库&#xff0c;非常适合开发和测试环境。以下是整合 H2 数据库的步骤&#xff1a; 1. 添加依赖 首先&#xff0c;在你的 pom.xml 文件中添加 H2 数据库的依赖&#xff1a; <dependency><grou…

RabbitMQ 可观测性最佳实践

RabbitMQ 简介 RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写&#xff0c;支持多种客户端。它通过使用交换机&#xff08;Exchanges&#xff09;、队列&#xff08;Queues&#xff09;和绑定&#xff08;Bindings&#xff09;来路由消息&#xff…

初识 Git——《Pro Git》

Why Git&#xff1f; 1. 本地版本控制系统 Why&#xff1a; 许多人习惯用复制整个项目目录的方式来保存不同的版本&#xff0c;或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单&#xff0c;但是特别容易犯错。 有时候会混淆所在的工作目录&#xff0c;一不小心…

C语言gdb调试

目录 1.gdb介绍 2.设置断点 2.1.测试代码 2.2.设置函数断点 2.3.设置文件行号断点 2.4.设置条件断点 2.5.多线程调试 3.删除断点 3.1.删除指定断点 3.2.删除全部断点 4.查看变量信息 4.1.p命令 4.2.display命令 4.3.watch命令 5.coredump日志 6.总结 1.gdb介绍…