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:
Configure the main internal regulator output voltage.
Configure Flash prefetch, Instruction cache, Data cache.
Initializes the RCC Oscillators.
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}
Fig. 52 Clock Configuration Sample on STM32CubeIDDE Configuration GUI.
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.
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.
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.
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.
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.
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:
Disable the main PLL and wait till PLL is disabled.
Configure the main PLL clock source, multiplication and division factors.
Enable the main PLL and wait till PLL is ready.
/*-------------------------------- 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.
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:
PLL source: HSI.
PLLM = 8.
PLLN = 64.
PLLP = 2.
PLLQ = 4.
Corresponding to the RCC_PLLCFGR (illustrated in Fig. 60), we set value for these following bits:
Bit 22 = 0.
Bits 5:0 = 8.
Bits 14:6 = 64.
Bits 17:16 = 2.
Bits 27:24 = 4.
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
Fig. 61 RCC clock configuration register
HCLK Configuration
Based on the Code Block 5, we can setup HCLK Configuration by the following steps:
Set the highest APBx dividers.
Configure the AHB Clock Divider.
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:
AHB prescaler: system clock divided by 8
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:
Bits 15:10 = 0x3f.
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.
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:
APB Low speed prescaler (APB1): AHB clock not divided.
APB high-speed prescaler (APB2): AHB clock not divided.
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:
Bits 12:10 = 0b000.
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(;;);
}