_STM32关于CPU超频的参考_HAL

news2025/1/13 4:21:35

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;
}

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

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

相关文章

图片和短信验证码(头条项目-06)

1 图形验证码接口设计 将后端⽣成的图⽚验证码存储在redis数据库2号库。 结构&#xff1a; {img_uuid:0594} 1.1 创建验证码⼦应⽤ $ cd apps $ python ../../manage.py startapp verifications # 注册新应⽤ INSTALLED_APPS [django.contrib.admin,django.contrib.auth,…

解决idea中无法拖动tab标签页的问题

1、按 Ctrl Alt S 打开设置&#xff0c;找到路径 File | Settings | Appearance & Behavior | Appearance 2、去掉勾选 Drag-and-drop with Alt pressed only 即可

单片机(MCU)-简单认识

简介&#xff1a; 内部集成了CPU&#xff0c;RAM&#xff0c;ROM&#xff0c;定时器&#xff0c;中断系统&#xff0c;通讯接口等一系列电脑的常用硬件功能。 单片机的任务是信息采集&#xff08;依靠传感器&#xff09;&#xff0c;处理&#xff08;依靠CPU&#xff09;&…

QT c++ 样式 设置 按钮(QPushButton)的渐变色美化

上一篇文章中描述了标签的渐变色美化,本文描述按钮的渐变色美化。 1.头文件 #ifndef WIDGET_H #define WIDGET_H #include <QWidget> //#include "CustomButton.h"#include <QVBoxLayout> #include <QLinearGradient> #include <QPushButton&…

【物流管理系统 - IDEAJavaSwingMySQL】基于Java实现的物流管理系统导入IDEA教程

有问题请留言或私信 步骤 下载项目源码&#xff1a;项目源码 解压项目源码到本地 打开IDEA 左上角&#xff1a;文件 → 新建 → 来自现有源代码的项目 找到解压在本地的项目源代码文件&#xff0c;点击确定&#xff0c;根据图示步骤继续导入项目 查看项目目录&#xff…

【数据结构-堆】【二分】力扣3296. 移山所需的最少秒数

给你一个整数 mountainHeight 表示山的高度。 同时给你一个整数数组 workerTimes&#xff0c;表示工人们的工作时间&#xff08;单位&#xff1a;秒&#xff09;。 工人们需要 同时 进行工作以 降低 山的高度。对于工人 i : 山的高度降低 x&#xff0c;需要花费 workerTimes…

如何用SQL语句来查询表或索引的行存/列存存储方式|OceanBase 用户问题集锦

一、问题背景 自OceanBase 4.3.0版本起&#xff0c;支持了列存引擎&#xff0c;允许表和索引以行存、纯列存或行列冗余的形式创建&#xff0c;且这些存储方式可以自由组合。除了使用 show create table命令来查看表和索引的存储类型外&#xff0c;也有用户询问如何通过SQL语句…

CDA数据分析师一级经典错题知识点总结(3)

1、SEMMA 的基本思想是从样本数据开始&#xff0c;通过统计分析与可视化技术&#xff0c;发现并转换最有价值的预测变量&#xff0c;根据变量进行构建模型&#xff0c;并检验模型的可用性和准确性。【强调探索性】 2、CRISP-DM模型Cross Industry Standard Process of Data Mi…

算法题(32):三数之和

审题&#xff1a; 需要我们找到满足以下三个条件的所有三元组&#xff0c;并存在二维数组中返回 1.三个元素相加为0 2.三个元素的下标不可相同 3.三元组的元素不可相同 思路&#xff1a; 混乱的数据不利于进行操作&#xff0c;所以我们先进行排序 我们可以采取枚举的方法进行解…

【设计模式】介绍常见的设计模式

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 ✨ 介绍一下常见的设计模式✨ Spring 中常见的设计模式 这期内容主要是总结一下常见的设计模式&#xff0c;可…

单通道串口服务器(三格电子)

