Clock Configuration For STM32F4

Implementation Device: Discovery kit with STM32F411VE MCU.

This clock configuration tutorial is based on Clock Configuration of STM32CubeMX Configuration GUI. On the configuration GUI, we follow the clock setting sample illustrated in Fig. 52. The configuration process follows the SystemClock_Config function (generated by STM32CubeMX Code Generator). Configuration steps are as follows:

  1. Configure the main internal regulator output voltage.

  2. Configure Flash prefetch, Instruction cache, Data cache.

  3. Initializes the RCC Oscillators.

  4. Initializes the CPU, AHB and APB buses clocks.

 1void SystemClock_Config(void)
 2{
 3RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 4RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 5
 6/** Configure the main internal regulator output voltage
 7*/
 8__HAL_RCC_PWR_CLK_ENABLE();
 9__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
10
11/** Initializes the RCC Oscillators according to the specified parameters
12* in the RCC_OscInitTypeDef structure.
13*/
14RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
15RCC_OscInitStruct.HSIState = RCC_HSI_ON;
16RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
17RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
18RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
19RCC_OscInitStruct.PLL.PLLM = 8;
20RCC_OscInitStruct.PLL.PLLN = 64;
21RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
22RCC_OscInitStruct.PLL.PLLQ = 4;
23if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
24{
25    Error_Handler();
26}
27
28/** Initializes the CPU, AHB and APB buses clocks
29*/
30RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
31                            |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
32RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
33RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV8;
34RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
35RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
36
37if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
38{
39    Error_Handler();
40}
41HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_1);
42}
../_images/Screenshot_2024-02-03_165901.png

Fig. 52 Clock Configuration Sample on STM32CubeIDDE Configuration GUI.

../_images/Screenshot_2024-02-08_184624.png

Fig. 53 Parameter Settings tab of STM32CubeMX.

1. Configure the main internal regulator output voltage

Power interface clock enable

Based in the RCC_APB1ENR structure (illustrated in Fig. 54), we can enable the power interface clock by set the 28th bit of the RCC_APB1ENR register.

../_images/RCC_APB1ENR_PWREN.png

Fig. 54 RCC APB1 peripheral clock enable register. (Source: [[STM32_RM0383], page 118])

// Power interface clock enable
RCC->APB1ENR |= (1 << 28);

Regulator voltage scaling output selection

Based on the Parameter Settings tab of RCC section of STM32CubeMX (illustrated in Fig. 55), Power Regulator Voltage Scale is Scale 1.

../_images/power_regulator_voltage_scale_1.png

Fig. 55 Parameter Settings tab of STM32CubeMX.

According to the PWR_CR (illustrated in Fig. 56), we can enable the Scale 1 mode by setting 0b11 value for the Bits 15:14.

../_images/PWR_CR_VOS.png

Fig. 56 PWR power control register. (Source: [[STM32_RM0383], page 86])

//  Regulator voltage scaling output selection
PWR->CR |= (0b11 << 14); // Scale 1 mode <= 100 MHz

2. Configure Flash prefetch, Instruction cache, Data cache

Based on the following HAL_Init function (in stm32f4xx_hal.c), we should enable the Flash prefetch, Instruction cache, Data cache. According to the

 1HAL_StatusTypeDef HAL_Init(void)
 2{
 3    /* Configure Flash prefetch, Instruction cache, Data cache */
 4#if (INSTRUCTION_CACHE_ENABLE != 0U)
 5    __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
 6#endif /* INSTRUCTION_CACHE_ENABLE */
 7
 8#if (DATA_CACHE_ENABLE != 0U)
 9    __HAL_FLASH_DATA_CACHE_ENABLE();
10#endif /* DATA_CACHE_ENABLE */
11
12#if (PREFETCH_ENABLE != 0U)
13    __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
14#endif /* PREFETCH_ENABLE */
15
16    /* Set Interrupt Group Priority */
17    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
18
19    /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
20    HAL_InitTick(TICK_INT_PRIORITY);
21
22    /* Init the low level hardware */
23    HAL_MspInit();
24
25    /* Return function status */
26    return HAL_OK;
27}

