STM32 Timer Tutorial Part 1 – Bare Metal Part 3

This is the Series of tutorials on the STM32 Microcontroller. The aim of this series is to provide easy and practical examples that anyone can understand. This is STM32 Timer Tutorial Part 1 – Bare Metal Part 3. In this tutorial, we are going to see all the registers that are available for the Timer/Counter in STM32. Then we will write a program for the timer.

Prerequisites

Before starting this STM32 GPIO Tutorial, Please go through the below tutorials.

  1. Create a New Project for STM32 in Keil – Bare Metal Part 1
  2. STM32 GPIO Tutorial (LED and Buttons)- Bare Metal Part 2

Hardware/Software Requirements

In this tutorial, we are going to see the timer for the below three STM32 controllers. If you have any one of the controllers or ARM cortex, you can use that. Please click the controller-specific tab and see the content properly.

  • STM32F103CBT6 (ARM Cortex M0)
  • STM32F411CE (ARM Cortex M4)
  • STM32F767Zi (ARM Cortex M7)
  • LEDs
  • Keil IDE

STM32 Timer Tutorial Part 1 – Bare Metal Part 3

What is a Timer?

According to Wiki, A timer is a specialized type of clock used for measuring specific time intervals. A timer that counts from zero upwards for measuring the time elapsed is often called a stopwatch. It is a device that counts down from a specified time interval and is used to generate a time delay. Like that, STM32 also has a few timers in it. And not only counting the time but, it is also used for many purposes. We will see those.

STM32 Timer Introduction

The timer (sometimes referred to as a counter) is an essential peripheral in microcontrollers. There are different types of timers available in the STM32 microcontrollers. Each can operate in multiple modes and perform so many tasks. We can control or change the modes and functionalities of the timer using the timer-related special function registers. Before we get into that, we will have to know about the clock in the STM32. Yes. Timer and Clock are two different things and not the same.

System Clock

The system clock is the heart of the microcontroller. Without the clock, the controller can’t run. We can provide the clock in three different ways to the STM32. Those are,

  • HSI Oscillator Clock (High-Speed Internal Clock)
  • HSE Oscillator Clock (High-Speed External Clock)
  • Main PLL Clock (Phase Locked Loop)

And also it has two secondary clock sources.

  • Low-speed internal RC (LSI RC). This drives the independent watchdog and, optionally, the RTC used for Auto-wakeup from the Stop/Standby mode.
  • Low-speed external crystal (LSE crystal) which optionally drives the RTC clock (RTCCLK)

The first three clocks are used to drive the system clock for the microcontroller. The final two are low-speed clocks and are primarily used to drive the watchdogs.

The HSI or HSE will be selected based on our configuration and the output will be given to the PLL. Using this PLL, we can control (increase or decrease) the System Clock value. When the System Clock signal switches from high to low, one instruction will be executed by the processor. That’s why the speeder clock executes the instruction fast.

Once the System clock is generated, this clock will be distributed to the peripherals and others using AHB Bus (Advanced High-Performance Bus), APB1 Bus (Low-speed Advanced Peripheral Bus), and APB2 Bus (High-speed Advanced Peripheral Bus). Each of these clocks can be scaled using prescalers.

Yes, I understand. This might confuse a few people. The point that I want to convey is, the timer always needs a clock source and the derived clock of this APB is given to the timer. We can control the clock values using the prescaler. In the below example, we are going to use the internal clock (HSI) for demonstration.

STM32 Timer

Now, we have derived the clock from the APB1 or APB2 bus. That output can be controlled using the prescaler. Assume that we have an 8-bit timer and its initial value is 0 (we can change the initial value). When the input clock of the timer goes from high to low, it counts the value from 0 to 1. Likewise, it goes to 255 as this is an 8-bit timer. If this is a 16-bit timer, then it goes till 65535. Most timers will “roll over” once they reach their max value. So, our 8-bit timer would start over again from 0 once it reaches 255. This is the working method of the timer. We can control this timer to do many tasks like counter, capture, etc using the timer registers.

STM32 Timer Modes

We can configure the timer and it can be used in the below modes.

Timer Mode (Time base generator)

The timer can be used as a time base generator. Depending on the clock, prescaler, auto-reload, and repetition counter (if present) parameters, the 16-bit timer can generate an update event from a nanosecond to a few minutes. For the 32-bit timer, the range is larger. This is used to generate the delay, specific operation done in each specific time interval (periodic operations) and etc.

Counter Mode

In this mode, we can count the external events. The timer value will be incremented based on external events. In this mode, the timer will get the clock from the external source through the external timer input pin. This mode is really helpful in many situations when you need to implement a digital counter without polling input pins or periodically reading a GPIO.

Input Capture Mode

The timer can be used in input capture mode to measure an external signal. The Input Capture Mode is another useful feature of Timer, usually used for frequency counting. The input capture module has the task of capturing the current value of the timer counter upon an input event. The input capture function is used in many applications such as:

  • Pulse width measurement
  • Period or timing measurement (Precisely time the duration between rising and falling edges of digital inputs)
  • Capturing the time of an event

Output Compare Mode

