I will attempt to answer this fairly vague question by listing the high level steps to set up a timer interrupt, to help point you in the right direction.

  • Turn on the timer peripheral by enabling it's clock. This is done via the RCC subsystem. You will find this kind of register manipulation in any STM32 code example. If you are going to use the timer for e.g. PWM waveform generation, or to measure external signals, you will also need to enable the clock for the GPIO port you are using. I assume you are past the general "blinking LED" stage and will not go into more detail about GPIO ports :)
  • Configure the timer peripheral to generate interrupts in response to specific timer events. This is not covered in the NVIC section of the manual, but in the section about timers. For the STM32F103 and probably other STM32 chips, the register is called DIER. You will likely want to set the UIE bit in this register, however don't take my word for it. You should read the whole section of the manual to understand how the timer works and how to set it up.
  • Enable the correct IRQ in the NVIC. This hardware (the NVIC) is not specific to STM32, but is common to all ARM microcontrollers. Each interrupt line on the chip (there may be one dedicated to the timer you are using, or it might be shared by several peripherals) is referred to by an integer. I usually look in the device header file for this information, and I recommend getting comfortable with looking through the vendor code for answers about the chip you're working with.
  • Implement your interrupt handler function, often referred to as an ISR (Interrupt Service Routine). In C code, you declare a function which is already named elsewhere in the vendor's initialization code. When you declare a function with that name, the compiler/linker will know to place the address of that function into a known location (the vector table, another good thing to look up). This is the part of the code where you will toggle your LED. It is important to note that each peripheral that can generate an interrupt has a status bit that your ISR should clear, to acknowledge that the ISR has been executed. This becomes important when you have an IRQ shared by multiple peripherals, or if you want to handle more than one event generated by one peripheral. For example, if you enable the "timer update" interrupt via the UIE bit, you will need to then clear the corresponding UIF bit in the status register. This is the only way the peripheral knows that the interrupt has been handled. If you don't do this, the chip will lock up because it is continuously entering the interrupt handler.

Other general advice: the book "The Definitive Guide To ARM Cortex-M3 and Cortex-M4 Processors" helped me understand a lot of this stuff, but not as much as closely reading the chip manual and vendor-provided code.

Answer from jrsa on Stack Exchange
Top answer
1 of 1
1

I will attempt to answer this fairly vague question by listing the high level steps to set up a timer interrupt, to help point you in the right direction.

  • Turn on the timer peripheral by enabling it's clock. This is done via the RCC subsystem. You will find this kind of register manipulation in any STM32 code example. If you are going to use the timer for e.g. PWM waveform generation, or to measure external signals, you will also need to enable the clock for the GPIO port you are using. I assume you are past the general "blinking LED" stage and will not go into more detail about GPIO ports :)
  • Configure the timer peripheral to generate interrupts in response to specific timer events. This is not covered in the NVIC section of the manual, but in the section about timers. For the STM32F103 and probably other STM32 chips, the register is called DIER. You will likely want to set the UIE bit in this register, however don't take my word for it. You should read the whole section of the manual to understand how the timer works and how to set it up.
  • Enable the correct IRQ in the NVIC. This hardware (the NVIC) is not specific to STM32, but is common to all ARM microcontrollers. Each interrupt line on the chip (there may be one dedicated to the timer you are using, or it might be shared by several peripherals) is referred to by an integer. I usually look in the device header file for this information, and I recommend getting comfortable with looking through the vendor code for answers about the chip you're working with.
  • Implement your interrupt handler function, often referred to as an ISR (Interrupt Service Routine). In C code, you declare a function which is already named elsewhere in the vendor's initialization code. When you declare a function with that name, the compiler/linker will know to place the address of that function into a known location (the vector table, another good thing to look up). This is the part of the code where you will toggle your LED. It is important to note that each peripheral that can generate an interrupt has a status bit that your ISR should clear, to acknowledge that the ISR has been executed. This becomes important when you have an IRQ shared by multiple peripherals, or if you want to handle more than one event generated by one peripheral. For example, if you enable the "timer update" interrupt via the UIE bit, you will need to then clear the corresponding UIF bit in the status register. This is the only way the peripheral knows that the interrupt has been handled. If you don't do this, the chip will lock up because it is continuously entering the interrupt handler.

Other general advice: the book "The Definitive Guide To ARM Cortex-M3 and Cortex-M4 Processors" helped me understand a lot of this stuff, but not as much as closely reading the chip manual and vendor-provided code.

