STM32F4 External interrupts tutorial

Each STM32F4 device has 23 external interrupt or event sources. They are split into 2 sections. First interrupt section is for external pins (P0 to P15) on each port, and other section is for other events, like RTC interrupt, Ethernet interrupt, USB interrupt and so on.

October 1, 2014: Added external interrupts library.



GPIO as Interrupt

Interrupt lines

I will show now how to configure GPIO pin to be an interrupt and how to handle it in your code with CMSIS function.

In section one (GPIOs) we have 16 interrupt lines. They are line0 to line15 and they also represent pin number. This means, PA0 is connected to Line0 and PA13 is connected to Line13.

You have to know that PB0 is also connected to Line0 and PC0 also and so on. This is for all pins on board, All Px0 (where x is GPIO name) pins are connected to Line0 and let’s say all Px3 are connected to Line3 on the Interrupt channel.

All pins with same number are connected to line with same number. They are multiplexed to one line.

IMPORTANT: You can not use two pins on one line simultaneously:

  • PA0 and PB0 and PC0 and so on, are connected to Line0, so you can use only one pin at one time to handle interrupt from there.
  • PA0 and PA5 are connected to different lines, they can be used at the same time.

Each line can trigger an interrupt on rising, falling or rising_falling enge on signal.

Interrupt handlers

OK, now you have selected your pin you want to use. But you have to handle interrupt somehow. This process is described below.

STM32F4 has 7 interrupt handlers for GPIO pins. They are in table below:

Irq Handler Description
EXTI0_IRQn EXTI0_IRQHandler Handler for pins connected to line 0
EXTI1_IRQn EXTI1_IRQHandler Handler for pins connected to line 1
EXTI2_IRQn EXTI2_IRQHandler Handler for pins connected to line 2
EXTI3_IRQn EXTI3_IRQHandler Handler for pins connected to line 3
EXTI4_IRQn EXTI4_IRQHandler Handler for pins connected to line 4
EXTI9_5_IRQn EXTI9_5_IRQHandler Handler for pins connected to line 5 to 9
EXTI15_10_IRQn EXTI15_10_IRQHandler Handler for pins connected to line 10 to 15

This table show you which IRQ you have to set for NVIC (first column) and function names to handle your interrupts (second column). You have probably also figured, that only lines 0 to 4 have own IRQ handler. Yes, lines 5-9 have the same interrupt handler and this is also for lines 10 to 15.

After you set settings for EXTI, you have to add them into NVIC. More about NVIC is described here.

Example

In this example, we will set pin PD0 and PB12 to be a GPIO interrupts. Code below should be well documented to understand how it works.

Make a feedback if this tutorial a little bit helps to understand external interrupts on STM32F4 device.