In output compare mode, a timer module controls an output waveform or indicates when a period of time has elapsed. When a match is detected between the output compare register (OCR) and the counter, the output compare function assigns the corresponding output pin to a programmable value defined by the output compare mode defined by the programmer.

The output compare pin can be driven high, or low, toggles its state, or stay unchanged based on the user configuration. We can configure the below modes.

  • Output compare timing – When the counter value and the capture/compare register are matched, the channel output is not affected and this is used for the timing base.
  • Output compare active – When the counter value and the capture/compare register are matched, it forces the channel output to the active level.
  • Output compare inactive – When the counter value and the capture/compare register are matched, it forces the channel output to the inactive level.
  • Output compare toggle – When the counter value and the capture/compare register are matched, it toggles the channel output.
  • Output compare forced active/inactive – OCREF is forced high (active mode) or low (inactive mode) independently from the counter value.

One-Pulse Mode

One pulse mode (OPM) is a particular case of the input capture mode and the output compare mode. It allows the counter to be started in response to a stimulus and to generate a pulse with a programmable length after a programmable delay.

Forced output mode

In this output mode, each output compare signal can be forced to active or inactive level directly by the software, independently of any comparison between the output compare register and the counter.

PWM Mode

I hope everyone must aware of this mode. In this mode, we can produce a square wave with a particular duty cycle, period, frequency, etc. Using this, we can do many actions like changing the brightness of the light, speed of the motor, etc.

LED Brightness Control using PWM

PWM Input Mode

This mode is a particular case of input capture mode. The user can measure the period and the duty cycle of the PWM applied on the pin.

Each timer will support a few modes. We will see those modes in each timer.

Types of STM32 Timers

Each STM32 variant controller has several built-in timers. They are numbered from TIM1 to TIM20, and grouped into different types.

  • General Purpose Timers (This tutorial)
  • Advanced Control Timers
  • Basic Timers
  • Low Power Timers

Usually, the higher-end chips will have more timers in them. The details of STM32 timers are quite a rabbit hole, but we’ll keep it simple in this tutorial. We will see the above-mentioned timers one by one in our upcoming tutorials.

As a timer is a vast concept, we will see the timer one by one. In this tutorial, we will see the General Purpose Timers.

General Purpose Timers in STM32

The general-purpose timers consist of a 16-bit or 32-bit auto-reload counter driven by a programmable prescaler. These timers will be used for a variety of purposes and can be used in Input capture mode, Output compare mode, and PWM mode by programming different configurations. The timers are completely independent and do not share any resources. They can be synchronized together. We can measure or generate the Pulse lengths and waveform from a few microseconds to several milliseconds s using the timer prescaler and the RCC clock controller prescalers.

General-purpose timer features

  • 16-bit or 32-bit (based on the microcontroller) up, down, up/down auto-reload counter.
  • 16-bit programmable prescaler used to divide (also “on the fly”) the counter clock frequency by any factor between 1 and 65535.
  • Up to 4 independent channels for:
    • Input capture
    • Output compare
    • PWM generation (Edge- and Center-aligned modes)
    • One-pulse mode output
  • Synchronization circuit to control the timer with external signals and to interconnect several timers.
  • Interrupt/DMA generation on the following events:
    • Update: counter overflow/underflow, counter initialization (by software or internal/external trigger)
    • Trigger event (counter start, stop, initialization or count by internal/external trigger)
    • Input capture
    • Output compare
  • Supports incremental (quadrature) encoder and hall-sensor circuitry for positioning purposes.
  • Trigger input for external clock or cycle-by-cycle current management.

In this post, we have used three microcontrollers (STM32F1STM32F4STM32F7) for demonstration. You can go to the section directly based on the microcontroller that you have. In those sections, we will see the registers that are available in the specific STM32.

SM32F4 Timer

STM32F4 has 13 timer modules (TIM1 to TIM14). TIM8 is not available in the STM32F411xC/E.

  • TIM1– Advanced-control timers
  • TIM2 to TIM5 – General-purpose timers (We will focus on this in the current tutorial)
  • TIM9 to TIM14 – General-purpose timers
  • TIM6 and TIM7 – Basic timers

In this post, we will see the General-Purpose timers (TIM2 to TIM5) in the STM32F4. The block diagram of the general-purpose timer is given below.

STM32F4 General Purpose Timer Module - STM32 Timer Tutorial
STM32F4 General Purpose Timer Module

STM32F4 Timer Registers

Now we will see the registers available in the STM32F4 general-purpose timer (TIM2 to TIM5) timer module. The below registers are available.

  1. TIMx control register 1 (TIMx_CR1)
  2. TIMx control register 2 (TIMx_CR2)
  3. TIMx slave mode control register (TIMx_SMCR)
  4. TIMx DMA/Interrupt enable register (TIMx_DIER)
  5. TIMx status register (TIMx_SR)
  6. TIMx event generation register (TIMx_EGR)
  7. TIMx capture/compare mode register 1 (TIMx_CCMR1)
  8. TIMx capture/compare mode register 2 (TIMx_CCMR2)
  9. TIMx capture/compare enable register (TIMx_CCER)
  10. TIMx counter (TIMx_CNT)
  11. TIMx prescaler (TIMx_PSC)
  12. TIMx auto-reload register (TIMx_ARR)
  13. TIMx capture/compare register 1 (TIMx_CCR1)
  14. TIMx capture/compare register 2 (TIMx_CCR2)
  15. TIMx capture/compare register 3 (TIMx_CCR3)
  16. TIMx capture/compare register 4 (TIMx_CCR4)
  17. TIMx DMA control register (TIMx_DCR)
  18. TIMx DMA address for full transfer (TIMx_DMAR)
  19. TIM2 option register (TIM2_OR)
  20. TIM5 option register (TIM5_OR)