According to the FLASH_ACR (illustrated in Fig. 57), we can set the Bits 10:8 to respectively enable Prefetch, Data cache, and Instruction cache. Also based on this register, we can configure the Flash Latency is 0 WS by resetting the Bits 0:3 of the FLASH_ACR, following Fig. 53.

../_images/FLASH_ACR_LATENCY.png

Fig. 57 PWR power control register. (Source: [[STM32_RM0383], page 59])

FLASH->ACR |= (0b111 << 8); // Instruction cache, Data cache, Prefetch enable
FLASH->ACR &= ~(0b1111 << 0); // Zero wait states

3. Initializes the RCC Oscillators

HSI Configuration

Following the HAL_RCC_OscConfig function (in stm32f4xx_hal_rcc.c), we just have to adjust the HSI calibration value. However, to ensure the HSI enable, we can set the 0th bit and check the 1th bit of RCC_CR (illustrated in Fig. 58).

__weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef  *RCC_OscInitStruct)
{
    // ...
    /*----------------------------- HSI Configuration --------------------------*/
    if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI) == RCC_OSCILLATORTYPE_HSI)
    {
        /* Check the parameters */
        assert_param(IS_RCC_HSI(RCC_OscInitStruct->HSIState));
        assert_param(IS_RCC_CALIBRATION_VALUE(RCC_OscInitStruct->HSICalibrationValue));

        /* Check if HSI is used as system clock or as PLL source when PLL is selected as system clock */
        if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSI) ||\
        ((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSI)))
        {
        /* When HSI is used as system clock it will not disabled */
        if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) != RESET) && (RCC_OscInitStruct->HSIState != RCC_HSI_ON))
        {
            return HAL_ERROR;
        }
        /* Otherwise, just the calibration is allowed */
        else
        {
            /* Adjusts the Internal High Speed oscillator (HSI) calibration value.*/
            __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue);
        }
        }
    // ...
}

Following Fig. 53, we adjust 16 to the HSITRIM, based on HSI Calibration Value in Fig. 53.

../_images/Screenshot_2024-02-09_114407.png

Fig. 58 RCC clock control register

// Internal high-speed clock enable
RCC->CR |= (1 << 0); // HSI oscillator ON

// Internal high-speed clock ready flag
while(!(RCC->CR & (1 << 1))); // wait until HSI enable

// Internal high-speed clock trimming
RCC->CR &= ~(0b11111 << 3); // clear 5 bits of HSITRIM
RCC->CR |= (0b10000 << 3); // HSITRIM is 16

PLL Configuration

According to Code Block 4, the PLL Configuration is as follows:

  1. Disable the main PLL and wait till PLL is disabled.

  2. Configure the main PLL clock source, multiplication and division factors.

  3. Enable the main PLL and wait till PLL is ready.

