Interrupts, when they occur execute code found in a memory location (which is reserved for that interrupt), save the state of the MCU, execute the code, then restore the MCU state. There are many other optional factors affecting interrupts, such as priority, triggers and so on.


  • In the Cortex-M programming manual, an Exception is anything that breaks the normal program flow, and invokes a handler from the vector table. Interrupts are a subset of Exceptions, coming from the peripherals outside the ARM core.
  • Exceptions have an Exception Number, starting from 0.
  • Interrupts have an IRQ Number, starting from 0.
  • Because all Interrupts are Exceptions, they all get an Exception Number, which is 16 higher than the IRQ Number. The Exceptions that are not Interrupts (including SysTick) have Exception Numbers in the 0-15 range, smaller than the Exception Numbers of the Interrupts. Somewhat confusingly, Exceptions that are not Interrupts have IRQ Numbers too, which by extension fall into the range from -16 to -1.
  • Because SysTick is implemented in the Cortex-M core, it is considered an exception, but not an interrupt having a Exception Number of 15, and IRQ Number -1.


Systick, being a Cortex-M core, and not a STM32F peripheral has its own set-pending and clear-pending bits in the SCB_ICSR as below.

SCB_ICSR () $00000000
*A set-pending bit for the Non-Maskable Interrupt NMI exception
*Set-pending and clear-pending bits for the PendSV and SysTick
*The exception number of the exception being processed
*Whether there are preempted active exceptions
*The exception number of the highest priority pending exception
*Whether any interrupts are pending.
N                 I
M     P P P P     S
I     E E E E     R
P     N N N N     P
E     D D D D     E
N     S S S S     N
D     V V T T     D
S     S C S C     I
E     E L E L     N
T     T R T R     G        |------VECT PENDING-----|VECT ACTIVE
3|   |2|2|2|2|   |2|       |1|1|1|1|1|1|           |-----------
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Cortex M0 Interrupt Flowchart


EXTI Lines 16 - 31