We have 20 registers here and we are not going to explain all the registers. We will explain a few basic registers in the below timer configuration. So, Please refer to the user manual for more details.

SM32F1 Timer

STM32F1 has 14 timer modules (TIM1 to TIM14).

  • TIM1 and TIM8 – Advanced-control timers
  • TIM2 to TIM5 – General-purpose timers (We will focus on this in the current tutorial)
  • TIM9 to TIM14 – General-purpose timers
  • TIM6 and TIM7 – Basic timers

In this post, we will see the General-Purpose timers (TIM2 to TIM5) in the STM32F1. The block diagram of the general-purpose timer is given below.

STM32F1 General Purpose Timer Block Diagram - STM32 Timer Tutorial
STM32F1 General Purpose Timer Block Diagram

STM32F1 Timer Registers

Now we will see the registers available in the STM32F1 general-purpose timer (TIM2 to TIM5) timer module. The below registers are available.

  1. TIMx control register 1 (TIMx_CR1)
  2. TIMx control register 2 (TIMx_CR2)
  3. TIMx slave mode control register (TIMx_SMCR)
  4. TIMx DMA/Interrupt enable register (TIMx_DIER)
  5. TIMx status register (TIMx_SR)
  6. TIMx event generation register (TIMx_EGR)
  7. TIMx capture/compare mode register 1 (TIMx_CCMR1)
  8. TIMx capture/compare mode register 2 (TIMx_CCMR2)
  9. TIMx capture/compare enable register (TIMx_CCER)
  10. TIMx counter (TIMx_CNT)
  11. TIMx prescaler (TIMx_PSC)
  12. TIMx auto-reload register (TIMx_ARR)
  13. TIMx capture/compare register 1 (TIMx_CCR1)
  14. TIMx capture/compare register 2 (TIMx_CCR2)
  15. TIMx capture/compare register 3 (TIMx_CCR3)
  16. TIMx capture/compare register 4 (TIMx_CCR4)
  17. TIMx DMA control register (TIMx_DCR)
  18. TIMx DMA address for full transfer (TIMx_DMAR)

We have 18 registers here and we are not going to explain all the registers. We will explain a few basic registers in the below timer configuration. So, Please refer to the user manual for more details.

SM32F7 Timer

STM32F7 has 14 timer modules (TIM1 to TIM14) and one LPTIM (Low Power Timer).

  • TIM1 and TIM8 – Advanced-control timers
  • TIM2 to TIM5 – General-purpose timers (We will focus on this in the current tutorial)
  • TIM9 to TIM14 – General-purpose timers
  • TIM6 and TIM7 – Basic timers

In this post, we will see the General-Purpose timers (TIM2 to TIM5) in the STM32F7. The block diagram of the general-purpose timer is given below.

STM32F7 General Purpose Timer Module - STM32 Timer Tutorial
STM32F7 General Purpose Timer Module

STM32F4 Timer Registers

Now we will see the registers available in the STM32F7 general-purpose timer (TIM2 to TIM5) timer module. The below registers are available.

  1. TIMx control register 1 (TIMx_CR1)
  2. TIMx control register 2 (TIMx_CR2)
  3. TIMx slave mode control register (TIMx_SMCR)
  4. TIMx DMA/Interrupt enable register (TIMx_DIER)
  5. TIMx status register (TIMx_SR)
  6. TIMx event generation register (TIMx_EGR)
  7. TIMx capture/compare mode register 1 (TIMx_CCMR1)
  8. TIMx capture/compare mode register 2 (TIMx_CCMR2)
  9. TIMx capture/compare enable register (TIMx_CCER)
  10. TIMx counter (TIMx_CNT)
  11. TIMx prescaler (TIMx_PSC)
  12. TIMx auto-reload register (TIMx_ARR)
  13. TIMx capture/compare register 1 (TIMx_CCR1)
  14. TIMx capture/compare register 2 (TIMx_CCR2)
  15. TIMx capture/compare register 3 (TIMx_CCR3)
  16. TIMx capture/compare register 4 (TIMx_CCR4)
  17. TIMx DMA control register (TIMx_DCR)
  18. TIMx DMA address for full transfer (TIMx_DMAR)
  19. TIM2 option register (TIM2_OR)
  20. TIM5 option register (TIM5_OR)

We have 20 registers here and we are not going to explain all the registers. We will explain a few basic registers in the below timer configuration. So, Please refer to the user manual for more details.