🌐
Microcontrollers Lab
microcontrollerslab.com › home › stm32 blue pill › stm32 blue pill timer interrupt with stm32cube ide and hal libraries
STM32 Blue Pill Timer Interrupt with STM32Cube IDE and HAL Libraries
December 2, 2025 - Timer interrupts in STM32 pause the sequential execution of a program loop() function for a predefined number of seconds (timed intervals) to execute a different set of commands. After the set commands are executed, the program resumes again ...
Discussions

embedded - STM32F103 blue pill interrupts from scratch - Stack Overflow
How could I create an interrupt for a blue pill from scratch? I do not want to use any sort of special library. Also, I use Keil IDE, thus, by "building from scratch" I refer rather not t... More on stackoverflow.com
🌐 stackoverflow.com
[SOLVED] setting up timer interrupt on blue pill without a library - Arduino for STM32
Everything relating to using STM32 boards with the Arduino IDE ... Post here first, or if you can't find a relevant section! ... Hello all, I have recently gotten into working with the blue pill board, so I am doing some fairly simple exercises to familiarize myself with using these boards and their peripherals. I am working to make a fairly simple "blink" program using a timer interrupt ... More on stm32duinoforum.com
🌐 stm32duinoforum.com
October 27, 2017
what timer/interupt library to match
Hello, thanks for this great lib. What timer / interupt library matches to this lib for STM32F103C8 / Blue Pill. More on github.com
🌐 github.com
10
April 28, 2019
c - Timer 1 on STM32 not outputting onto GPIO Pin (Bluepill) - Stack Overflow
Using the Blue pill STM32F103 and libopencm3, I am using timer 1 to output a PWM signal onto PA8 and toggle the on board LED PC13. However this does not work. What have I done wrong in my code? I can confirm that the timer is running at the correct frequency (scaled down to 24Hz) by toggling the LED in the interrupt... More on stackoverflow.com
🌐 stackoverflow.com
🌐
DeepBlue
deepbluembedded.com › home › blog › stm32 timer interrupt hal example – timer mode lab
STM32 Timer Interrupt HAL Example - Timer Mode LAB – DeepBlueMbedded
February 17, 2025 - And toggle an LED in the interrupt service routine (ISR) for the timer overflow event. As you can see, the output time interval is determined by the Prescaler value, the clock frequency, and the timer auto-reload register’s value.
🌐
Microcontrollers Lab
microcontrollerslab.com › home › stm32 blue pill › stm32 blue pill timer in counter mode with stm32cube ide and hal libraries
STM32 Blue Pill Timer in Counter Mode with STM32Cube IDE
September 30, 2022 - We will set up a code in which the Timer2 counter counts the ticks from 0 to 20, when the tick count reaches 20, a timer interrupt is triggered which denotes that an overflow occurred and the counter restarts again.
🌐
YouTube
youtube.com › microcontrollers lab
(Demo) STM32 Blue Pill Timer Interrupt with STM32Cube IDE and HAL Libraries - YouTube
(Demo) STM32 Blue Pill Timer Interrupt with STM32Cube IDE and HAL Librarieshttps://microcontrollerslab.com/stm32-blue-pill-timer-interrupt-stm32cube-ide-hal-...
Published   December 18, 2021
Views   716
🌐
STMicroelectronics Community
community.st.com › t5 › stm32-mcus-products › timer-interrupt-discontinuity-issue-using-stm32f103c8t6 › td-p › 685406
Timer interrupt discontinuity issue using stm32f10... - STMicroelectronics Community
June 12, 2024 - I am trying to generate interrupt every 100uSec using Timer-2 using stm32f103c8t6 blue pill. I have simply set the system clock to 72 MHz pre scaled the timer two by 72 time using 72-1 and setting counter period to 100. I have also enabled TIM2 Global Interrupt in NVIC global interrupt setting.
Top answer
1 of 1
2

I am not going to take you entirely literally when you mandate "no libraries", because no one who wants to get work done and knows what they are doing on Cortex-M would do that - and I will assume at least that you will use the CMSIS - a common API provided for all ARM Cortex-M devices, and which makes your code more, not less portable.

All the CMSIS code is provided as source, rather than static library, so there is nothing hidden and if you chose not to use it, you can see how it works and replicate that functionality (needlessly) if you wish.

In the CMSIS default implementations are provided as "weak-links" that can be overridden by user code simply by defining a function of the pre-defined name to override the default. The default implementation is generally an infinite loop - so that unhandled interrupts are "trapped" so you can intervene with your debugger or wait for a watchdog reset for example.

The Cortex-M core interrupt handlers and exception handlers have common names across all Cortex-M parts:

Reset_Handler      
NMI_Handler        
HardFault_Handler  
MemManage_Handler  
BusFault_Handler   
UsageFault_Handler 
SVC_Handler        
DebugMon_Handler   
PendSV_Handler     
SysTick_Handler    