EXTI Line Connected To
EXTI line 16 PVD output
EXTI line 17 RTC Alarm event
EXTI line 18 internal USB wakeup event
EXTI line 19 RTC Tamper and TimeStamp events
EXTI line 20 RTC Wakeup event (available only on STM32F07x and STM32F09x devices)
EXTI line 21 Comparator 1 output
EXTI line 22 Comparator 2 output
EXTI line 23 internal I2C1 wakeup event
EXTI line 24 reserved (internally held low)
EXTI line 25 internal USART1 wakeup event
EXTI line 26 internal USART2 wakeup event (available only on STM32F07x and STM32F09x devices)
EXTI line 27 internal CEC wakeup event
EXTI line 28 internal USART3 wakeup event (available only on STM32F09x devices)
EXTI line 29 reserved (internally held low)
EXTI line 30 reserved (internally held low)
EXTI line 31 VDDIO2 supply comparator output (available only on STM32F04x, STM32F07x and STM32F09x devices


There are 16 ‘EXTI lines’ (EXTI0-15). Each line is driven by the same numbered bit of all GPIOs. As can be seen, six GPIOS drive the same bit numbered ‘EXTI line’ but only one GPIO can be selected per EXTI.


TABLE B: Interrupt and Exception Vectors

Each Interrupt Source matches a “Bit Position” value (see explanation below) and these are used to ‘hook’ into the ISR. How it works is shown in the example program.


Depending on part number our your device may not have all the peripherals listed in this table.

Bit Pos- Priority Type of Priority Interrupt Source Description Address
. . . . . 0x0000 0000
. -3 fixed Reset Reset 0x0000 0004
. -2 fixed NMI Non maskable interrupt. 0x0000 0008
. -1 fixed HardFault All class of fault 0x0000 000C
. 3 settable SVCall System service call via SWI instruction 0x0000 002C
. 5 settable PendSV Pendable request for system service 0x0000 0038
. 6 settable SysTick System tick timer (SPECIAL SEE ABOVE) 0x0000 003C
. 7 settable WWDG Window watchdog interrupt 0x0000 0040
1 8 settable PVD_VDDIO2 PVD and VDDIO2 supply comparator interrupt (combined EXTI lines 16 and 31) 0x0000 0044
2 9 settable RTC RTC interrupts (combined EXTI lines 17, 19 and 20) 0x0000 0048
3 10 settable FLASH Flash global interrupt 0x0000 004C
4 11 settable RCC_CRS RCC and CRS global interrupts 0x0000 0050
5 12 settable EXTI0_1 EXTI Line[1:0] interrupts 0x0000 0054
6 13 settable EXTI2_3 EXTI Line[3:2] interrupts 0x0000 0058
7 14 settable EXTI4_15 EXTI Line[15:4] interrupts 0x0000 005C
8 15 settable TSC Touch sensing interrupt 0x0000 0060
9 16 settable DMA_CH1 DMA channel 1 interrupt 0x0000 0064
10 17 settable DMA_CH2_3 DMA channel 2 and 3 interrupts 0x0000 0068
10 17 settable DMA2_CH1_2 DMA2 channel 1 and 2 interrupts 0x0000 0068
11 18 settable DMA_CH4_5_6_7 DMA channel 4, 5, 6 and 7 interrupts 0x0000 006C
11 18 settable DMA2_CH3_4_5 DMA2 channel 3, 4 and 5 interrupts 0x0000 006C
12 19 settable ADC_COMP ADC and COMP interrupts (ADC interrupt combined with EXTI lines 21 and 22) 0x0000 0070
13 20 settable TIM1_BRK_UP_TRG_COM TIM1 break, update, trigger and commutation interrupt 0x0000 0074
14 21 settable TIM1_CC TIM1 capture compare interrupt 0x0000 0078
15 22 settable TIM2 TIM2 global interrupt 0x0000 007C
16 23 settable TIM3 TIM3 global interrupt 0x0000 0080
17 24 settable TIM6_DAC TIM6 global interrupt and DAC underrun interrupt 0x0000 0084
18 25 settable TIM7 TIM7 global interrupt 0x0000 0088
19 26 settable TIM14 TIM14 global interrupt 0x0000 008C
20 27 settable TIM15 TIM15 global interrupt 0x0000 0090
21 28 settable TIM16 TIM16 global interrupt 0x0000 0094
22 29 settable TIM17 TIM17 global interrupt 0x0000 0098
23 30 settable I2C1 I2C1 global interrupt (combined with EXTI line 23) 0x0000 009C
24 31 settable I2C2 I2C2 global interrupt 0x0000 00A0
25 32 settable SPI1 SPI1 global interrupt 0x0000 00A4
26 33 settable SPI2 SPI2 global interrupt 0x0000 00A8
27 34 settable USART1 USART1 global interrupt (combined with EXTI line 25) 0x0000 00AC
28 35 settable USART2 USART2 global interrupt (combined with EXTI line 26) 0x0000 00B0
29 36 settable USART3_4 USART3 and USART4 global interrupts 0x0000 00B4
30 37 settable CEC_CAN CEC and CAN global interrupts (combined with EXTI line 27) 0x0000 00B8
31 38 settable USB USB global interrupt (combined with EXTI line 18) 0x0000 00BC

Bit Position ?

The Bit Position is actual Interrupt value, and used to ‘hook’ the Interrupt to a ‘Interrupt Handler’.


Bit Position: 5 = %100000 = 20 Hecadecimal and applies to EXTI0_1

Bit Position 5 in Binary


Hardware Interrupt Selection

To configure a line as interrupt source, use the following procedure:

  1. Choose one of the 16 GPIO EXTI mapping groups to suit your GPIO pin from TABLE A above.
  2. Choose the GPIO for the selected EXTI.
x000: PA[x] pin
x001: PB[x] pin
x010: PC[x] pin
x011: PD[x] pin
x100: PE[x] pin
x101: PF[x] pin

I.E. GPIOC-2 would be:

%X010  8  lshift $40010008  bis!  \ SYSCFG_EXTICR1_EXTI2


To configure any SYSCFG registers like the one above, SYSCFGEN must be enabled in the RCC Register. If it isn’t then only GPIOA works as a trigger for EXTI !

  1. Configure the interrupt mask bit in the EXTI_IMR register.
  2. Configure the Trigger Selection bits of the Interrupt line (EXTI_RTSR and EXTI_FTSR)
  3. Configure the enable and mask bits that control the NVIC IRQ channel mapped to the EXTI used in 1) above.
  4. Set the priority for the interrupt vector in question in the NVIC through the IPR0-IPR7 registers.
  5. Enable the interrupt in the NVIC_ISER_SETENA, use the Bit Position from TABLE B to suit your Interrupt Source. If it’s a EXTI Line Interrupt group, then it will be Bit Position 5 ($20), 6 ($40) or 7 ($80).
  6. Write your interrupt service routine (ISR)

Hardware event selection

To configure a line as event source, use the following procedure:

Configure the corresponding mask bit in the EXTI_EMR register.
Configure the Trigger Selection bits of the Event line (EXTI_RTSR and EXTI_FTSR)

Software interrupt/event selection

Any of the external lines can be configured as software interrupt/event lines. The following is the procedure to generate a software interrupt.

Configure the corresponding mask bit (EXTI_IMR, EXTI_EMR)
Set the required bit of the software interrupt register (EXTI_SWIER)

Interrupt Service Routine

Inside your interrupt service routine, check the source of the interrupt...either the GPIO pin directly or the external interrupt line. Once you figure out which one triggered the interrupt, perform the interrupt processing scheme associated with it. Make sure that you clear the corresponding pending bit of the external interrupt lines of interest in the EXT_PR (external interrupt pending register) register by writing a ‘1’ to it. [hertaville]

Forth Interrupt Methodology

Mecrisp-Stellaris uses the “tick” (‘) Word to obtain the memory location of the “Interrupt Handler” Word or ISR to be executed when the interrupt occurs via a ‘interrupt hook’.

['] interrupt-handler irq-exti0_1 !        \ See the example program for more information

“interrupt-handler” is the ISR, but where does the “irq-exti0_1” interrupt hook come from ?

It’s in the Dictionary for your chip, listed with all the available interrupt hooks. The interrupt hooks for a STM32F051 are listed here.


Address: 000047C8 Link: 000047EC Flags: 00000081 Code: 000047DA Name: irq-systick
Address: 000047EC Link: 00004810 Flags: 00000081 Code: 000047FC Name: irq-fault
Address: 00004810 Link: 00004838 Flags: 00000081 Code: 00004826 Name: irq-collection
Address: 00004838 Link: 0000485C Flags: 00000081 Code: 0000484A Name: irq-exti0_1
Address: 0000485C Link: 00004880 Flags: 00000081 Code: 0000486E Name: irq-exti2_3
Address: 00004880 Link: 000048A8 Flags: 00000081 Code: 00004894 Name: irq-exti4_15
Address: 000048A8 Link: 000048C8 Flags: 00000081 Code: 000048B6 Name: irq-tsc
Address: 000048C8 Link: 000048EC Flags: 00000081 Code: 000048DA Name: irq-dma_ch1
Address: 000048EC Link: 00004914 Flags: 00000081 Code: 00004900 Name: irq-dma_ch2_3
Address: 00004914 Link: 0000493C Flags: 00000081 Code: 00004928 Name: irq-dma_ch4_5
Address: 0000493C Link: 0000495C Flags: 00000081 Code: 0000494A Name: irq-adc
Address: 0000495C Link: 00004980 Flags: 00000081 Code: 0000496E Name: irq-tim1_up
Address: 00004980 Link: 000049A4 Flags: 00000081 Code: 00004992 Name: irq-tim1_cc
Address: 000049A4 Link: 000049C8 Flags: 00000081 Code: 000049B4 Name: irq-tim2
Address: 000049C8 Link: 000049EC Flags: 00000081 Code: 000049D8 Name: irq-tim3
Address: 000049EC Link: 00004A14 Flags: 00000081 Code: 00004A00 Name: irq-tim6_dac
Address: 00004A14 Link: 00004A38 Flags: 00000081 Code: 00004A24 Name: irq-tim14
Address: 00004A38 Link: 00004A5C Flags: 00000081 Code: 00004A48 Name: irq-tim15
Address: 00004A5C Link: 00004A80 Flags: 00000081 Code: 00004A6C Name: irq-tim16
Address: 00004A80 Link: 00004AA4 Flags: 00000081 Code: 00004A90 Name: irq-tim17
Address: 00004AA4 Link: 00004AC8 Flags: 00000081 Code: 00004AB4 Name: irq-i2c1
Address: 00004AC8 Link: 00004AEC Flags: 00000081 Code: 00004AD8 Name: irq-i2c2
Address: 00004AEC Link: 00004B10 Flags: 00000081 Code: 00004AFC Name: irq-spi1
Address: 00004B10 Link: 00004B34 Flags: 00000081 Code: 00004B20 Name: irq-spi2
Address: 00004B34 Link: 00004B58 Flags: 00000081 Code: 00004B46 Name: irq-usart1
Address: 00004B58 Link: 00004B7C Flags: 00000081 Code: 00004B6A Name: irq-usart2
Address: 00004B7C Link: 00004C14 Flags: 00000081 Code: 00004B8E Name: irq-cec_can

STM32F0 Discovery Board Interrupt Example Program

\ ---------------------------------------------------------------------------------------- \
\ Program Name: f0-disco-pb-interrupt.fs
\ Date: Fri 21 Dec 2018 11:56:46 AEDT
\ Copyright 2018  t.porter <>, licensed under the GPL
\ For Mecrisp-Stellaris by Matthias Koch.
\ Chip: STM32F051, Board: STM32F0 Discovery Board
\ Clock: 48 Mhz using the internal STM32F051 RC clock and PLL.
\ Multitasking is in use with multitask.fs so the terminal is responsive while program is running
\ All register names are CMSIS-SVD compliant
\ Note: gpio a,b,c,d,e, and uart1 are enabled by Mecrisp-Stellaris Core.
\ This demo uses a interrupt to:
\ 1) While the user push button (gpioa-0) is pressed, the green led is lit.
\ 2) The terminal displays a total push button count.
\ ---------------------------------------------------------------------------------------- \