You can refer to the respective User manual for a better understanding.

  1. STM32F103 User manual
  2. STM32F411 User manual
  3. STM32F767 User manual

STM32 Timer Example 1 – Generate Simple Dealy (Timer mode)

In this example, we are just going to use the TIM3 to generate a delay of 1 millisecond. Then using that 1ms delay function, we can run 1000 times to get the 1-second delay. So, we are going to toggle the onboard LED in every 1-second delay.

Configuration

STM32F4 Configuration

We can configure the clock and TIM3 registers.

Setup the System Clock

Here, we are going to use the internal HSI clock and generate the 16MHz. We are not going to use the PLL and any prescaler. So, AHB, APB1, and APB2 will get 16MHz. But you can use the external clock HSE. The only thing is you need to configure the timer based on that.

The below function generates the 16MHz clock.

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock*/
  RCC->CR &= ~RCC_CR_HSEON;
}

Enable the peripheral clock for TIM3

We need to enable the clock for the Timer 3 peripheral in the APB1. The below code does that.

/* Enable the APB clock FOR TIM3  */
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);

Configure the Prescaler

As we are getting 16MHz to the timer module, we want to reduce that to 1MHz. So, we need to set the TIM3->PSC register with the proper value. The below formula will be used to find the value.

Required freq = CLK / (PSC + 1)

Here we need 1MHz and CLK is 16MHz.

So, 1MHz = 16MHz / (PSC +1)

PSC = 15

We need to set the PSC register with 15.

TIM3->PSC = 15;

Configure the Auto Reload Register

We have set the PSC register with 15. So, the timer count will be incremented every 1micro second. How did I say that? It is simple math. Time = 1 / Frequency. So, Time = 1 / 1MHz. Now you got it? Great.

The auto-reload register is used to set the maximum value. For example, the 16-bit timer counts from 0 to 65535. Then it again starts from 0. If we set the auto-reload register to 100, then the timer count starts from 0 and counts to 100. Then it resets to 0 again instead of going to 65535. Now, the timer count increments by every 1us. If we want to generate 1 ms, then we need to count from 0 to 1000. That is enough. Since our goal is to generate 1 ms, we can set the ARR register to 1000. Once it reaches to 1000, it will set the timer’s status register UIF bit will be set. We need to clear it manually.

/* (1 MHz / 1000) = 1KHz = 1ms */
/* So, this will generate the 1ms delay */
TIM3->ARR = 999;

Enable the Timer module

We can enable the timer by setting the CEN (0th) bit in the CR1 register.

/* Finally enable TIM3 module */
TIM3->CR1 = (1 << 0);

Wait for delay

After enabling the timer, it will start counting from 0. When it reaches to 1000, it will set the UIF bit in the Status Register. We need to wait until that bit is set for a 1-ms delay. Then we need to clear that bit. Refer to the below code.

/* Wait UIF to be set */
while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
/* Reset UIF */
TIM3->SR &= ~TIM_SR_UIF;

STM32F1 Configuration

We can configure the clock and TIM3 registers.

Setup the System Clock

Here, we are going to use the internal HSI clock and generate the 8MHz. We are not going to use the PLL and any prescaler. So, AHB, APB1, and APB2 will get 8MHz. But you can use the external clock HSE. The only thing is you need to configure the timer based on that.

The below function generates the 8MHz clock.

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 8MHz. The Internal HSI Clock is 8MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo8Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  FLASH->ACR  |= FLASH_ACR_LATENCY_2;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

Enable the peripheral clock for TIM3

We need to enable the clock for the Timer 3 peripheral in the APB1. The below code does that.

/* Enable the APB clock FOR TIM3  */
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);

Configure the Prescaler

As we are getting 8MHz to the timer module, we want to reduce that to 1MHz. So, we need to set the TIM3->PSC register with the proper value. The below formula will be used to find the value.

Required freq = CLK / (PSC + 1)

Here we need 1MHz and CLK is 8MHz.

So, 1MHz = 8MHz / (PSC +1)

PSC = 7

We need to set the PSC register with 7.

TIM3->PSC = 7;

Configure the Auto Reload Register

We have set the PSC register with 7. So, the timer count will be incremented every 1micro second. How did I say that? It is simple math. Time = 1 / Frequency. So, Time = 1 / 1MHz. Now you got it? Great.

The auto-reload register is used to set the maximum value. For example, the 16-bit timer counts from 0 to 65535. Then it again starts from 0. If we set the auto-reload register to 100, then the timer count starts from 0 and counts to 100. Then it resets to 0 again instead of going to 65535. Now, the timer count increments by every 1us. If we want to generate 1 ms, then we need to count from 0 to 1000. That is enough. Since our goal is to generate 1 ms, we can set the ARR register to 1000. Once it reaches to 1000, it will set the timer’s status register UIF bit will be set. We need to clear it manually.

 /* (1 MHz / 1000) = 1KHz = 1ms */
/* So, this will generate the 1ms delay */
TIM3->ARR = 999;

Enable the Timer module

We can enable the timer by setting the CEN (0th) bit in the CR1 register.

/* Finally enable TIM3 module */
TIM3->CR1 = (1 << 0);

Wait for delay