Code Block 4 PLL Configuration in HAL_RCC_OscConfig function of stm32f4xx_hal_rcc.c.
/*-------------------------------- PLL Configuration -----------------------*/
/* Check the parameters */
assert_param(IS_RCC_PLL(RCC_OscInitStruct->PLL.PLLState));
if ((RCC_OscInitStruct->PLL.PLLState) != RCC_PLL_NONE)
{
/* Check if the PLL is used as system clock or not */
if(__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_PLL)
{
    if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_ON)
    {
    /* Check the parameters */
    assert_param(IS_RCC_PLLSOURCE(RCC_OscInitStruct->PLL.PLLSource));
    assert_param(IS_RCC_PLLM_VALUE(RCC_OscInitStruct->PLL.PLLM));
    assert_param(IS_RCC_PLLN_VALUE(RCC_OscInitStruct->PLL.PLLN));
    assert_param(IS_RCC_PLLP_VALUE(RCC_OscInitStruct->PLL.PLLP));
    assert_param(IS_RCC_PLLQ_VALUE(RCC_OscInitStruct->PLL.PLLQ));

    /* Disable the main PLL. */
    __HAL_RCC_PLL_DISABLE();

    /* Get Start Tick */
    tickstart = HAL_GetTick();

    /* Wait till PLL is disabled */
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)
    {
        if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
        {
        return HAL_TIMEOUT;
        }
    }

    /* Configure the main PLL clock source, multiplication and division factors. */
    WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource                                            | \
                                RCC_OscInitStruct->PLL.PLLM                                                 | \
                                (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos)             | \
                                (((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U) << RCC_PLLCFGR_PLLP_Pos) | \
                                (RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)));
    /* Enable the main PLL. */
    __HAL_RCC_PLL_ENABLE();

    /* Get Start Tick */
    tickstart = HAL_GetTick();

    /* Wait till PLL is ready */
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
    {
        if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
        {
        return HAL_TIMEOUT;
        }
    }
    }

Disable the main PLL and wait till PLL is disabled

Based on the RCC_CR (illustrated in Fig. 59), we can reset the 24th bit to disable PLL and use the 25th bit to check current PLL enable status.

../_images/RCC_CR_PLL_ON_RDY.png

Fig. 59 RCC clock control register.

// Main PLL (PLL) enable
RCC->CR &= ~(1 << 24);// PLL OFF
// Main PLL (PLL) clock ready flag
while(RCC->CR & (1 << 25)); // wait until PLL off

Configure the main PLL clock source, multiplication and division factors.

According the setting of Fig. 52, we configure these following parameters:

  1. PLL source: HSI.

  2. PLLM = 8.

  3. PLLN = 64.

  4. PLLP = 2.

  5. PLLQ = 4.

Corresponding to the RCC_PLLCFGR (illustrated in Fig. 60), we set value for these following bits:

  1. Bit 22 = 0.

  2. Bits 5:0 = 8.

  3. Bits 14:6 = 64.

  4. Bits 17:16 = 2.

  5. Bits 27:24 = 4.

../_images/Screenshot_2024-02-09_150552.png

Fig. 60 RCC PLL configuration register

// Main PLL(PLL) and audio PLL (PLLI2S) entry clock source
RCC->PLLCFGR &= ~(1 << 22);// HSI clock selected as PLL and PLLI2S clock entry

// Division factor for the main PLL (PLL) input clock
RCC->PLLCFGR &= ~(0x3f << 0); // clear 6 bit of PLLM
RCC->PLLCFGR |= (8 << 0); //  PLLM=8

// Main PLL (PLL) multiplication factor for VCO
RCC->PLLCFGR &= ~(0x1ff << 6); // clear 9 bit of PLLN
RCC->PLLCFGR |= (64 << 6); //  PLLN=64

// Main PLL (PLL) division factor for main system clock
RCC->PLLCFGR &= ~(0x3 << 16); // clear 2 bit of PLLP
RCC->PLLCFGR |= (2 << 16); //  PLLP=2

// Main PLL (PLL) division factor for USB OTG FS, and SDIO clocks
RCC->PLLCFGR &= ~(0xf << 24); // clear 4 bit of PLLQ
RCC->PLLCFGR |= (4 << 24); //  PLLQ=4

Enable the main PLL and wait till PLL is ready.

Similar to disabling the PLL, instead, we just reset the 24th bit to enable PLL and use the 25th bit to check current PLL enable status.

// Main PLL (PLL) enable
RCC->CR |= (1 << 24);// PLL ON
// Main PLL (PLL) clock ready flag
while(!(RCC->CR & (1 << 25))); // wait until PLL ready

4. Initializes the CPU, AHB and APB buses clocks

../_images/RCC_CFGR_HPRE_SW_PPRE.png