Peripheral interrupt handlers have names defined by the vendor, but the naming convention is <interrupt_source>_IRQHandler. For example on STM32F1xx EXTI0_IRQHandler is the shared external interrupt assigned to bit zero of GPIO ports.

To implement an CMSIS interrupt handler, all you need do is:

  1. Implement the interrupt handler function using the CMSIS handler function name
  2. Enable the interrupt in the NVIC (interrupt controller).

There other are things you might do such as assign the interrupt priority scheme (the split between preempt priorities and subpriorities), but lets keep it simple for the time being.

Because it is ubiquitous to all Cortex-M parts, and because it is useful in almost any non-trivial application an illustration using the SYSTICK interrupt is useful as a starting point.

#include "stm32f1xx.h"
  
volatile uint32_t msTicks = 0 ;
  
void SysTick_Handler(void)  
{
    msTicks++ ;
}
  
int main (void)  
{
    if( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) // 1ms tick
    {
        // Error Handling 
    }
  
    ...

}

SysTick_Config() is another CMSIS function. In core_cm3.h it looks like this:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

So let's say you have a external interrupt source on the falling edge of GPIOA pin 0, then you would use the STM32 EXTI0 interrupt. The minimal handler would look like:

void EXTI0_IRQHandler(void)
{
    EXTI->PR |= (1<<0);                           // clear pending interrupt

    // Handle interrupt...
}

Setting up the EXTI requires enabling the GPIO and the EXTI itself as well as the NVIC:

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ;           // enable clock for GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN ;           // enable clock for Alternate Function
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0 ;        // set pin to use

EXTI->IMR = EXTI_IMR_MR0 ;             // unmask interrupt
EXTI->EMR = EXTI_EMR_MR0 ;             // unmask event
EXTI->FTSR = EXTI_FTSR_TR0 ;           // set falling edge

NVIC->ISER[0] |= (1 << (EXTI0_IRQChannel & 0x1F));    // enable interrupt EXTI 0

The peripheral registers and structures are defined in stm32f10weakx.h, and the "weak" default peripheral handlers to be overridden are in startup_stm32f10x_cl.s for your specific part. Any handlers you override must match these symbol names exactly.

All the peripheral interrupt sources and how to configure them is defined un the ST Reference Manual RM0008.

All the Cortex-M core specific stuff - systtick, NVIC, exception handlers etc. is provided by ARM at https://developer.arm.com/ip-products/processors/cortex-m/cortex-m3

CMSIS for CM3 is documented at https://developer.arm.com/documentation/dui0552/a/