After enabling the timer, it will start counting from 0. When it reaches to 1000, it will set the UIF bit in the Status Register. We need to wait until that bit is set for a 1-ms delay. Then we need to clear that bit. Refer to the below code.

/* Wait UIF to be set */
while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
/* Reset UIF */
TIM3->SR &= ~TIM_SR_UIF;

STM32F7 Configuration

We can configure the clock and TIM3 registers.

Setup the System Clock

Here, we are going to use the internal HSI clock and generate the 16MHz. We are not going to use the PLL and any prescaler. So, AHB, APB1, and APB2 will get 16MHz. But you can use the external clock HSE. The only thing is you need to configure the timer based on that.

The below function generates the 16MHz clock.

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash wait state */
  FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

Enable the peripheral clock for TIM3

We need to enable the clock for the Timer 3 peripheral in the APB1. The below code does that.

/* Enable the APB clock FOR TIM3  */
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);

Configure the Prescaler

As we are getting 16MHz to the timer module, we want to reduce that to 1MHz. So, we need to set the TIM3->PSC register with the proper value. The below formula will be used to find the value.

Required freq = CLK / (PSC + 1)

Here we need 1MHz and CLK is 16MHz.

So, 1MHz = 16MHz / (PSC +1)

PSC = 15

We need to set the PSC register with 15.

TIM3->PSC = 15;

Configure the Auto Reload Register

We have set the PSC register with 15. So, the timer count will be incremented every 1micro second. How did I say that? It is simple math. Time = 1 / Frequency. So, Time = 1 / 1MHz. Now you got it? Great.

The auto-reload register is used to set the maximum value. For example, the 16-bit timer counts from 0 to 65535. Then it again starts from 0. If we set the auto-reload register to 100, then the timer count starts from 0 and counts to 100. Then it resets to 0 again instead of going to 65535. Now, the timer count increments by every 1us. If we want to generate 1 ms, then we need to count from 0 to 1000. That is enough. Since our goal is to generate 1 ms, we can set the ARR register to 1000. Once it reaches to 1000, it will set the timer’s status register UIF bit will be set. We need to clear it manually.

/* (1 MHz / 1000) = 1KHz = 1ms */
/* So, this will generate the 1ms delay */
TIM3->ARR = 999;

Enable the Timer module

We can enable the timer by setting the CEN (0th) bit in the CR1 register.

/* Finally enable TIM3 module */
TIM3->CR1 = (1 << 0);

Wait for delay

After enabling the timer, it will start counting from 0. When it reaches to 1000, it will set the UIF bit in the Status Register. We need to wait until that bit is set for a 1-ms delay. Then we need to clear that bit. Refer to the below code.

/* Wait UIF to be set */
while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
/* Reset UIF */
TIM3->SR &= ~TIM_SR_UIF;

Complete STM32 Timer Source Code

STM32F4 Source Code

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer
*               - STM32 Timer Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32411CE Board
*
*******************************************************************************/

#include "stm32f4xx.h" 