0 variable button.presses
0 variable button.pressed.flag

: SYSCFG_EXTICR1_EXTI0 ( -- )  %0000  0  lshift $40010008  bis! ;  \ SYSCFG_EXTICR1_EXTI0 PA[0] pin NOTE:
: EXTI_IMR_MR0-SET ( -- )  %1  0  lshift $40010400  bis! ;  \ EXTI_IMR_MR0 SET Interrupt Mask on line 0
: EXTI_IMR_MR0-RESET ( -- ) %1  0  lshift $40010400  bic! ; \ EXTI_IMR_MR0 RESET Interrupt Mask on line 0
: EXTI_RTSR_TR0-SET ( -- )  %1  0  lshift $40010408  bis! ; \ EXTI_RTSR_TR0 Rising trigger event configuration of  line 0
: EXTI_PR_PR0-SET ( -- )  %1  0  lshift $40010414  bis! ;   \ EXTI_PR_PR0 interrupt pending flag for line 0
: EXTI0_1-ENABLE ( -- )  %00000000000000000000000000100000 0  lshift $E000E100  bis! ; \ NVIC_ISER_SETENA
: USERPB? ( -- ) %1  0  lshift $48000010  bit@ ;             \ GPIOA_IDR_IDR0 | user pb pressed ?

: interrupt-handler ( -- )
  EXTI_IMR_MR0-RESET                        \ disable Interrupt Mask on line 0
  EXTI_PR_PR0-SET                           \ clear External Interrupt Pending register (EXTI_PR)
        USERPB? if                          \ has PB been pressed ?
           begin                            \ yes
              5 ms                          \ PB debounce delay
              USERPB? not                   \ exit when PB no longer pressed
     green-off                              \ no, PB not pressed
  button.presses @ 1+ button.presses !      \ increment count total by one press
  1 button.pressed.flag !
  EXTI_IMR_MR0-SET                          \ re-enable Interrupt Mask on line 0

: interrupts.init ( -- )
  %0000 SYSCFG_EXTICR1_EXTI0                \ EXTI0 SELECT GPIOA-0. To select
  EXTI_IMR_MR0-SET                          \ EXTI0 SET Interrupt Mask on line 0
  EXTI_RTSR_TR0-SET                         \ EXTI0 rising edge trigger
  ['] interrupt-handler irq-exti0_1 !       \ tie the interrupt handler Word to the irq-exti0_1 interrupt hook

: main  ( -- )                              \ Words in main are executed about *every* 5uS (48 Mhz system clock) unless blocked
  pause                                     \ give control to multitasker
     button.pressed.flag @  if              \ only print button press count total when user PB is pressed
        ." button.presses = " button.presses @ . cr
        0  button.pressed.flag !

: init-general ( -- )  \ Words in init-general are executed *once* at boot up.
  f0-disco-48mhz       \ Switch system clock from 8 to 48 MHz
  interrupts.init      \ initialize all interrupt related registers
  EXTI0_1-ENABLE       \ Enable EXTI0_1 interrupt

 task: maintask     \ essential multitasking Word
: main& ( -- )      \ multitasking.fs Word
  maintask activate \ multitasking.fs Words
  begin main again  \ this is the only task, and it loops forever (about every 5uS @ 48Mhz clock) unless blocked

: init ( -- )       \ essential multitasking Word
  init-general      \ main user application inits
  multitask         \ multitasking.fs Word
  main&             \ multitasking.fs Word

init                \ this init for non flash use, i.e. after uploading this program
button.presses = 1
button.presses = 2
button.presses = 3
button.presses = 4
button.presses = 5
button.presses = 6