一、创建标准库工程

1、下载 STM32F4 标准固件库

STM32标准外设软件库: 相关产品 - STMicroelectronicshttps://www.st.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries/products.html

名称 类型 作用说明
_htmresc 文件夹 存放官方 logo、图片等网页资源,开发中可忽略
Libraries 文件夹 核心库文件目录,包含 CMSIS 标准文件和外设驱动源码
Project 文件夹 官方提供的外设例程、模板工程(支持 Keil、IAR 等工具)
Utilities 文件夹 开发板相关的辅助工具 / 驱动(如板级支持包、第三方组件)
stm32f4xx_dsp_stdperiph_lib_um 编译的 HTML 帮助文档(.chm 官方库函数手册,包含所有外设驱动、DSP 函数的 API 说明、用法示例
Package_license/Release_Notes 文档 库的许可协议和版本更新说明

解压得到

💡 关键文件说明

  1. Libraries 文件夹这是开发中最核心的部分,包含两个关键子目录:
    • CMSIS:ARM Cortex-M 内核的通用接口文件,包含启动文件、系统时钟配置、内核寄存器定义
    • STM32F4xx_StdPeriph_Driver:所有外设(GPIO、UART、SPI、ADC 等)的驱动源码和头文件,以及 DSP 数字信号处理库
  2. stm32f4xx_dsp_stdperiph_lib_um.chm这是开发中必备的 “工具书”,你可以在这里:
    • 按外设分类查找所有库函数的定义、参数、返回值
    • 查看函数使用示例、寄存器配置说明
    • 查阅 DSP 库(FFT、滤波、矩阵运算等)的使用方法

              

2、新建工程目录结构

3、从官方库复制文件(关键)

4、MDK创建工程

5、设置MDK

① 设置编译输出文件路径

② 设置编译器版本

工程类型 必须用的编译器
STM32 标准库 StdPeriph V5 (ARMCC)
HAL 库 / LL 库 / 新项目 V6 (ARMClang)


 

③ 设置宏和包含头文件

  • 设置宏
    • USE_STDPERIPH_DRIVER告诉编译器启用标准外设库驱动,而不是直接操作寄存器。
    • USE_HAL_DRIVER:告诉编译器启用HAL库驱动
    • STM32F40_41xxx:指定目标芯片型号,这是条件编译的开关
      • 标准外设库的头文件(如 stm32f4xx.h)会根据这个宏,只编译对应芯片外设代码
  • 包含头文件
    • 编译时会在这些路径里查找对应的文件,未包含会直接报 “头文件找不到” 的错误。

④ 禁止参与编译

  • DMA2D:F407 没有。这是一个专门用于图形加速的 2D DMA 控制器,是 F429 及以上型号才有的外设。
  • FMC:F407 有的是FSMC(静态存储控制器),不是 FMC。FMC 是 F429 等型号对 FSMC 的升级版本,两者寄存器定义不完全兼容。
  • LTDC:F407 没有。这是 LCD-TFT 显示控制器,也是 F429 及以上型号才有的外设。

如何在 Keil 里禁用:

  • 右键点击文件 → Options for File
  • 取消勾选 Include in Target Build
  • 点击OK,文件图标上会出现一个小叉,表示不参与编译

禁用与不禁用的区别

    不禁用的风险 禁用的好处
    工程文件杂乱,编译时间变长 工程更清爽,编译速度更快
    误操作调用这些函数,会导致程序跑飞、HardFault 从根源上杜绝误调用的可能
    调试时容易被无关文件干扰 代码结构更清晰,维护更方便

     

    ⑤ 处理宏重定义警告

            注释掉 stm32f4xx.h 里重复定义的 5 行

    //#define  DBGMCU_APB2_FZ_DBG_TIM1_STOP        DBGMCU_APB1_FZ_DBG_TIM1_STOP    
    //#define  DBGMCU_APB2_FZ_DBG_TIM8_STOP        DBGMCU_APB1_FZ_DBG_TIM8_STOP
    //#define  DBGMCU_APB2_FZ_DBG_TIM9_STOP        DBGMCU_APB1_FZ_DBG_TIM9_STOP
    //#define  DBGMCU_APB2_FZ_DBG_TIM10_STOP       DBGMCU_APB1_FZ_DBG_TIM10_STOP
    //#define  DBGMCU_APB2_FZ_DBG_TIM11_STOP       DBGMCU_APB1_FZ_DBG_TIM11_STOP

    ⑥ 查看 HSE (外部高速时钟) 定义

         上电默认使用HSI(内部高速时钟)

    二、创建HAL库工程

    1、下载HAL库固件包

    • ① STM32CubeMX 内直接下载(最方便)

      • 安装并打开 STM32CubeMX(官网可下)。
      • 菜单栏:Help → Manage embedded software packages
    • ② 官网手动下载
      • 网址:STM32Cube MCU和MPU包: 相关产品 - STMicroelectronics
    • ③ 解压固件包

    2、新建工程目录结构

    3、从官方库复制文件(关键)

    复制文件其实无论是标准库还是HAL库其实几乎都是一样

    • 最主要的5个文件
      • 启动文件.s  (startup_stm32f4xx.s)
      • 系统初始化.c  (system_stm32f4xx.c )
      • 内核寄存器.c  (core_cm4.h)
      • 外设寄存器.c  (stm32f4xx.h)
      • 外设库.c  (STM32F4xx_StdPeriph_Driver整个文件夹)

    4、MDK创建工程

    5、设置MDK

    以下2处和标准库有差异,其余都一样

    ① 设置预处理器符号,用“,”隔开

    ②  查看 HSE (外部高速时钟) 定义

    在 stm32f407xx_hal_conf.h 里修改成实际硬件晶振值

    解答这里为啥标准库和HAL库不同的疑惑

    • 配置路径
      • 库类型 晶振配置位置 原因
        标准库 stm32f4xx.h 单文件大一统,所有定义放一起
        HAL 库 xxx_hal_conf.h 专门给用户改的配置文件,内核文件不动
    • 配置值
      • 默认 HSE 原因
        标准库 25MHz 老官方板用 25M
        HAL 库 8MHz 新官方板用 8M

    三、工程代码执行顺序

    硬件上电 → 真实执行顺序(5个主要文件)

    •  第一个运行: startup_stm32f4xx.s(启动文件)

      • 硬件强制、无法改变(绝对第一个执行)

        • → 初始化栈指针 SP
        • → 初始化数据段、清零未初始化段
        • 定义中断向量表(包括复位向量)
        • 跳去执行 system_stm32f4xx.c 里的 SystemInit ()

          • 
            Reset_Handler   PROC   /* 定义复位处理函数(CPU一上电,就跳这里执行!) */  
              
                            EXPORT  Reset_Handler   [WEAK]  /* 导出复位函数,让编译器知道这是入口 */
                           
                            IMPORT  SystemInit /* 告诉汇编器:SystemInit 函数在别的C文件里(system_stm32f4xx.c) */
            
                            IMPORT  __main           /* 告诉汇编器 __main 是C语言主函数入口 */
            
                            LDR     R0, =SystemInit  /* 把 SystemInit 函数的地址 加载到 R0 寄存器 */
            
                            BLX     R0               /* 调用执行 SystemInit() 函数 */
            
                            LDR     R0, =__main      /* 把 main 函数地址加载到 R0 */
            
                            BX      R0               /* 跳转到 main() 函数!进入C语言世界 */
            
                            ENDP                     /* 函数结束 */
      • ② 第二个运行:system_stm32f4xx.c 里的 SystemInit ()

        • system_stm32f4xx.c 就干1件事:实现 SystemInit () 配置系统时钟
        • 它是 C 语言第一个运行的函数,是整个芯片能跑起来的底层基石。
          • 执行 SystemInit ()
            • 开启 FPU 浮点单元(让 CPU 支持硬件浮点运算)

            • 复位所有时钟寄存器(安全初始状态(仅 HSI 16MHz 运行))

            • 初始化外部存储器(外部 SRAM/SDRAM(可选,极少用到

            • 配置系统时钟(SetSysClock 核心步骤)

              • 配置 FLASH 等待周期(CPU 等 5 个时周期,才读 Flash 指令 / 数据)
              • 配置总线分频(AHB / APB1 / APB2)
              • 开启 HSE 外部晶振
              • 配置 PLL 倍频(从低速 → 倍频到 168MHz)
              • 切换系统时钟源为 PLL
              • 更新 SystemCoreClock 全局时钟变量
          • 配置中断向量表的存放位置(告诉 CPU:中断表在 Flash 中的地址)

          • 注意了:HAL库 和 标准库 SystemInit () 函数实现 不一样

            • 前面讲的是标准库的,而下面才是HAL库的
              • SystemInit 只做两件事:开启 FPU、配置向量表
              • 时钟复位、配置全由 用户自己实现(SystemClock_Config() 来完成)
              • 导致 执行完 SystemInit,依旧跑在16MHz HSI(内部慢时钟)

          • 总结:

            •     库 SystemInit 做了什么? 进 main 时时钟 你需要手动配时钟吗?
              标准库 完整配置 PLL 168MHz 168MHz 不需要,自动好了
              HAL 库 仅复位,不配置 PLL 16MHz HSI 必须手动些函数 SystemClock_Config ()
      • ③ 第三个运行:main () 

        • SystemInit 执行完 → 跳去 main ()

          • 然后才用到:

            • 内核寄存器.c
            • 外设寄存器.c
            • hal 外设库.c
      •  ④ 官方 标准库 和 HAL库 SystemInit 函数对比
        • 标准库
          void SystemInit(void)
          {
            // ===================== 1. 开启硬件浮点单元 FPU =====================
            // 如果芯片支持FPU,并且开启了硬件浮点
            // 给CP10、CP11权限,让CPU可以执行浮点指令(不开启则浮点运算巨慢)
            #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
              SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
            #endif
          
            // ===================== 2. 复位所有时钟 → 最干净、最安全的初始状态 =====================
            // 打开内部16MHz时钟 HSI(上电默认时钟,最可靠)
            RCC->CR |= (uint32_t)0x00000001;
          
            // 复位时钟配置寄存器 CFGR(清空所有分频、时钟源选择)
            RCC->CFGR = 0x00000000;
          
            // 关闭 HSE外部晶振、PLL锁相环、时钟安全系统
            RCC->CR &= (uint32_t)0xFEF6FFFF;
          
            // 复位PLL配置寄存器(清空倍频、分频)
            RCC->PLLCFGR = 0x24003010;
          
            // 关闭外部晶振旁路模式
            RCC->CR &= (uint32_t)0xFFFBFFFF;
          
            // 关闭所有时钟中断,防止干扰
            RCC->CIR = 0x00000000;
          
            // ===================== 3. 外部RAM初始化(几乎不用,忽略) =====================
          #if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM)
            SystemInit_ExtMemCtl(); 
          #endif
          
            // ===================== 4. 核心:配置系统时钟到168MHz =====================
            // 里面完成:
            // 1. 打开HSE外部晶振
            // 2. 配置PLL倍频到168MHz
            // 3. 配置FLASH等待周期(CPU等Flash)
            // 4. 配置AHB/APB1/APB2总线分频
            // 5. 切换系统时钟为PLL
            // 6. 更新全局时钟变量 SystemCoreClock
            SetSysClock();
          
            // ===================== 5. 配置中断向量表的存放位置 =====================
            // 告诉CPU:中断函数列表放在哪里
          #ifdef VECT_TAB_SRAM
            // 放在SRAM(极少用)
            SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
          #else
            // 放在FLASH(正常程序都放这里)
            SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
          #endif
          }
        • HAL库
          // 系统初始化函数(芯片上电后自动第一个执行)
          void SystemInit(void)
          {
            // ==================== 1. 硬件浮点单元 FPU 配置 ====================
            // 如果芯片硬件支持 FPU,并且工程开启了硬件浮点计算
            // 给 CP10 / CP11 完全权限,让 CPU 可以执行浮点运算指令
            #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
              SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));
            #endif
          
            // ==================== 2. 外部存储器初始化(几乎不用) ====================
            // 只有使用外部 SRAM 或 SDRAM 时才会执行
            // 普通单片机项目 99.9% 用不到,直接忽略
          #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
            SystemInit_ExtMemCtl(); 
          #endif
          
            // ==================== 3. 配置中断向量表的存放位置 ====================
            // 如果用户自定义了中断向量表地址(如Bootloader跳转)
            // 告诉CPU:中断函数列表放在哪个内存地址(Flash 或 SRAM)
          #if defined(USER_VECT_TAB_ADDRESS)
            SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
          #endif
          }

    四、标准 \ HAL库 最小模板

    • 1. 标准库最小模板

      • 执行顺序:启动文件.s → SystemInit () → main () → 开时钟 → 初始化外设
        • #include "stm32f4xx.h"
          
          int main(void)
          {
              // 【标准库 唯一必须:先开时钟!】
              RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
              
              // 然后配置 GPIO
              GPIO_InitTypeDef GPIO_InitStruct;
              GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
              GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
              GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
              GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
              GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
              GPIO_Init(GPIOA, &GPIO_InitStruct);
          
              while(1)
              {
                  // 业务代码
              }
          }
          

    • 2. HAL 库最小模板

      • 执行顺序:启动.s → SystemInit () → main () → HAL_Init () → 配置时钟 → 初始化外设
        #include "stm32f4xx_hal.h"
        
        int main(void)
        {
            // 【HAL 库 必须第一个调用!】
            HAL_Init();
            
            // 【必须:配置系统时钟】
            SystemClock_Config();
            
            // 然后初始化外设(HAL 会自动开时钟)
            __HAL_RCC_GPIOA_CLK_ENABLE();   // 这句也可以在 MX_GPIO_Init 里
            
            GPIO_InitTypeDef GPIO_InitStruct = {0};
            GPIO_InitStruct.Pin = GPIO_PIN_0;
            GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        
            while(1)
            {
                // 业务代码
            }
        }
        
        // 时钟配置函数(必须)
        void SystemClock_Config(void)
        {
            // 内部实现:配置 PLL 到 168MHz
        }
        
      • HAL_Init () 做的 4 件大事

        • 1. 设置中断优先级分组

          • 配置整个芯片的中断规则(NVIC 分组)

          • 保证后续中断(时钟、外设、定时器)不乱优先级

          • 是系统稳定运行的基础

        • 2. 初始化 SysTick 系统滴答定时器

          • 提供 1ms 系统时基

          • HAL_Delay()、超时判断、时间戳都依赖它

          • 不初始化 → HAL 延时功能完全失效

        • 3. 开启 Flash 缓存加速

          • 指令缓存、数据缓存、预取指功能打开

          • 让 CPU 在 ** 高主频(168MHz)** 下读取代码更快、不卡顿

        • 4. 调用底层硬件初始化接口 HAL_MspInit()

          • 这是一个弱定义空函数

          • 需要用户自己实现

            • 真正在这里面做:

              • 复位所有外设(APB1/APB2)

              • 开启底层时钟(PWR 等)

              • 让系统进入干净、安全、默认状态


    • 3. 两者最核心区别

      • 步骤

        标准库

        HAL 库

        上电

        启动文件.s

        启动文件.s

        C 入口

        SystemInit()

        SystemInit()

        main 第一句

        直接开外设时钟

        必须 HAL_Init ()

        时钟

        由 SystemInit 配置

        (更改HSE,需用户实现)

        必须手动调用 SystemClock_Config

        (选用HSI还是HSE,用户一次性实现)

        外设使用

        先开时钟,再初始化

        直接初始化(库自动处理时钟)

        统一初始化

        必须 HAL_Init ()

    五、标准 \ HAL库 自定义系统时钟

    •  标准库:
      // 功能:标准库 —— 系统时钟初始化(HSE 25MHz → 系统时钟 168MHz)
      void SystemClock_Config(void)
      {
          // 1. 复位时钟
          RCC_DeInit();
      
          // 2. 使能 PWR 电源时钟(跑168MHz必须开)
          // 默认电压等级 2(默认) 最高支持:144MHz
          RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
          PWR_VoltageScalingConfig(PWR_VoltageScaling_1);
      
          // 3. 开启 25MHz 外部晶振
          RCC_HSEConfig(RCC_HSE_ON);
          while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
      
          //=========================================================================
          // 【重点】25MHz 晶振 PLL 配置
          // 公式:SYSCLK = HSE / PLLM * PLLN / PLLP
          // 25MHz / 25 * 336 / 2 = 168MHz
          //=========================================================================
          RCC_PLLConfig(RCC_PLLSource_HSE, 25, 336, 2, 4);
      
          // 4. 开启 PLL 并等待锁定
          RCC_PLLCmd(ENABLE);
          while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
      
          // 5. Flash 完整配置(预取 + 指令缓存 + 数据缓存 + 5个等待周期)
          FLASH_PrefetchBufferCmd(ENABLE);
          FLASH_InstructionCacheCmd(ENABLE);
          FLASH_DataCacheCmd(ENABLE);
          FLASH_SetLatency(FLASH_Latency_5);
      
          // 6. 总线分频配置
          RCC_HCLKConfig(RCC_SYSCLK_Div1);        // AHB  = 168MHz
          RCC_PCLK1Config(RCC_HCLK_Div4);         // APB1 = 42MHz
          RCC_PCLK2Config(RCC_HCLK_Div2);         // APB2 = 84MHz
      
          // 7. 切换时钟到 PLL
          RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
          while (RCC_GetSYSCLKSource() != 0x08);
      
          // 8. 更新全局时钟变量(必须!)
          SystemCoreClockUpdate();
      }
    • HAL库:
      // 系统时钟配置函数(必须在 HAL_Init() 之后、main 函数里调用)
      void SystemClock_Config(void)
      {
        // 定义两个时钟配置结构体,清零初始值
        RCC_OscInitTypeDef RCC_OscInitStruct = {0};  // 用于配置振荡器(HSE/HSI/PLL)
        RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};  // 用于配置总线时钟(SYSCLK/AHB/APB)
      
        //==================== 1. 电源配置 ====================
        // 使能电源时钟(必须开,否则无法配置电压等级)
        __HAL_RCC_PWR_CLK_ENABLE();
        
        // 设置电源调压器为 1 档(最高性能,支持 180MHz)
        // STM32 主频越高,所需内核电压就越高,若默认低档电压,强行配 180MHz:芯片不稳定、死机、跑飞
        __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
      
        //==================== 2. 配置 HSE + PLL ====================
        // 选择要配置的振荡器类型(我要使用 HSE,给外部晶振通电、启动):HSE
        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
        
        // 打开外部高速晶振 HSE(你的 8M/25M 晶振)
        RCC_OscInitStruct.HSEState = RCC_HSE_ON;
        
        // 打开 PLL(锁相环,用来倍频到 180MHz)
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
        
        // PLL 时钟来源:选择 HSE
        RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      
        //==================== PLL 计算公式 ====================
        // SYSCLK = HSE / PLLM * PLLN / PLLP
        // 例:25MHz /25 *360 /2 = 180MHz
        // 如果你是 8MHz 晶振:PLLM=8, PLLN=360, PLLP=2 → 8/8*360/2=180MHz
        RCC_OscInitStruct.PLL.PLLM = 25;    // HSE 分频系数
        RCC_OscInitStruct.PLL.PLLN = 360;   // 倍频系数
        RCC_OscInitStruct.PLL.PLLP = 2;     // 最终分频系数(输出系统时钟)
        RCC_OscInitStruct.PLL.PLLQ = 7;     // USB/SDIO 时钟用(可选)
      
        // 把上面配置写入硬件寄存器
        if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
        {
          Error_Handler();  // 配置失败进入错误处理
        }
      
        //==================== 3. 配置总线分频 ====================
        // 选择要配置的时钟:系统时钟 + AHB + APB1 + APB2
        RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                    |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
        
        // 系统时钟来源:PLL(180MHz)
        RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
        
        // AHB 总线分频:1 分频 → HCLK = 180MHz(CPU 主频)
        RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
        
        // APB1 总线分频:4 分频 → 45MHz(APB1 最高不能超过 42MHz,F4 可超频到 45)
        RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
        
        // APB2 总线分频:2 分频 → 90MHz(APB2 最高可到 90MHz)
        RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
      
        // 把总线配置写入硬件,并设置 Flash 等待周期(5 个周期对应 180MHz)
        if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5WS) != HAL_OK)
        {
          Error_Handler();  // 配置失败进入错误处理
        }
      
        // ========= 4. 更新全局时钟变量 (SystemCoreClockUpdate 标准库自带)========
        SystemCoreClockUpdate();   //告诉所有库和外设:现在 CPU 真正跑的速度是 168MHz
      
      }
    • 更新全局时钟变量(库函数自带)
      • SystemCoreClockUpdate():动态计算(读寄存器→ 判断 HSI/HSE/PLL→ 套公式→ 计算)
        void SystemCoreClockUpdate(void)
        {
          // 先定义几个计算器变量
          uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
        
          /*!!!第一步:去硬件里看一眼 —— 现在CPU用的是谁的时钟?!*/
          tmp = RCC->CFGR & RCC_CFGR_SWS;
        
          // 根据时钟来源,分 4 种情况计算速度
          switch (tmp)
          {
            // ==========================
            // 情况1:用内部16M时钟 HSI
            // ==========================
            case 0x00:
              SystemCoreClock = 16000000; // 直接写死 16MHz
              break;
        
            // ==========================
            // 情况2:用外部晶振 HSE
            // ==========================
            case 0x04:
              SystemCoreClock = HSE_VALUE; // 直接用你配置的 8M/25M
              break;
        
            // ==========================
            // 情况3:用 PLL 倍频时钟(你现在168MHz就是这种!)
            // ==========================
            case 0x08:
        
              // 1. 看 PLL 用的是 HSI 还是 HSE
              pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
        
              // 2. 读 PLL_M 分频值
              pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
        
              // 3. 如果是 HSE 做时钟源
              if (pllsource != 0)
              {
                  // 计算公式:
                  // PLL_VCO = HSE / M * N
                  pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
              }
              else
              {
                  // 用 HSI 内部时钟算
                  pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
              }
        
              // 4. 读 PLL_P 分频
              pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
        
              // 5. 最终算出系统时钟!
              // SYSCLK = PLL_VCO / P
              SystemCoreClock = pllvco / pllp;
              break;
        
            // 其他情况...
            default:
              SystemCoreClock = HSI_VALUE;
              break;
          }
        
          // 最后再算一下 AHB 分频,得到最终 CPU 速度
          tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
          SystemCoreClock >>= tmp;
        }

    Logo

    AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

    更多推荐