Find elsewhere
🌐
Stm32duinoforum
stm32duinoforum.com › forum › viewtopic_f_3_t_2748.html
[SOLVED] setting up timer interrupt on blue pill without a library - Arduino for STM32
October 27, 2017 - //GPIO setup GPIOC_BASE->CRH = 0x44244444; //enable PC13 as output GPIOC_BASE->BSRR = 0x20000000; //reset PC13 (low) } void loop() { //delay(1000); //GPIOC_BASE->ODR ^=0x2000; //toggle PC13 } //timer 2 interrupt void __irq_tim2(void){ //TIMER2_BASE->SR &= 0xFFFE; //clear update interrupt flag TIMER2_BASE->SR = 0; GPIOC_BASE->ODR ^= 0x2000; //toggle PC13 } Last edited by Veraxis on Wed Nov 01, 2017 10:00 pm, edited 2 times in total. ... the documents referred to in viewtopic.php?f=2&t=2745#p36189 may well help first is target to a DiscoveryVL stm32f100 second is something of a bible stephen
🌐
GitHub
github.com › stm32duino › STM32LowPower › issues › 7
what timer/interupt library to match · Issue #7 · stm32duino/STM32LowPower
April 28, 2019 - Hello, thanks for this great lib. What timer / interupt library matches to this lib for STM32F103C8 / Blue Pill.
Author   stm32duino
🌐
Arduino for STM32
stm32duino.com › board index › arduino for stm32 › general discussion
BluePill fast 10 us Timer interrupt example code needed - Page 3 - Arduino for STM32
10us in this case tmr4OC1AttachISR(myISR); //install user isr - where you flip the output pin(s) After that, the user task, myISR(), is called every 10us. this particular timer on this particular device has 4 compare channels + 1 overflow. so you in theory can have it trigger 3 independent tasks using compare ch2/3/4, and one more for the overflow channel (not independent however). ... again, it should be said that interrupts are a terribly way for frequently invokation of tasks, even if quick tasks.
🌐
ElectronicsHub
electronicshub.org › home › working with interrupts in stm32f103c8t6 blue pill board
Working with Interrupts in STM32F103C8T6 Blue Pill Board Blue Pill Board
June 13, 2024 - In STM32 Blue Pill or the STM32F103C8T6 MCU to be specific, there is a special hardware unit called NVIC (short for Nested Vectored Interrupt Controller), which is responsible for managing all the external interrupts and peripheral interrupts.
🌐
GitHub
github.com › lupyuen › codal-libopencm3 › blob › master › stm32 › bluepill › timer.cpp
codal-libopencm3/stm32/bluepill/timer.cpp at master · lupyuen/codal-libopencm3
cm_enable_interrupts(); } · void platform_start_timer(void (*tickFunc0)(void), void (*alarmFunc0)(void)) { // Start the STM32 Timer to generate interrupt ticks to perform task switching. tickFunc = tickFunc0; // Allow tickFunc to be modified at every call to platform_start_timer().
Author   lupyuen
🌐
Microcontrollers Lab
microcontrollerslab.com › home › stm32 blue pill › stm32 blue pill timer input capture mode with frequency measurement example
STM32 Blue Pill Timer Input Capture Mode Frequency Measurement
December 2, 2025 - When a particular event occurs on the input capture channel pin, its current value is captured and saved to the input capture register TIMx_CCRx. This sets the corresponding CCXIF flag and triggers the interrupt/DMA if it was configured.
🌐
GitHub
github.com › csrohit › bluepill-systick
GitHub - csrohit/bluepill-systick: Programming SysTick timer on STM32f103 Blue Pill Development Board
For applications that do not require an OS, the SysTick can be used for time keeping, time measurement or as an interrupt source for tasks that need to be executed regularly. SysTick register can only be accessed using word access.
Author   csrohit
🌐
Micropeta
micropeta.com › video62
Timer interrupt with Blue Pill using STM32CubeIDE
This project assumes you have already installed STM32CubeIDE. You need to have previously done a basic blink sketch with blue-pill using STM32CubeIDE. I have made a complete video from installing STM32CubeIDE to LED blink program. You can watch it by clicking this link.
🌐
Microcontrollers Lab
microcontrollerslab.com › home › electronics components › stm32f103c8t6 blue pill development board
STM32F103C8T6 Blue Pill Pinout, Peripherals, Programming and Features
December 2, 2025 - It also comes with an integrated ... watchdog timer for the proper execution of instructions. Following diagram shows the pinout of the STM32F103C8T6 Blue Pill Development Board: Let us discuss the pinout of the STM32F103C8T6 Blue Pill Development Board. The pin configuration detail in tabular is mentioned below: External Interrupts: The hardware ...
🌐
STMicroelectronics Community
community.st.com › t5 › stm32-mcus-products › timers-other-than-timer-1-don-t-have-functioning-pwm-blue-pill › td-p › 251210
Timers other than timer 1 don't have functioning PWM? (Blue pill)
May 14, 2020 - The difference was, when it wasn't working, timer 1 update event interrupt was on, and because I had an auto reload of 12, was occurring at ~6MHz (assuming timer 1 runs with the base clock of 72MHz.) This time, I disabled the update event, and the PWM started working.
🌐
Medium
medium.com › @csrohit › blue-pill-systick-programming-878dec0f1582
Programming SysTick timer on STM32f1 Blue Pill board as ARM Cortex M3
December 25, 2023 - That’s why SysTick is configured to interrupt after counting 8000 cycles i.e. including 7999 and 0, thereby generating exactly a 1ms time base. Entire source code and information related to building and running code on the Blue Pill board ...
🌐
Reddit
reddit.com › r/embedded › interrupt handling code doesn't work in stm32 blue pill
r/embedded on Reddit: Interrupt Handling Code doesn't work in stm32 blue pill
December 3, 2024 -

I wrote a Bare metal code to generate interrupt (toggle on-Board LED) whenever external button is pressed. when i run the code, no toggling if i press the button. But, If I just touch the reset button (making contact), the LED Toggles. This is kinda weird. Is this due to electrical issue, cause i enabled pull-up/pull down mode in Registers but still get this error. Do anyone have any idea to solve this issue??

I use STM32F103C8T6. I think there is no problem with board, cause other code works. and Note: i think it's a clone board because i can't run in debug mode even with stlink-V2. and is that have anything to do with the issue?

Edit: Sorry for delay guys, as suggested I tried using HAL library and the code worked. So, problem was with my code. And i also share the link to my code:

For main function: https://github.com/ggurusaran/EMbedded_code/blob/main/stm32f103xx_drivers/Src/E01_ledtoggle.c

Drivers->Inc contains the header files i created for this code and Drivers->Src contains function prototypes. Sorry, if path is confusing, im quite new in using GitHub. thanks