一、产品介绍 1.1 功能简介 SG-TCP232-110 是一款用来进行串口数据和网口数据转换的设备。解决普通 串口设备在 Internet 上的联网问题。 设备的串口部分提供一个 232 接口和一个 485 接口&#xff0c;两个接口内部连接&#xff0c;同 时只能使用一个口工作。 设 备 的网 口…

【蓝牙】win11 笔记本电脑连接 hc-06

文章目录 前言步骤 前言 使用电脑通过蓝牙添加串口 步骤 设置 -> 蓝牙和其他设备 点击 显示更多设备 更多蓝牙设置 COM 端口 -> 添加 有可能出现卡顿&#xff0c;等待一会 传出 -> 浏览 点击添加 hc-06&#xff0c;如果没有则点击 再次搜索 确定 添加成…

信息安全、网络安全和数据安全的区别和联系

信息安全、网络安全和数据安全是信息安全领域的三大支柱&#xff0c;它们之间既存在区别又相互联系。以下是对这三者的详细比较&#xff1a; 一.区别 1.信息安全 定义 信息安全是指为数据处理系统建立和采用的技术和管理的安全保护&#xff0c;保护计算机硬件、软件和数据不…

oracle闪回表

文章目录 闪回表案例1&#xff1a;&#xff08;未清理回收站时的闪回表--成功&#xff09;案例2&#xff08;清理回收站时的闪回表--失败&#xff09;案例3&#xff1a;彻底删除表&#xff08;不经过回收站--失败&#xff09;案例4&#xff1a;闪回表之后重新命名新表总结1、删…

如何让QPS提升20倍

一、什么是QPS QPS&#xff0c;全称Queries Per Second&#xff0c;即每秒查询率&#xff0c;是用于衡量信息检索系统&#xff08;例如搜索引擎或数据库&#xff09;或请求-响应系统&#xff08;如Web服务器&#xff09;每秒能够处理的请求数或查询次数的一个性能指标。以下是…

vue 实现打包并同时上传至服务器端

将 publish_script 及以下文件 upload.server.js 添加到 主文件下&#xff0c;与 src 同级别 具体操作步骤&#xff1a; 1、安装 npm install scp2 2、将下面两条命令加入至 package.json 的 scripts 中 "upload": "node publish_script/upload.server.js&q…

2015年IMO第3题

△ A B C \triangle ABC △ABC 的垂心为 H H H, A H AH AH 为直径的圆交 △ A B C \triangle ABC △ABC 的外接圆 ⨀ O \bigodot O ⨀O 于 A A A, Q Q Q. H Q HQ HQ 为为直径的圆交 ⨀ O \bigodot O ⨀O 于 Q Q Q, K K K. M M M 为 B C BC BC 边中点, F F F 为 A…

新活动平台建设历程与架构演进

01 前言 历时近两年的重新设计和迭代重构&#xff0c;用户技术中心的新活动平台建设bilibili活动中台终于落地完成&#xff01;并迎来了里程碑时刻 —— 接过新老迭代的历史交接棒&#xff0c;从内到外、从开发到搭建实现全面升级&#xff0c;开启了活动生产工业化新时代&#…

《安富莱嵌入式周报》第348期:开源低功耗测试仪,开源创意万用表,续航100-300小时,开源PCB电机,自制shell和网络协议栈,开源水培自动化系统

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV1Tzr9Y3EQ7/ 《安富莱嵌入式周报》第348期&#xff1a;开源低功…

【Kaggle】练习赛《预测贴纸的销量》(下)

前言 上篇利用各地区的GDP数据还填充目标标签的缺失值&#xff1b;中篇顺着这个思路&#xff0c;利用这个原理来预测未来的销量&#xff0c;具体方法思路&#xff1a;先一一对国家、产品和商店进行汇总&#xff0c;然后对未来三年的每日销售额进行预测&#xff0c;然后再进行分…