STM32F103 Clock Setup using Registers

This tutorial will cover Clock setup, Timer Setup for Delay, and GPIO configuration for STM32F103C8 (BluePill) using the Register based programming. I will cover all the steps, and the link to download the code is at the end of this post.

Before starting the main setup, Let’s see the clock setup that we will be using in this tutorial.

As you can see above, the system will run at 72 MHz. Also take note of the APB1, and APB2 clocks, since they will be used in the timer setup


This part is very simple in BluePill compared to STM32F4 devices. The reason is that the function to set up the clock is predefined in the core files that we include in our project. Check the code below

void SystemInit (void)
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
  #endif /* DATA_IN_ExtSRAM */

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

This function SystemInit() is already present in system_stm32f10x.c file. All we need to do is call it in our main function and the clock setup will be complete

If you take a look at the top of the system_stm32f10x.c file, you can see that we have options to select different clocks

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
// #define SYSCLK_FREQ_24MHz  24000000 
// #define SYSCLK_FREQ_36MHz  36000000 
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000

As you can see in the above code, we can select any of the predefined clocks. All you need to do is uncomment that line.
Here 72 MHz in uncommented and that’s why the system will run at 72 MHz

This is it for the clock setup. Now let’s take a look at the timer setup

Timer Configuration

In order to configure the timer, we need to follow some set of steps, and they are given below

/************** STEPS TO FOLLOW *****************
1. Enable Timer clock
2. Set the prescalar and the ARR
3. Enable the Timer, and wait for the update Flag to set

Let’s see them one by one

1. Enable the Timer Clock

I am choosing timer 2 for this tutorial. And to enable the timer 2 clock, we need to see the RCC_APB1ENR Register

RCC->APB1ENR |= (1<<0); // enable clock for TIM2

So all we need to do is write a 1 in the 0th position of the RCC_APB1ENR Register

Now let’s see how to set the presclalar and Auto Reload Register

2. Set Prescalar and ARR

Timers are connected to the Peripheral Clocks. In F103C8, there are 2 peripheral clocks i.e APB1 and APB2. In this tutorial I have decided to use the Timer 2, which is basically connected to the APB2.

We know by now that our system clock is at 72 MHz, and the rest of the clock setup will be as shown below

As shown above, the APB1 Clock is at 72 MHz. The Prescalar is used to bring this clock down to our working range. Here in this tutorial we will be focusing on creating delays in micro and milliseconds, so our working range is around 1 MHz.

As according to the above formula, If we use the prescalar of 71, the timer clock will come down to [72MHz/(71+1)] = 1 MHz

TIM2->PSC = 72-1;      	// Prescalar value

After setting the timer for 1 MHz, if we start the timer, the counter starts counting from 0.
Each count by the counter takes 1 microsecond. The maximum count this counter can achieve is the value of the ARR.
After this the timer will overflow and our time delay will be messed up.

To avoid this we will keep the ARR value to the maximum possible. This is a 16 bit register, so the maximum value can be 0xffff.

TIM2->ARR = 0xffff-1;  	// ARR value


In order to enable the Timer, we will modify the Control Register 1 (TIM_CR1).

As shown above, the 0th bit of the TIMx_CR1 Register enable the timer, and to do so we need to write a 1 in the 0th position

TIM2->CR1 |= (1<<0);  	// enable timer

To confirm if the timer has started, we will check the status Register (TIMx_SR)

UIF Bit is set whenever there is update event. So we will wait until this bit is set

while (!(TIM2->SR & (1<<0)));  // UIF: Update interrupt flag..  This bit is set by hardware when the registers are updated

This is it for the Timer setup. Let’s configure the GPIO now


I am using timer 2 to blink a LED, which is connected to PC13. In order to set PC13 as output, we need to follow some set of steps and they are as follows

/************** STEPS TO FOLLOW *****************
	1. Enable the GPIOC clock
	2. Configure the Pin as output

1. Enable the GPIOC Clock

To enable the GPIOC clock, we need to modify the APB2 Enable Register (RCC_APB2ENR)

As you can see in the picture above, the 4th bit of RCC_APB2ENR controls the GPIOC Clock. SO we need to write a 1 to the 4th position in order to enable the PORT C clock

RCC->APB2ENR |= (1<<4);  // Enable GPIOC Clock


Now we will configure the pin as output pin. To do this, we need to modify the Port Configuration Register.

This Register is actually divided into 2 categories. Since we are using Pin PC13, we will be using Port configuration register high (GPIOx_CRH). For Pin 0 to 7, we will use GPIOx_CRL

Port Configuration Register controls both, mode and configuration for the Pin. 4 Bits are used to setup a single pin, for example, in order to set up PIN 10, we have to use bits 11:10:9:8

Since we are using the Pin PC13 for blinking the LED, we need to set it as the output mode. I am using the 10 MHz speed for the pin (there is no particular reason for it).

For the configuration part, i am choosing output Push Pull mode, as it’s best to use in simple GPIO operations.

This setup will lead us to configure the bits (23:22:21:20) as 0:0:0:1

GPIOC->CRH &= ~(0xF<<20)  // Clear Bits 23:22:21:20
GPIOC->CRH |= (1<<20);  // PC13 Output mode 10 MHz, push pull


int main ()
	SystemInit ();
	GPIO_Config ();
	TIM2_Config ();
	while (1)
		GPIOC->BSRR |= (1<<13);  // Set the pin PC13
		Delay_ms (1000);
		GPIOC->BSRR |= (1<<29);  // RESET the pin PC13
		Delay_ms (1000);
  • Here we will first call the SystemInit to configure the clocks
  • next GPIO_Config to configure the PC13 as output
  • Then TIM2_Config will configure the timer for the delay purpose

Now inside the while loop, we will turn the LED ON and OFF. To handle this, we need to look into the Port Bit Set Reset Register (GPIOx_BSRR)

GPIOx_BSRR is a 32 bit Register. The lower 16 Bits (0 to 15) are used to SET the Corresponding Bit. And the upper 16 bits (15 to 31) are used to RESET the corresponding bit

Since we are using PC13, in order to SET this pin, we will write a 1 to the 13th position, and in order to RESET it, we will write a 1 to the 29th position

There is s delay of 1 second between these two statements, and this will keep LED blinking every 1 second


Check out the Video Below

You can buy me a coffee Sensor by clicking DONATE OR Just click DOWNLOAD to download the code

Notify of

Oldest Most Voted
Inline Feedbacks
View all comments

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add to your ad blocking whitelist or disable your adblocking software.