Fig. 61 RCC clock configuration register

HCLK Configuration

Based on the Code Block 5, we can setup HCLK Configuration by the following steps:

  1. Set the highest APBx dividers.

  2. Configure the AHB Clock Divider.

Code Block 5 HCLK Configuration in HAL_RCC_OscConfig function of stm32f4xx_hal_rcc.c.
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef  *RCC_ClkInitStruct, uint32_t FLatency)
{
//...

    /*-------------------------- HCLK Configuration --------------------------*/
    if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
    {
    /* Set the highest APBx dividers in order to ensure that we do not go through
        a non-spec phase whatever we decrease or increase HCLK. */
    if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
    {
        MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
    }

    if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
    {
        MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
    }

    assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
    }

// ...
}

According the setting of Fig. 52, we configure these following parameters:

  1. AHB prescaler: system clock divided by 8

  2. APB1 and APB2 prescaler: AHB clock divided by 16

Corresponding to the RCC_CFGR (illustrated in Fig. 61), we respectively set value for these following bits:

  1. Bits 15:10 = 0x3f.

  2. Bits 7:4 = 0b1010.

// HCLK Configuration
/* Set the highest APBx dividers in order to ensure that we do not go through
        a non-spec phase whatever we decrease or increase HCLK. */
RCC->CFGR |= (0x3f << 10); // set bit 10 -> 15

// HPRE: AHB prescaler
//  RCC->CFGR &= ~(0b1111 << 4);// system clock not divided: system clock divided by 1
RCC->CFGR &= ~(0b1111 << 4); // clear 4 bit of AHB prescaler
RCC->CFGR |= (0b1010 << 4);// system clock divided by 8

SYSCLK Configuration

According to Code Block 6, we must set appropiate value for System clock switch.

Corresponding to the setting of Fig. 52, we select PLL as the System clock.

Code Block 6 SYSCLK Configuration in HAL_RCC_OscConfig function of stm32f4xx_hal_rcc.c.
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef  *RCC_ClkInitStruct, uint32_t FLatency)
{
//...

    __HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);

// ...
}

Following the RCC_CFGR (illustrated in Fig. 61), we adjust 0b10 to Bits 1:0 and check System clock switch status based on the Bits 3:2.

// System clock switch
RCC->CFGR &= ~(0b11 << 0); // clear 2 bit of SW
RCC->CFGR |= (0b10 << 0); // PLL selected as system clock

// System clock switch status
while((RCC->CFGR & (0b11 << 1)) != 0b10); // wait until PLL is used as the system clock

APBx Configuration

According to Code Block 6, we must set appropiate value for APBx prescaler.

Corresponding to the setting of Fig. 52, we configure these following parameters:

  1. APB Low speed prescaler (APB1): AHB clock not divided.

  2. APB high-speed prescaler (APB2): AHB clock not divided.

Code Block 7 APBx Configuration in HAL_RCC_OscConfig function of stm32f4xx_hal_rcc.c.
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef  *RCC_ClkInitStruct, uint32_t FLatency)
{
//...

    /*-------------------------- PCLK1 Configuration ---------------------------*/
    if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
    {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
    }

    /*-------------------------- PCLK2 Configuration ---------------------------*/
    if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
    {
    assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
    MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U));
    }

// ...
}

According to the RCC_CFGR (illustrated in Fig. 61), we respectively set value for these following bits:

  1. Bits 12:10 = 0b000.

  2. Bits 15:13 = 0b000.

// PCLK1 Configuration: APB Low speed prescaler (APB1)
RCC->CFGR &= ~(0b111 << 10); // AHB clock divided by 1

// PCLK2 Configuration: APB high-speed prescaler (APB2)
RCC->CFGR &= ~(0b111 << 13); // AHB clock divided by 1

Full Implementation Source

#include <stdint.h>
#include <stm32f4xx.h>

