'Code for increasing clock speed of STM32F401 not working

I was trying to increase the clock speed of STM32F401 to 84Mhz using PLL. I tried for log time. But the code is not working. Can anyone check what needs to be done? External crystals is functional , because I checked that using HAL library.

The reason I am trying without HAL is that I've seen several document saying that the HAL consumes too much memory and it is better to avoid it.

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

This is the clock configuration in CubeMX:

Clock configuration in CubeMX



Solution 1:[1]

Although the question is still missing some information, let me try to give you some pointers:

Writing to MCU registers

First of all, writing to the PLLCFGR register in many small steps is generally not the way to go. See the following piece of your code:

//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]

This performs 9 writes to the same register. In the first write, you select the HSE oscillator clock. In the second write, you clear the division factor for the main PLL (PLLM). However, the value 0 is an invalid value. This might already cause issues (I don't think the behavior in this case is specified, so we can only guess what will happen).

This can be easily avoid by first calculating the correct value and then writing it to the register in a single operation like this:

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;

The same applies as well to other registers in your code.

Dynamically reconfiguring clocks

It's not entirely clear to me if you want to increase the clock speed of the MCU dynamically (so when it's already running) or statically (so changing the initial configuration).

In case you want to change the clock speed dynamically, there a few additional things to consider:

  • The Main PLL configuration cannot be changed once the PLL is enabled. So if you want to change the clock speed, you will first have to disable it.
  • You have to make sure that there is at least one valid system clock at all times. If you're reconfiguring the Main PLL, you will have to temporarily switch to the internal clock (HSI).

Since I don't know your startup clock configuration, I can't go into more details, but I hope this will provide enough information.

Flash wait states

As mentioned by old_timer and Tagli, you might also need to adjust the flash wait states. This is described in section 3.4.1 "Relation between CPU clock frequency and Flash memory read time" of the STM32F401 Reference Manual (RM0368) (revision 5). The required value depends on your supply voltage and CPU clock frequency.

Power Regulator Voltage

To be able to achieve 84 MHz on the STM32F4, the regulator voltage scaling output must be set to Scale 2 (VOS = 2 in the PWR_CR register). This is the default after reset, but if your initial configuration was different, you may have to set it again.


NB: Regarding your statement "The reason I am trying without HAL is that I've seen several document saying that the HAL consumes too much memory and it is better to avoid it."... There's a valid point in that statement, but it's not a general rule that you should follow. For low-level stuff like clock configuration, the HAL code is pretty basic and won't cost that much memory. And given the complexity of it, I wouldn't bother rewriting this, unless I really needed to save every byte of memory. The high-level HAL functions, e.g. for UART or I2C, are pretty big, since they try to cover many use cases, and you might save quite some memory by writing only the code you need. Still, I'd start with the HAL functions and only optimize when needed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1