/***************************************************************************//**

  \details  Providing milli seconds Delay by running Timer

  \return   void

  \retval   none

*******************************************************************************/
static void delay( uint32_t ms )
{
  uint32_t i;
  for( i = 0; i <= ms; i++ )
  {
    /* Clear the count */
    TIM3->CNT = 0;
    
    /* Wait UIF to be set */
    while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
    /* Reset UIF */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock*/
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (16 MHz / (15+1)) = 1 MHz timer clock speed */
  TIM3->PSC = 15;
  
  /* (1 MHz / 1000) = 1KHz = 1ms */
  /* So, this will generate the 1ms delay */
  TIM3->ARR = 999;
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = (1 << 0);
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{

  /* Set System clock to 16 MHz using HSI */
  SetSystemClockTo16Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the AHB clock all GPIO port C */
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);

  /* set all Port C as output */
  GPIOC->MODER = 0x55555555;

  /* Endless loop */
  while(1)
  {
    /* Turn ON the LED of PC13 */
    GPIOC->BSRR |= GPIO_BSRR_BS13;

    delay(1000);

    /* Turn OFF the LED of PC13 */
    GPIOC->BSRR |= GPIO_BSRR_BR13;

    delay(1000);
  }
}

STM32F1 Source Code

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer
*               - STM32 Timer Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32F103 Board
*
*******************************************************************************/

#include "stm32f10x.h" 

/***************************************************************************//**

  \details  Providing milli seconds Delay by running Timer

  \return   void

  \retval   none

*******************************************************************************/
static void delay( uint32_t ms )
{
  uint32_t i;
  for( i = 0; i <= ms; i++ )
  {
    /* Clear the count */
    TIM3->CNT = 0;
    
    /* Wait UIF to be set */
    while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
    /* Reset UIF */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 8MHz. The Internal HSI Clock is 8MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo8Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  FLASH->ACR  |= FLASH_ACR_LATENCY_2;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (8 MHz / (7+1)) = 1 MHz timer clock speed */
  TIM3->PSC = 7;
  
  /* (1 MHz / 1000) = 1KHz = 1ms */
  /* So, this will generate the 1ms delay */
  TIM3->ARR = 999;
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = (1 << 0);
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{

  /* Set System clock to 8MHz using HSI*/
  SetSystemClockTo8Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the APB clock all GPIO port C */
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);

  /* PC13 as output */
  GPIOC->CRH &= ~(GPIO_CRH_MODE13|GPIO_CRH_CNF13);    /* Clear MODE13 and CNF13 fields */
  GPIOC->CRH |= GPIO_CRH_MODE13_1|GPIO_CRH_MODE13_0;  /* Set MODE13 to 3 (Output) */

  /* Endless loop */
  while(1)
  {
    /* Turn ON the LED of PC13 */
    GPIOC->BSRR |= GPIO_BSRR_BS13;

    delay(1000);

    /* Turn OFF the LED of PC13 */
    GPIOC->BSRR |= GPIO_BSRR_BR13;

    delay(1000);
  }
}

STM32F7 Source Code

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer
*               - STM32 Timer Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32F767Zi Nucleo Board
*
*******************************************************************************/

#include "stm32f7xx.h" 

/***************************************************************************//**

  \details  Providing milli seconds Delay by running Timer

  \return   void

  \retval   none

*******************************************************************************/
static void delay( uint32_t ms )
{
  uint32_t i;
  for( i = 0; i <= ms; i++ )
  {
    /* Clear the count */
    TIM3->CNT = 0;
    
    /* Wait UIF to be set */
    while((TIM3->SR & TIM_SR_UIF) == 0);    /* This will generate 1ms delay */
    
    /* Reset UIF */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash wait state */
  FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (16 MHz / (15+1)) = 1 MHz timer clock speed */
  TIM3->PSC = 15;
  
  /* (1 MHz / 1000) = 1KHz = 1ms */
  /* So, this will generate the 1ms delay */
  TIM3->ARR = 999;
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = (1 << 0);
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{
  /* Set System clock to 16 MHz using HSI */
  SetSystemClockTo16Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the AHB clock all GPIO port B */
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);

  /* set Port B as output */
  GPIOB->MODER |= 0x55555555;
  
  /* Set Port B as Push Pull */
  GPIOB->OTYPER = 0x00000000;
  
  /* Set Low Speed */
  GPIOB->OSPEEDR = 0x00000000;

  /* Endless loop */
  while(1)
  {
    /* Turn ON the PB0 (Green LED), PB7 (Blue LED), PB14 (Red LED) */
    GPIOB->BSRR |= GPIO_BSRR_BS0 | GPIO_BSRR_BS7 | GPIO_BSRR_BS14;

    delay(1000);

    /* Turn OFF the PB0 (Green LED), PB7 (Blue LED), PB14 (Red LED) */
    GPIOB->BSRR |= GPIO_BSRR_BR0 | GPIO_BSRR_BR7 | GPIO_BSRR_BR14;

    delay(1000);
  }
}

STM32 Timer Tutorial – Output

Please find the output of the example below.

STM32 Timer Example 2 – Generate Dealy using Timer Interrupt

In this example, we are just going to use the TIM3 to generate a delay of 1 second. When it reached a 1-second delay, it will trigger the interrupt. So, we are going to toggle the onboard LED in every 1-second delay.

Configuration

STM32F4 and STM32F7 Configuration

In our previous example, we were continuously polling the timer status register’s TIM_SR_UIF bit. In this example, we are not going to poll it. Instead, we will enable the interrupt. When the timer reaches its maximum (ARR register) value, it will trigger the interrupt.

We have used the HSI clock and generated 16MHz in our previous example (SetSystemClockTo16Mhz()). We are going to use the same function here as well.

During the timer configuration, we are going to do the below steps.

  • In our previous example, we configured the PSC register to 16 and ARR register to 1000. Due to that, that TIM_SR_UIF will be set after 1 ms. But here we want to generate the interrupt for 1 second. So, we need to change the PSR values based on our delay.

As we are getting 16MHz to the timer module, we want to reduce that to 1KHz. So, we need to set the TIM3->PSC register with the proper value. The below formula will be used to find the value.

Required freq = CLK / (PSC + 1)

Here we need 1KHz and CLK is 16MHz.

So, 1KHz = 16MHz / (PSC +1)

PSC = 15999

We have set the PSC register with 15999. So, the timer count will be incremented every 1milli second.

 Time = 1 / Frequency. So, Time = 1 / 1KHz.

So, if we set the ARR register to 1000, it can generate a 1-second delay.

  • Enable the Timer interrupt using the TIMx->DIER register
  • Clear the interrupt status (TIM3->SR &= ~TIM_SR_UIF)
  • Enable the Global NVIC Timer interrupt (NVIC_EnableIRQ(TIM3_IRQn))
  • Set the priority (Optional)
  • Create the Timer ISR function
void TIM3_IRQHandler(void)
{
  /* if UIF flag is set */
  if(TIM3->SR & TIM_SR_UIF)
  {
    /* Your Code */

    /* Clear the Interrupt Status */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

STM32F1 Configuration

In our previous example, we were continuously polling the timer status register’s TIM_SR_UIF bit. In this example, we are not going to poll it. Instead, we will enable the interrupt. When the timer reaches its maximum (ARR register) value, it will trigger the interrupt.

We have used the HSI clock and generated 8MHz in our previous example (SetSystemClockTo8Mhz()). We are going to use the same function here as well.

During the timer configuration, we are going to do the below steps.

  • In our previous example, we configured the PSC register to 8 and ARR register to 1000. Due to that, that TIM_SR_UIF will be set after 1 ms. But here we want to generate the interrupt for 1 second. So, we need to change the PSR values based on our delay.

As we are getting 8MHz to the timer module, we want to reduce that to 1KHz. So, we need to set the TIM3->PSC register with the proper value. The below formula will be used to find the value.

Required freq = CLK / (PSC + 1)

Here we need 1KHz and CLK is 8MHz.

So, 1KHz = 8MHz / (PSC +1)

PSC = 7999

We have set the PSC register with 7999. So, the timer count will be incremented every 1milli second.

 Time = 1 / Frequency. So, Time = 1 / 1KHz.

So, if we set the ARR register to 1000, it can generate a 1-second delay.

  • Enable the Timer interrupt using the TIMx->DIER register
  • Clear the interrupt status (TIM3->SR &= ~TIM_SR_UIF)
  • Enable the Global NVIC Timer interrupt (NVIC_EnableIRQ(TIM3_IRQn))
  • Set the priority (Optional)
  • Create the Timer ISR function
void TIM3_IRQHandler(void)
{
  /* if UIF flag is set */
  if(TIM3->SR & TIM_SR_UIF)
  {
    /* Your Code */

    /* Clear the Interrupt Status */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

STM32 Timer Interrupt without HAL – Source Code

STM32F4 Timer Interrupt

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer Interrupt
*               - STM32 Timer Interrupt Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32411CE Board
*
*******************************************************************************/

#include "stm32f4xx.h" 
#include <stdbool.h>

static volatile bool led_on = 0;

/***************************************************************************//**

  \details  Timer 3 ISR 

  \return   void

  \retval   none

*******************************************************************************/
void TIM3_IRQHandler(void)
{
  /* if UIF flag is set */
  if(TIM3->SR & TIM_SR_UIF)
  {
    led_on = !led_on;
    
    if( led_on )
    {
      /* Turn ON the LED of PC13 */
      GPIOC->BSRR |= GPIO_BSRR_BS13;
    }
    else
    {
      /* Turn OFF the LED of PC13 */
      GPIOC->BSRR |= GPIO_BSRR_BR13;
    }
    
    /* Clear the Interrupt Status */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock*/
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (16 MHz / (15999+1)) = 1 KHz timer clock speed */
  TIM3->PSC = 15999;
  
  /* (1 KHz / 1000) = 1Hz = 1s */
  /* So, this will generate the 1s delay */
  TIM3->ARR = 999;
  
  /* Enable the Interrupt */
  TIM3->DIER |= TIM_DIER_UIE;
  
  /* Clear the Interrupt Status */
  TIM3->SR &= ~TIM_SR_UIF;
  
  /* Enable NVIC Interrupt for Timer 3 */
  NVIC_EnableIRQ(TIM3_IRQn);
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = TIM_CR1_CEN;
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{

  /* Set System clock to 16 MHz using HSI */
  SetSystemClockTo16Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the AHB clock all GPIO port C */
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);

  /* set all Port C as output */
  GPIOC->MODER = 0x55555555;

  /* Endless loop */
  while(1)
  {
  
  }
}

STM32F7 Timer Interrupt

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer Interrupt
*               - STM32 Timer Interrupt Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32F767Zi Nucleo Board
*
*******************************************************************************/

#include "stm32f7xx.h" 
#include <stdbool.h>

static volatile bool led_on = 0;

/***************************************************************************//**

  \details  Timer 3 ISR 

  \return   void

  \retval   none

*******************************************************************************/
void TIM3_IRQHandler(void)
{
  /* if UIF flag is set */
  if(TIM3->SR & TIM_SR_UIF)
  {
    led_on = !led_on;
    
    if( led_on )
    {
      /* Turn ON the PB0 (Green LED), PB7 (Blue LED), PB14 (Red LED) */
      GPIOB->BSRR |= GPIO_BSRR_BS0 | GPIO_BSRR_BS7 | GPIO_BSRR_BS14;
    }
    else
    {
      /* Turn OFF the PB0 (Green LED), PB7 (Blue LED), PB14 (Red LED) */
      GPIOB->BSRR |= GPIO_BSRR_BR0 | GPIO_BSRR_BR7 | GPIO_BSRR_BR14;
    }
    
    /* Clear the Interrupt Status */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 16MHz. The Internal HSI Clock is 16MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo16Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  /* Configure Flash wait state */
  FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_3WS;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (16 MHz / (15999+1)) = 1 KHz timer clock speed */
  TIM3->PSC = 15999;
  
  /* (1 KHz / 1000) = 1Hz = 1s */
  /* So, this will generate the 1s delay */
  TIM3->ARR = 999;
  
  /* Enable the Interrupt */
  TIM3->DIER |= TIM_DIER_UIE;
  
  /* Clear the Interrupt Status */
  TIM3->SR &= ~TIM_SR_UIF;
  
  /* Enable NVIC Interrupt for Timer 3 */
  NVIC_EnableIRQ(TIM3_IRQn);
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = TIM_CR1_CEN;
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{
  /* Set System clock to 16 MHz using HSI */
  SetSystemClockTo16Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the AHB clock all GPIO port B */
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);

  /* set Port B as output */
  GPIOB->MODER |= 0x55555555;
  
  /* Set Port B as Push Pull */
  GPIOB->OTYPER = 0x00000000;
  
  /* Set Low Speed */
  GPIOB->OSPEEDR = 0x00000000;

  /* Endless loop */
  while(1)
  {
   
  }
}

STM32F1 Timer Interrupt

You can download the entire project source code from GitHub.

/***************************************************************************//**
*   \file       main.c
*
*   \details    Blinking Onboard LED with 1 second delay using Timer Interrupt
*               - STM32 Timer Interrupt Tutorial
*
*   \author     EmbeTronicX
*
*   \This code is verified with STM32F103 Board
*
*******************************************************************************/

#include "stm32f10x.h" 
#include <stdbool.h>

static volatile bool led_on = 0;

/***************************************************************************//**

  \details  Timer 3 ISR 

  \return   void

  \retval   none

*******************************************************************************/
void TIM3_IRQHandler(void)
{
  /* if UIF flag is set */
  if(TIM3->SR & TIM_SR_UIF)
  {
    led_on = !led_on;
    
    if( led_on )
    {
      /* Turn ON the LED of PC13 */
      GPIOC->BSRR |= GPIO_BSRR_BS13;
    }
    else
    {
      /* Turn OFF the LED of PC13 */
      GPIOC->BSRR |= GPIO_BSRR_BR13;
    }
    
    /* Clear the Interrupt Status */
    TIM3->SR &= ~TIM_SR_UIF;
  }
}

/***************************************************************************//**

  \details  This function enables the HSI clock as a system clock and generate 
            the 8MHz. The Internal HSI Clock is 8MHz. So, we are not using PLL
            and not dividing, Multiplying. So, we will get the 16MHz as it is.

  \return   void

  \retval   none

*******************************************************************************/
static void SetSystemClockTo8Mhz(void)
{
  /* Enabling the HSI clock - If not enabled and ready */
  if( (RCC->CR & RCC_CR_HSIRDY) == 0) 
  {
    RCC->CR |= RCC_CR_HSION;  /* HSION=1 */
  
    /* Waiting until HSI clock is ready */
    while( (RCC->CR & RCC_CR_HSIRDY) == 0);
  }
  
  /* Select AHB prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* APB1 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;
  
  /* APB2 prescaler to 1 */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* Select the HSI as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_HSI;
  
  FLASH->ACR  |= FLASH_ACR_LATENCY_2;
  
  /* Disabling HSE Clock */
  RCC->CR &= ~RCC_CR_HSEON;
}

/***************************************************************************//**

  \details  This function configures the timer 3 to generate the 1ms delay. 

  \return   void

  \retval   none

*******************************************************************************/
static void ConfigureTimer3(void)
{
  /* Enable the APB clock FOR TIM3  */
  SET_BIT(RCC->APB1ENR, RCC_APB1ENR_TIM3EN);
  
  /* fCK_PSC / (PSC[15:0] + 1)
     (8 MHz / (7999+1)) = 1 KHz timer clock speed */
  TIM3->PSC = 7999;
  
  /* (1 KHz / 1000) = 1Hz = 1s */
  /* So, this will generate the 1s delay */
  TIM3->ARR = 999;
  
  /* Enable the Interrupt */
  TIM3->DIER |= TIM_DIER_UIE;
  
  /* Clear the Interrupt Status */
  TIM3->SR &= ~TIM_SR_UIF;
  
  /* Enable NVIC Interrupt for Timer 3 */
  NVIC_EnableIRQ(TIM3_IRQn);
  
  /* Finally enable TIM3 module */
  TIM3->CR1 = TIM_CR1_CEN;
}

/***************************************************************************//**

  \details  The main function.  It should not return.

  \return   void

  \retval   none

*******************************************************************************/
int main(void)
{

  /* Set System clock to 8MHz using HSI*/
  SetSystemClockTo8Mhz();
  
  /* Configure the Timer 3 */
  ConfigureTimer3();
  
  /* Enable the APB clock all GPIO port C */
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);

  /* PC13 as output */
  GPIOC->CRH &= ~(GPIO_CRH_MODE13|GPIO_CRH_CNF13);    /* Clear MODE13 and CNF13 fields */
  GPIOC->CRH |= GPIO_CRH_MODE13_1|GPIO_CRH_MODE13_0;  /* Set MODE13 to 3 (Output) */

  /* Endless loop */
  while(1)
  {
  
  }
}

STM32 Timer Interrupt without HAL- Output

Please find the output of the example below.

In our next tutorial, we will discuss the other modes of the timer.

Please read the other STM32 Tutorials.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials – STM32Zephyr RTOS Tutorials – ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Table of Contents