int main(void)
{
    // Power interface clock enable
    RCC->APB1ENR |= (1 << 28);

    //  Regulator voltage scaling output selection
    PWR->CR |= (0b11 << 14); // Scale 1 mode <= 100 MHz

    //
    FLASH->ACR |= (0b111 << 8); // Instruction cache, Data cache, Prefetch enable
    FLASH->ACR &= ~(0b1111 << 0); // Zero wait states


    //
    // Internal high-speed clock enable
    RCC->CR |= (1 << 0); // HSI oscillator ON

    // Internal high-speed clock ready flag
    while(!(RCC->CR & (1 << 1))); // wait until HSI enable

    // Internal high-speed clock trimming
    RCC->CR &= ~(0b11111 << 3); // clear 5 bits of HSITRIM
    RCC->CR |= (0b10000 << 3); // HSITRIM is 16


    //
    // Off PLL for configuration
    // Main PLL (PLL) enable
    RCC->CR &= ~(1 << 24);// PLL OFF
    // Main PLL (PLL) clock ready flag
    while(RCC->CR & (1 << 25)); // wait until PLL off

    // Main PLL(PLL) and audio PLL (PLLI2S) entry clock source
    RCC->PLLCFGR &= ~(1 << 22);// HSI clock selected as PLL and PLLI2S clock entry

    // Division factor for the main PLL (PLL) input clock
    RCC->PLLCFGR &= ~(0x3f << 0); // clear 6 bit of PLLM
    RCC->PLLCFGR |= (8 << 0); //  PLLM=8

    // Main PLL (PLL) multiplication factor for VCO
    RCC->PLLCFGR &= ~(0x1ff << 6); // clear 9 bit of PLLN
    RCC->PLLCFGR |= (64 << 6); //  PLLN=64

    // Main PLL (PLL) division factor for main system clock
    RCC->PLLCFGR &= ~(0x3 << 16); // clear 2 bit of PLLP
    RCC->PLLCFGR |= (2 << 16); //  PLLP=2

    // Main PLL (PLL) division factor for USB OTG FS, and SDIO clocks
    RCC->PLLCFGR &= ~(0xf << 24); // clear 4 bit of PLLQ
    RCC->PLLCFGR |= (4 << 24); //  PLLQ=4

    //
    // On PLL after configuration complete
    // Main PLL (PLL) enable
    RCC->CR |= (1 << 24);// PLL ON
    // Main PLL (PLL) clock ready flag
    while(!(RCC->CR & (1 << 25))); // wait until PLL ready

    //
    // HCLK Configuration
    //PPRE1: APB Low speed prescaler (APB1)

    /* Set the highest APBx dividers in order to ensure that we do not go through
            a non-spec phase whatever we decrease or increase HCLK. */
    RCC->CFGR |= (0x3f << 10); // set bit 10 -> 15

    // HPRE: AHB prescaler
    //      RCC->CFGR &= ~(0b1111 << 4);// system clock not divided: system clock divided by 1
    RCC->CFGR &= ~(0b1111 << 4); // clear 4 bit of AHB prescaler
    RCC->CFGR |= (0b1010 << 4);// system clock divided by 8

    //
    // System clock switch
    RCC->CFGR &= ~(0b11 << 0); // clear 2 bit of SW
    RCC->CFGR |= (0b10 << 0); // PLL selected as system clock

    // System clock switch status
    while((RCC->CFGR & (0b11 << 1)) != 0b10); // wait until PLL is used as the system clock

    //
    // PCLK1 Configuration: APB Low speed prescaler (APB1)
    RCC->CFGR &= ~(0b111 << 10); // AHB clock divided by 1

    // PCLK2 Configuration: APB high-speed prescaler (APB2)
    RCC->CFGR &= ~(0b111 << 13); // AHB clock divided by 1
    /* Loop forever */
    for(;;);
}

Reference

STM32 Clock Setup using Registers » ControllersTech