STM32 Clock Setup using Registers

This is a new tutorial series, where we will be programming our beloved STM32 by manipulating the Registers, and none of the libraries like HAL or SPL or LL will be used.

This is the first and the most important tutorial in this series, and today we will learn how to setup the clock

As the clock setup is a very complicated process, For today’s tutorial alone, we need some info from the CubeMX. So follow along

  • open the CubeMX
  • select your controller
  • choose the external crystal (RCC)
  • set the maximum value in the HCLK, and hit enter
  • keep it opened, as we will be using it

Create a project in Keil, and select the microcontroller


Now we need to include some basic files in our project, so select the CMCIC core, and the Device Startup files as shown below


Add the main file in the project, and along with that i have also added the RccConfig.h and RccConfig.c files. These are the files where the code for the clock setup will be written, and we can include them as it is in our upcoming projects.


Let’s Start the setup

below are the steps shown for the clock setup

	/*************>>>>>>> STEPS FOLLOWED <<<<<<<<************
	
	1. ENABLE HSE and wait for the HSE to become Ready
	2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
	3. Configure the FLASH PREFETCH and the LATENCY Related Settings
	4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
	5. Configure the MAIN PLL
	6. Enable the PLL and wait for it to become ready
	7. Select the Clock Source and wait for it to be set
	
	********************************************************/

Let’s see all of them one by one

1. ENABLE HSE and wait for the HSE to become Ready

The HSE can be configured in the RCC_CR Register as you can see below

As you can see the 16th bit of this register can enable the HSE, and the 17th bit will set to 1, when the HSE is finally ready

RCC->CR |= 1<<16;  
while (!(RCC->CR & (1<<17)));

above, I have set the bit 16 to HIGH (to enable the HSE), and then I am waiting for the 17th bit to set HIGH (HSE to become Ready)


2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR

The Power Enable Clock can be set in the RCC_APB1ENR Register as shown in the picture below

In order to enable the Power, i need to set the bit 28 of the RCC_APB1ENR Register

RCC->APB1ENR |= 1<<28;

The Voltage Regulator can be configured in the PWR_CR Register as shown below

In the RCC setup of the CubeMX, the voltage Regulator is selected for scale 1 by default. And i will also use the same for my application. Basically I need to write a 3 (11) in the 14th bit

PWR->CR |= 3<<14; 

3. Configure the FLASH PREFETCH and the LATENCY Related Settings

The settings are as shown in the RCC setup from the CubeMX below

To configure them, we need to look in the FLASH_ACR Register

As shown in the CubeMX picture above, the Instruction Cache, Prefetch Buffer, and the Data Cache all are enabled. Also the Flash Latency is 5WS

So i need to write a 1 in the 8th, 9th and the 10th positions. Also a 5 in the 0th position (for 5WS)

FLASH->ACR = (1<<8) | (1<<9)| (1<<10)| (5<<0);

4. Configure the PRESCALARS HCLK, PCLK1, PCLK2

Here we will set the values for AHB Prescalar, APB1 Presclar, and APB2 Prescalar
The values are shown below (in RED color) in the Clock Setup from the CubeMX

To configure them, we will look into the RCC_CFGR Register

As shown in the CubeMX setup above

  • The AHB Prescalar is 1, so the HPRE (bits 7:4) will be all 0s.
  • APB1 Prescalar is 4, so the PPRE1 (bits 12:10) will be “101” i.e write 5 to the 10th position
  • APB2 Prescalar is 2, so the PPRE2 (bits 15:13) will be “100” i.e write 4 to the 13th position

// AHB PR
RCC->CFGR &= ~(1<<4);
	
// APB1 PR
RCC->CFGR |= (5<<10);
	
// APB2 PR
RCC->CFGR |= (4<<13);

5. Configure the MAIN PLL

Here we will configure the PLL source (HSE or HSI), and also set the values for PLLM, PLLN and PLLP
The vales are shown below (in the BLUE color)

The configuration can be set in the RCC_PLLCFGR Register

We can directly push the required values to the PLLM and PLLN Bits, while the care must be taken when setting the PLLP value.

  • The PLLP value, according to the CubeMX is 2, and to set it, I need to write 0s in the PLLP (Bits 17:16)
  • PLLM and PLLN values are 4 and 180, according to the CubeMX, and I will directly set them in the PLLM (Bits 5:0) and PLLN (Bits 14:6)
  • As I am using HSE crystal for the clock, I need to set the PLLSRC (Bit 22) to 1.

#define PLL_M 	4
#define PLL_N 	180
#define PLL_P 	0  // PLLP = 2