tilz0R

Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • Pingback: Library 26- Rotary encoder on STM32F4xx - STM32F4 Discovery()

  • Dennis W

    Hi Tilen

    Thanks for the great tutorial. There are a few things I don’t understand. Can you help me?

    1) Why do you have to enable the clocks for GPIOB and SYSCFG ?
    2) I can’t find PD0 on the board. Where is it used?
    3) Is it a good summary to say:

    // set up the GPIO pin so that it can be used as an interrupt?
    GPIO_Init(GPIOD, &GPIO_InitStruct);

    // specifically, connects the D version of the pin to the interrupt?
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource0);

    // tells the system we want to setup the Line0 interrupt?
    EXTI_InitStruct.EXTI_Line = EXTI_Line0; // connects

    // Tells the system that we want to setup the IRQ0 line?
    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;

    4) I want to use the L3GD and the touchscreen’s interrupts. What pin / line would they be connected to?

    Thanks
    Dennis

    • There are 2 functions to set interrupt.
      One sets PD0 and one sets PB12.

      If you want to use some pin, first thing is to enable clock.
      You have to enable clock to GPIOB and GPIOD.

      SYSCFG is used to set correct pin to specific line.

      You have good sequence for set pin. Only enabling clock is needed.

      L3GD20 on STM32F429-Discovery has 2 interrupt pins
      Schematic says PA1 and PA2 are in use.

  • Thamanoon K.

    Hi Tilen
    Thank you very much for the good tutorial.

  • Hi This Is Janardhana. I have one doubt, how to write the handler for 2 pins like PORTB 9, PORTD 8, both are having the same handler so how can I differentiate those two ?

  • Joe von Löthing

    Hey, the program listing has the line “#include “misc.h”” but I can’t find any reference to misc.h on your site. Where does it come from?

    • STM32F4xx CMSIS files or Cortex-M4 CMSIS files from ARM.

  • Tim

    Tilen,
    On line 78, why is it
    GPIO_Init(GPIOD, &GPIO_InitStruct);
    and not:
    GPIO_Init(GPIOB, &GPIO_InitStruct); ?

    Just something I came across why trying to implement the keypad interrupt

  • zainka

    g’day Sir…

    One question. Why is it necessary to test for “flag set” for interrupt handlers which have only one source, like external interrupts 0 to 4? Or isn’t it really necessary and youst something you do for common practice?

    As long as the interrupt handler has been called upon the interrupt flag has been set, right? Or could there be other situations where the handler is called? I now assume that one does not trigger the handler as a part of own code by using a function call towards it.

    It should be enough to just clear the flag.

    • Actually, you are right, that’s correct.
      But my practise is to always check first if it was called from correct source.

      handler functions are stored in flash like others, you can call them too, without even know you did if you fail your address when using pointers or something like that.

  • Pingback: Library 38- External interrupts for STM32F4 - STM32F4 Discovery()

  • Tim

    Hello Tilen,
    I just got back to working on the 4×4 matrix keypad using interrupts. I got the interrupts for all the key to work correctly (it goes into the correct interrupt vector), but as soon as I call your decode key functions, it would decode the key correctly only one time, then the interrupt for everything just stops working. Do you have any idea what might be the problem?

    • Matrix keyboard does not use interrupts. I don’t imagine, how you set this up to work. You still need something which will set your columns to high/low before you can read it on rows.

      • Tim

        I got it to work. The reason for why I have been trying to get the matrix keypad to work using interrupt is that I am working on a project in which the MCU needs to be in low power mode (sleeping) the majority of the time to save battery, and only wakes up to interrupts (which means the while(1) loop is empty). Thanks for your tutorials though, they helped me a lot in getting myself familiar with this MCU.
        And I believe it is really common implementing this matrix keypad with interrupts. Just do a quick Google search and you will see it.
        Thanks again for the awesome tutorials!

  • Hi Tim
    thx for the good tutorial!
    Can I somehow use an “external Interrupt line” triggered from a internal peripheral eg timer, UART etc. directly?
    On the falling edge of a PWM Signal (two channels, center alligned) on timer 1 I do need an ISR routine to jump in. Of course I can have that PWM channel output to a pin and connect this pin with an EXTI pin to trigger the interrupt (falling edge configured). It works 😉 but there must be an internal way to do this…

    • Take a look to stm32f4xx_exti.c file. There is an option for software exti.

  • Nicolas Leroy

    Hi again Tilen,

    Thank your for the tutorial. I’m actually trying to link my STM32F429 Disco to an AS3911B chip (RFID chup). Therefor, i need a SPI and an interrupt interface. No problem with the SPI, i managed to read/write any register. But i can’t get the interrupt interface to work. I think i configured everything well on my STM32F4, but i don’t get any response from the chip, the IRQ pin always stay at 0. I’ve already tried to make an internal interrupt and i worked well (with the user pushbutton). Any idea where this can come from ?

    • Well, it looks like your device does not trigger interrupt.
      Check it’s manual how to enable IRQ functionality.
      It’s not enabled by default probably.

      • Nicolas Leroy

        Indeed, first thing i need to do to “activate” the chip is to set a bit in a particular register. In fact it activates the quartz oscillator and it is supposed the send me an IRQ when the frequency of this oscillator is stable, but i never get it.

  • Clode

    Hi,

    I have a problem with my stm32f4 (I work with keil)…I was configured tow DC motos with PWM with TIM3 connected to PA7 PA6 , PB0 , and PB1 pins. To know the motors rotation I was use tow simple optical encoder that return numeric value 0V and 5V. I read the impulsion returned by encoder with EXTI (PA0 , PA1) . In the early when I turn the motors manually, everything works perfectly. But when they turn with PWM, one motor I that the tow encoder return values. I think there is some interference between PWM and EXT. Please so one could give me an idea to solve my problem. Thank you in advance.

    • Huh,
      I’m not sure about that, but can you provide me frequency from your encoder when this stop working?
      How fast your motor works?

      I think that the problem might be in handling so fast interrupts in high speed.
      You can use timer for encoder input capture too.

      • Clode

        Thank you for replying to my requst.
        When i configured my PWM i made 2000 for TIM3 period and 500 for prescaler.

        The first problem come wen i turn on the motor but when i rotate the weels manuelly i dont have a problem.

  • Victor Manuel Munoz

    Thank your for the tutorial.

  • M.A.A

    Thanks for your info sharing.
    I’m a newbie at arm. when i used your code presented above, the code is built without any error but after programming stm32f4discovery board with it, the PA0 user button doesn’t work. it seems that the interrupt doesn’t work at all. maybe i should add stm32f4xx_it.c to project. but after adding this file, it says that it connot find main.h file. how should i make main.h? what should i write in it?
    thanks again.

    • HI,

      You are free to delete main.h include from your file. Main.h was designed from ST in case user wants do some special defines or similar. Just delete it.

      According to my example, how you know that interrupt never happens to you? Did you run in debug? If not, try it.

      • M.A.A

        Hello, thank you for answering my question.

        after deleting main.h, an error occured for TimingDelay_Decrement() in stm32f4xx_it.c file and says that it is undefined. so i disabled it too and the compilation was completed. but after programming, the user switch which is supposed to turn the leds off as an exti doesn’t work.

        • In this example are leds not included.
          Let me check your code. If it is not confidential, then use pastebin.net and give me a link to main.c file so I can take a look at your work.

          • M.A.A

            Here is the code:

            http://pastebin.com/HfPFiUhD

            Not bad to say that i’m using IAR.
            thank you.

          • M.A.A

            I found the problem! PA0 was defined to be pull up. i don’t know why but when it is in pull up mode, pressing the push bottom takes no effect. by changing the mode to pull down it worked correctly. If you know the reason, i’m eager to understand.

          • Aa right.
            When button is pressed, logical 1 will be on pin, but when not, logical zero should be. In your case, there was 1 because of pullup and after you initialized pin exti was initialized and interrupt was not triggered.

          • M.A.A

            Thanks. but now i have another problem! sorry!

            by pressing push botton each time, the ISR is called multiple times. after adding the line “while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));” it wasn’t solved. i don’t know what to do. Here is the new code:

            http://pastebin.com/aixzbXX8

            sorry for my questions.

          • 2 causes:
            – Button debouncing
            – Set exti edge only to rising. Now you have rising and falling and at least 2 times ISR is called when you press and release a button.

          • M.A.A

            Yes, you are right. thank you very much for your help.

          • Tristan Robitaille

            Hi,

            I’m using the exact same code for testing purposes on my STM32F411E-DISCO board but nothing happens when I press the button. I suspect that the program crashes right after the first interrupt. Do you know what could be the problem?

          • This example does not init pins on discovery boards or nucleo.

          • Tristan Robitaille

            Well, the LEDs just stop circling when I press the user button. Everything seems to work before the interrupt but when I press the button, the board gets stuck to a LED.

  • Nicolas Leroy

    Hi Majerle 🙂

    Thank you for the tutorial.

    I’m actually trying to create a communication between my STM32F429 & an RFID Chip from AMS : AS3911B.

    My SPI works pretty well, but the chip also include an IRQ Pin which i can’t get to work. I think i configured it well on Keil, but i never get any answer from the chip (the IRQ pin never gets HIGH or my STM32 doesn’t see it, i don’t know)

    I use PF0 as IRQ Pin input

    Any idea how i could get this to work ?

    Here’s my code http://pastebin.com/Pnx1jeBt

    Thanks again !

    Nicolas Leroy

    • This paste has been removed 😉

      • Nicolas Leroy

        Woops, don’t know why, it works well for me
        Does this link work ? http://pastebin.com/8Xacfb3D

        • Things looks okay, except instead of pull up on pf pin, use pulldown and test.

          • Nicolas Leroy

            I’ve just tried, nothing changed. I also have a 10kOhm resistor between pf/Chip IRQ Pin and +Vcc (5V) (this is why i put pull up)

          • If you wanna handle rising edge interrupts, then your irq should be low, not high as in your case. Disconnect chip for rfid, enable pulldown and measure voltage on this pin using voltmeter.

          • Nicolas Leroy

            The IRQ Pin is low by default, and is set to high when particular events occures. So why should i use falling edge interrrupt? I force one of these events to happen but my IRQ pin stays low ( ~0V i have my oscilloscope plugged on that pin)

          • I’ve never said about falling edge!
            If you wanna use rising edge, then your pin should be LOW by default. When it will go high, interrupt will happen. So use pull-DOWN to have pin DOWN.

            It looks like your device does not makes interrupts at all!

          • Nicolas Leroy

            Oops i meant pull down instead of falling edge interrupt. In fact i’m not sure to understand the difference between pull UP & DOWN or even how it really works (pretty new to ucontroller world), because i used the same configuration for all my SPI pins (10k resistor & pull up) and it works well.

            Indeed, i dont know if the chip itself has a problem (dont think so) or if i just dont force the interrupt as i should

  • Anton Khrapov

    Hi Majerle,
    Could you please elaborate a bit regarding interrupts use? I want to read one full port GPIOE, for example, but I want it happen only when one pin 0 triggered on port GPIOD. Does that mean that interrupt would happen when GPIOD pin0 triggered and another time when the data comes to pin0 on GPIOE? If so, how do you avoid false interrupts?

    • If you enable irq for PD0, theb it wont happeb on PE0, because only one Px0 can be active at a time as irq.

      • Anton Khrapov

        Aha, thats what you do in here:
        SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource0); ?
        I was just concerned that if all pins of the same number from all ports connected to a single interrupt line, then triggering any pin from same line would result in interrupt.

        People who make these things are crazy. Scarely smart but crazy.

        • Yes, you are right 🙂 Only one pin at a time.

          Haha they are, very smart people 🙂

  • Vincenzo

    Hi,
    I need to simulate an external interrupt on PD0, which register i must set to do this?

    • Hi,

      EXTI->SWIER = GPIO_Line_x where x is your gpio number.

      • Vincenzo

        ok thank you

      • Vincenzo

        your solution don’t work. I tried to set the SWIER register but i can’t recognize the interrupt. Do you have a suggestion?

  • Pingback: STM32F4-Discovery Programming |()

  • Pingback: HAL library 4- EXTI for STM32Fxxx - STM32F4 Discovery()

  • Saeid Yazdani

    Hi and thanks for the tutorial. I wonder what would be the best case for the scenario below (if you can give me some tips, I really appreciate it). I want to connect 8 buttons to Px0 to Px7 and send the state of buttons through I2C (uisng a union/struct combo). What would be the best case so that every button press on any of the ports would trigger a custom function to read all the buttons and send the byte away? If I press Px0 the IRQ0 will fire up, but for Px1 this would not happen and I do not want to waste 7 IRQs for this scenario!

    • You can connect pins from Px8 to Px15 and use only 2 irq functions.

  • alexander della vedova

    stm32f4xx_exti.h is missing with mbed library
    you can make mbed version or coide version?

  • Ernest Selman

    Is there a way to have different interrupt for falling and rising edges?

    • No, but you can use if statement to check pin value

  • Andrew Shinn

    Great tutorial, it helped me get interrupt code working really quickly and easily, thank you.

  • Nabil

    Could you please tell me what is the purpose of EXTI? Why we need to multiplex? Isn’t it possible to send the interrupt directly to NVIC?

    • In NVIC you set interrupts to be handled by cpu.
      In exti, you tell which pin will be connected to be handled then by nvic.

      • Nabil

        Are they two different controller? Is EXTI available to all other boards other than STM32?

  • Denis Chernov

    Hello, if i use multiple pins PB12-PB14 for EXTI, how to determinate which Pin cause an interrupt?

    I used :

    if(EXTI_GetFlagStatus(EXTI_Line12)!= RESET)

    {

    EXTI_ClearFlag(EXTI_Line12);

    EXTI_ClearITPendingBit(EXTI_Line12);

    return;

    }

    if(EXTI_GetFlagStatus(EXTI_Line13)!= RESET)

    {

    EXTI_ClearFlag(EXTI_Line13);

    EXTI_ClearITPendingBit(EXTI_Line13);

    return;

    }

    if(EXTI_GetFlagStatus(EXTI_Line14)!= RESET)

    {

    EXTI_ClearFlag(EXTI_Line14);

    EXTI_ClearITPendingBit(EXTI_Line14);

    return;

    }

    but it is not correct

    • Rules.

      • Denis Chernov

        Sorry

        • Check example what I used in IRQ handler.

          • Denis Chernov

            if I understand correctly “if (EXTI_GetITStatus(EXTI_Line12) != RESET)” checks which bit caused an interrupt, i use same expression but i never comes into the body condition ‘if’

          • then 2 possibilities are here probably:
            1. wrong initialization
            2. code in wrong interrupt handler

            Btw..how you know if interrupt has actually occur?

          • Denis Chernov

            I see “1” in EXTI -> PR in debug mode. Yea, you are right there was a mistake in init EXTI. Now i have another question , does PR bit cleared automatically after EXTI_ClearITPendingBit(EXTI_Line12)? In my CoIDE it stays in ‘1’

          • It is cleared after you call this function.

  • Insaf

    /* PB12 is connected to EXTI_Line12, which has EXTI15_10_IRQn vector */
    Hello,
    is the name of the function “EXTI15_10_IRQn” a convention or you choose it ?

    • ST chose it in their library.

      • Insaf

        where can i find it ?

        • Find what? Function must be created like in post described.

        • kuds

          Check out stm32f4xx.h for all the names [STRG]+[F] IRQn

  • praveen kumar

    hi Majerle,

    I am using DRDY(int1) signal from sensor to indicate a new data is ready for processing .This INT1 pin is attached to PIN PE0 in the discovery board , which I have configured as ext line interrupt for line 0 . I am not getting the interrupt at the expected duration for ex: if ODR = 100Hz , then interrupt should come at 10ms but instead pin is showing it is coming at 9.6ms.

    Could you please help me out with this problem

    • If interrupts are there, then problem is your calculation process. I’m sure you are doing it wrong.

      • praveen kumar

        can you please guide me which calculation process you are referring to , is it related to timer related calculation in controller or LIS3DSH sensor.
        Please guide me which all parameters I shall look into.

        • I can’t because I don’t see code. How sure you are about fact that LIS really outputs 100Hz and not 102Hz data rate? Or similar?

          • praveen kumar

            Thanks for your reply!!
            My reference in Sensor Datasheet which mentions ODR 100Hz. I believe it shall be precise 100Hz.
            Have you seen any cases where output rate varying than specified? I am using MEMS for first time.
            What option shall I use if I want to have precise 100Hz Output data rate.

          • Use logic analyzer to be sure! Output data rate depends on input clock of LIS. If it is not accurate then output won’t be as well.

            And btw..you are offtopic. Use another post to disqus about LIS settings!

          • praveen kumar

            Thanks for you valuable inputs 🙂

  • Dinushan Paranavithana

    Thanks a Lot! very helpful 🙂

  • Christina Kim

    Thanks! It is very helpful to me!
    Incidentally, Could you tell me what font do you use in your code?

  • Szabolcs

    Hello,

    I’m new in area of ST microcontrollers and I wolud like to ask to help
    me if it is possible. So I have a project, where I need the EXTI
    function of GPIO’s. I use the STM32CubeMX to define the pins. This
    program generate the HAL(Hardware Abstraction Layer) functions which
    make easy the development. Use of these functions I didn’t resolve the
    EXTI problem. The program doesn’t go to the EXTI interrupt handler. For
    resolve this problem I cut out all of complex features of my program and
    made a new test project, where I use the LED’s of development board and
    some GPIO’s, but isn’t successfull. Can You help me?

    Thank You,Szabolcs

    • From know info, I’m unable to give you useful info.
      But, is PIN value changed?

      • Szabolcs

        Yes. Now I resolved the problem. The EXTI interrupt didn’t enable in the NVIC, and the Cube generated the project without this.Excuse me for this post.

  • Thinh

    Hi Majerle Tilen, i wanna read data from ultrasonic sensor to control motor speed. Can you help me ?

  • tino1b2be

    Thank you for this tutorial. Really helps out a lot. I want to configure 16 different GPIO pins to trigger interrupts. Since you mentioned that I can have several pins on the same line (hence they will use the same handlers) is there a way to check which pin actually triggered the interrupt. For example, if PA0 and PD0 are both set to trigger interrupts, since they use the same handler, is there a way to find out which pin was pushed?

    EDIT: I’m using STM32F4 Discovery

    • It is obvious you did not read post at all or not carefully. Pins PA0 and PD0 CANNOT have enable interrupt source at the same time. Only one pin on ports with the same number can be active at a time.
      So PA0 and PD0 at the same time is not possible.
      However, you can do it using PA0 and PD1 or PA0 and PA1 and so on.

      • tino1b2be

        Oh ok. Sorry I misread that line.

        I need to have 16 (or 25 if possible) different GPIO triggered interrupts. Any suggestions on how I can do this?

        • Maximal 16 lines is possible with STM32.
          And all of that lines must have different number: PA0, PA1, PB2, PC5, PD4, etc etc

      • tino1b2be

        Since line 5 to 9 are handled by the same handler, how can I tell which pin triggered the interrupt?

        • EXTI flag is your friend. Check how is done in EXTI lib 38 on this page.

          • tino1b2be

            Awesome library man! Thank you

  • James

    Hi Tilen.

    I have copied your code exactly, and the interrupt for PB12 seems to trigger both the EXTI0 and EXTI_15_10 interrupt handlers. What do you think the problem might be?

    • James

      Also, the PD0 interrupt only makes the EXTI0 interrupt handler execute, so the behavior is only for the second interrupt. Tried changing From PB12 to PB1 and still same behavior.

      • Can you verify that your pb0 pin is not floating or similar? It is not possible on theory what you said.

        • James

          Thank you. PB0 was floating. It works perfectly now.

        • James

          Nevermind. Even though pb0 is now grounded, the EXTI0 interrupt handler is still occasionally executed when PB12 goes high. Any other reasons?

          • The only reason and possibility is that PB0 is changing. Or that another Px0 pin is connected to EXTI 0 port.

  • Daniel Matthes

    Hi, thanks for your tutorial. I’ve copied your code, but it doesn’t work. When I force an interrupt by pressing the push button EXTI->PR and NVIC->ABR0 will be set, but the IRQHandler will not be called. After pausing the debugging I can see that the debugger is locked in the Infinite Loop section of the startup_stm32 code. Where is my fault?