提高STM32F401时钟速度的代码不起作用

Code for increasing clock speed of STM32F401 not working

我试图使用 PLL 将 STM32F401 的时钟速度提高到 84Mhz。我试过记录时间。但是代码不起作用。任何人都可以检查需要做什么吗?外部晶体可以正常工作,因为我使用 HAL 库进行了检查。

我尝试不使用 HAL 的原因是我看到几个文档说 HAL 消耗太多内存,最好避免它。

void Enable_PLL_F401(void)
{
    //Enable The HSE
    RCC->CR |= RCC_CR_HSEON;
    //Wait Until HSE Stabilizes
    //Remove While Loop; If the HSE isn't stabilized, code will stuck
    while(!(RCC->CR & RCC_CR_HSERDY))
        ;
    /* Activate Prefetch Buffer*/
    /*        Optional         */
    
    
    //Configure the PLL registers
    RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC;  //PLL Source HSE
    RCC->PLLCFGR &=~ (RCC_PLLCFGR_PLLM); //Clearing PLLM values
    RCC->PLLCFGR |= (RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4);  //Divided by 25
    RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLN;   //Clearing PLLM values
    RCC->PLLCFGR |= (RCC_PLLCFGR_PLLN_4|RCC_PLLCFGR_PLLN_6|RCC_PLLCFGR_PLLN_8);      //Multiplied by 336
    RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLP;   //Clearing PLLP values
    RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;  //Divided by 4
    RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLQ;   //Clearing PLLP values
    RCC->PLLCFGR |= RCC_PLLCFGR_PLLQ_2;  //Divided by 4 [USB-OTG Clock]
    
    RCC->CFGR &=~ RCC_CFGR_HPRE;         //System Clock Not Divided [AHB Prescalar]
    RCC->CFGR &=~ RCC_CFGR_PPRE1_DIV16;  //Clearing PPRE1 values
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
    RCC->CFGR &=~ RCC_CFGR_PPRE2_DIV16;  //Clearing PPRE2 Values
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;    //AHB not divided [APB2 Prescalar]
    
    RCC->CR |= RCC_CR_PLLON;
    //Wait Until HSE Stabilizes
    //Remove While Loop; If the HSE isn't stabilized, code will stuck
    while(!(RCC->CR & RCC_CR_PLLRDY))
        ;
    
    RCC->CFGR &=~ RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    
    while(!(RCC->CFGR & RCC_CFGR_SWS_PLL))
        ;
    
    //SystemInit();
    SystemCoreClockUpdate();
}

这是CubeMX中的时钟配置:

虽然问题还缺少一些信息,但还是让我试着给你一些指点:

写入MCU寄存器

首先,以许多小步骤写入 PLLCFGR 寄存器通常不是可行的方法。请参阅以下代码片段:

//Configure the PLL registers
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC;  //PLL Source HSE
RCC->PLLCFGR &=~ (RCC_PLLCFGR_PLLM); //Clearing PLLM values
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4);  //Divided by 25
RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLN;   //Clearing PLLM values
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLN_4|RCC_PLLCFGR_PLLN_6|RCC_PLLCFGR_PLLN_8);      //Multiplied by 336
RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLP;   //Clearing PLLP values
RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;  //Divided by 4
RCC->PLLCFGR &=~ RCC_PLLCFGR_PLLQ;   //Clearing PLLP values
RCC->PLLCFGR |= RCC_PLLCFGR_PLLQ_2;  //Divided by 4 [USB-OTG Clock]

这对同一个寄存器执行了 9 次写入。在第一次写入时,您 select HSE 振荡器时钟。在第二次写入中,您清除主 PLL (PLLM) 的分频因子。但是,值 0 是无效值。这可能已经引起了问题(我不认为这种情况下的行为是指定的,所以我们只能猜测会发生什么)。

这可以很容易地避免,方法是先计算出正确的值,然后像这样在一次操作中将其写入寄存器:

uint32_t reg = RCC->PLLCFGR;
//Configure the PLL registers
reg |= RCC_PLLCFGR_PLLSRC;  //PLL Source HSE
reg &=~ (RCC_PLLCFGR_PLLM); //Clearing PLLM values
reg |= (RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4);  //Divided by 25
reg &=~ RCC_PLLCFGR_PLLN;   //Clearing PLLM values
reg |= (RCC_PLLCFGR_PLLN_4|RCC_PLLCFGR_PLLN_6|RCC_PLLCFGR_PLLN_8);      //Multiplied by 336
reg &=~ RCC_PLLCFGR_PLLP;   //Clearing PLLP values
reg |= RCC_PLLCFGR_PLLP_0;  //Divided by 4
reg &=~ RCC_PLLCFGR_PLLQ;   //Clearing PLLP values
reg |= RCC_PLLCFGR_PLLQ_2;  //Divided by 4 [USB-OTG Clock]
RCC->PLLCFGR = reg;

这同样适用于代码中的其他寄存器。

动态重新配置时钟

我不太清楚你是想动态地提高MCU的时钟速度(所以当它已经运行)还是静态(因此更改初始配置)。

如果您想动态更改时钟速度,还需要考虑一些其他事项:

  • 一旦启用 PLL,就无法更改主 PLL 配置。所以如果你想改变时钟速度,你首先必须禁用它。
  • 您必须确保始终至少有一个有效的系统时钟。如果您要重新配置主 PLL,则必须暂时切换到内部时钟 (HSI)。

由于我不知道你的启动时钟配置,我不能说得更详细,但我希望这能提供足够的信息。

闪存等待状态

如 old_timer 和 Tagli 所述,您可能还需要调整闪存等待状态。 这在 STM32F401 Reference Manual (RM0368)(修订版 5)的第 3.4.1 节“CPU 时钟频率与闪存读取时间 之间的关系”中进行了描述。所需的值取决于您的电源电压和 CPU 时钟频率。

功率调节器电压

为了能够在 STM32F4 上实现 84 MHz,稳压器电压缩放输出必须设置为缩放 2(PWR_CR 寄存器中的 VOS = 2)。这是重置后的默认设置,但如果您的初始配置不同,您可能需要重新设置。


注意:关于您的陈述“我在没有 HAL 的情况下尝试的原因是我看到几个文档说 HAL 消耗太多内存,最好避免它。"...该声明中有一个有效的观点,但这不是您应该遵循的一般规则。对于 low-level 时钟配置之类的东西,HAL 代码非常基本,不会占用那么多内存。考虑到它的复杂性,我不会费心重写它,除非我 真的 需要保存内存的每个字节。 high-level HAL 函数,例如对于 UART 或 I2C,它们相当大,因为它们试图涵盖许多用例,并且您可以通过仅编写所需的代码来节省相当多的内存。尽管如此,我还是会从 HAL 函数开始,只在需要时进行优化。