RCC->PLLCFGR = (PLL_M <<0) | (PLL_N << 6) | (PLL_P <<16) | (1<<22);

6. Enable the PLL and wait for it to become ready

The PLL can be enabled in the RCC_CR Register as shown below

To enable the PLL, we must write a 1 to the PLL ON (24th bit), and then we will wait for the PLL RDY (25th bit) to set, which will indicate that the PLL is ready.

RCC->CR |= (1<<24);
while (!(RCC->CR & (1<<25)));


7. Select the Clock Source and wait for it to be set

The clock source can be selected in the RCC_CFGR Register

  • SW[1:0] is used to set the system clock. Since I am using the PLL_P as the system clock, I will write a 2 (1:0) to the SW Bits
  • SWS[3:2] is used to monitor the status of the system clock. So here we will wait for these bits to indicate that the PLL_P has been set as the system clock (wait for the SWS bits to indicate 2 (1:0)).

RCC->CFGR |= (2<<0);
while (!(RCC->CFGR & (2<<2)));

That completes the setup process. The above setup will allow my controller to run at 180 MHz, as I did setup in the cubeMX



RESULT

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

28 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
steve
1 month ago

In your development board, the crystal is not connected in X3 and c33, c34 capacitors are not connected. I don’t understand how you used it as an external crystal.

ronak
3 months ago

void SwitchToHSI_1MHz(void) {

// // Enable HSI
// __HAL_RCC_HSI_ENABLE();
// printf(“HSI is Enabled \r\n”);

// // Wait for HSI to be ready
// while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET);
// printf(“HSI is READY. Proceeding with switch…\r\n”);

// Disable PLL (if enabled)
__HAL_RCC_PLL_DISABLE();
printf(“PLL is Disabled\r\n”);

// Configure HSI to run at 1 MHz (Divide by 16)
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV16; // Divide by 16 to get 1 MHz
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

printf(“Enabling HSI…\r\n”);
__HAL_RCC_HSI_ENABLE();
while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET); // Wait until HSI is ready
printf(“HSI is now enabled.\r\n”);

// Apply oscillator configuration
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
printf(“Oscillator configuration failed!\r\n”);

} else {
printf(“Oscillator configuration successful!\r\n”);


not working from here /////////////////////////////////////////////////////////////////

HAL_Delay(30);

// Switch System Clock to HSI (1 MHz)
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // Initialize structure to zero
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; // Set SYSCLK to HSI
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV16; // Divide HSI (16 MHz) by 16 → 1 MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // Keep APB1 clock unchanged

// Apply Clock Configuration
HAL_StatusTypeDef status = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
if (status != HAL_OK) {
printf(“Clock configuration failed! Error Code: %d\r\n”, status); // Include error code
} else {
printf(“Clock configuration Successful\r\n”);
}
}

John D.
6 months ago

At step 5. Configure the MAIN PLL ive noticed that you do not set RCC-PLLCFGR bit 22 to 1 so that the PLL is fed from HSE. Is there a reason for that or could it be that I am wrong or missed something ? THANKYOU.

John D.
Reply to  John D.
6 months ago

Never mind. It was there but i didnt see it. Thanks !

Chakra
9 months ago

Hi, I downloaded code and flash my Nucleo-F446RE board. The LED remains ON all the time, no blinking.
Further, I also commented in the program:- Pin OUTPUT and while loop (shown below). still the LED is ON. I am surprised. What could be the reason, if any one have encountered this ? thank you
// 2. Set the Pin as OUTPUT
//GPIOA->MODER |= (1<BSRR |= (1<BSRR |= ((1<<5) <<16); // Reset pin PA5
delay (20000000);
} */

Berat Can ÇEKİÇ
2 years ago

Hi Sir , i did already ask my question on the Youtube .. Im really stuck , when i wrote these codes – my card is STM32F4 Discovery Board , My CPU is STM32F407VET6- my PLL values doesnt changed
.
void my_clock_config()
{
/*************>>>>>>> STEPS FOLLOWED <<<<<<<<************
1. ENABLE HSE and wait for the HSE to become Ready
2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
3. Configure the FLASH PREFETCH and the LATENCY Related Settings
4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
5. Configure the MAIN PLL
6. Enable the PLL and wait for it to become ready
7. Select the Clock Source and wait for it to be set
********************************************************/

//1.Enable HSE and wait for the HSE become Ready

RCC->CR |= 1<<16;
while(!(RCC->CR & (1<<17)));

//2.Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR

RCC->APB1ENR |= 1<<28; //Power Enabled – internal powe-
PWR->CR |= 1<<14;  //VOS :1 = Scale 1 mode as is default

//3.Configure the FLASH PREFETCH and the LATENCY Related Settings

FLASH->ACR |= 1<<8;
FLASH->ACR |= 1<<9;
FLASH->ACR |= 1<<10;
FLASH->ACR |= 5<<0;

//4.Configure the PRESCALARS HCLK, PCLK1, PCLK2

RCC->CFGR &= ~(1<<4);
RCC->CFGR |= 5<<10;
RCC->CFGR |= 4<<13;

//5. Configure the MAIN PLL

RCC->PLLCFGR |= 4<<0;
RCC->PLLCFGR |= 168<<6;
RCC->PLLCFGR |= 0<<16;

RCC->PLLCFGR |= 1<<22;     //Choosing HSE for PLL source

//6. Enable the PLL and wait for it to become ready

RCC->CR |= 1<<24;
while(!(RCC->CR & (1<<25)));

//7. Select the Clock Source and wait for it to be set

RCC->CFGR |= 2<<0;
while(!(RCC->CFGR & (2<<2)));

}

Last edited 2 years ago by Berat Can ÇEKİÇ
Tuan Pham
Reply to  Berat Can ÇEKİÇ
1 year ago

Are you confused with the HSE for the PLL source?
I mean it should be PLL source: RCC->PLLCFGR |= 2<<22.

Charlie M
2 years ago

I’m getting quite distracted and swamped by all the ads here. Is it possible to download this tutorial presentation ?

asdf2
2 years ago

please tell me how could I know that the clock needs to be set in this order, if there was no this article? Where are these steps in the manual? Or how to use it to understand that everything needs to be done in that order? And

Md. Istiack Ahamed
2 years ago

I have STM32F103C6T6A but couldn’t find the header file for this module. Should I get the exact stm32f446? or i can do it with this one?

ktmrc8r
2 years ago

Thank you very much, this tutorial helped me a lot … is it possible somehow to verify the modified clock speed ? e.g. in the debug mode (I use STMcubeIDE

Thank you again

John D.
Reply to  ktmrc8r
5 months ago

Yes, you can do that through MCO1 If you kneed info on how to then let me know.

Mehedi Hasan
3 years ago

thank you very much. It was very helpful.

Shams313
3 years ago

Thank you for awesome lessons, sir. i will continue to learn from you.

Last edited 3 years ago by Shams313
Quang
3 years ago

Dear Sir,
Where do you set HSE Frequency ? I couldn’t find it in this code

Last edited 3 years ago by Quang
Shams313
Reply to  Quang
3 years ago
   // 2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
   RCC->APB1ENR |= RCC_APB1ENR_PWREN;
   PWR->CR |= PWR_CR_VOS;

these two lines ensures the proper power delivery to the MCU relating to the oscillator circuit, basically the external oscillator, which is fixed value by board design, the board he used has a 8mhz oscillator shared between st link and main mcu, that’s why he typed 8mhz in cubemx clock configuration flow chart. if u design a board with 16 mhz external oscillator then the HSE input clock would be 16mhz instead of 8mhz and to achieve max frequency u need to change PLL M,N,P etc and other Prescaler values to delivar the clock in different buses that avails clock for different peripherals.

i’m using f429 discovery board by weave share electronics and it has separate ocillator for st link and main MCU HSE input, both are 8mhz.

so this is external hardware, u can’t define the input as much u wish .

Last edited 3 years ago by Shams313
Sang Truong Tan
3 years ago

Dear Sir,
How to find Step by Step for each peripheral?
Thanks!

Saleh Pazouki
4 years ago

Dear Sir,
RCCclock_config.c & RccClock_config.h are not included in the download file. Is it neccessary to have it in order to run the code?

Murat
4 years ago

Hello, I have STM32F4 Discovery but I have difficulties setting up the clock. I checked the reference manuals for register differences, I checked the syntax but I didn’t find any problems. When I download the code to the Discovery Board regardless of the delay function my LEDs (I tried the ones on the board and 3.5mm ones.) just lights up dim and doesn’t blink or do anything really. Can you help with this ?

Nagendra
4 years ago

Dear Sir,

4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
The AHB Prescalar is 1, so the HPRE (bits 7:4) will be all 0s.
So in that case, i think
RCC->CFGR &= ~(F<<4);

But you mentioned

// AHB PR
RCC->CFGR &= ~(1<<4);

So why you only makes 4 bit zero.

Please explain.

Nagendra
Reply to  admin
4 years ago

Thanks for your explanation.