Interrupt Vector Table and Interrupt Handlers

Interrupt Vector Table

If you use one of the IDE new project wizards to generate a skeletal project, then the interrupt vector table is included in the startup file. The “wizard” adds the startup file to the project so if you modify it, a Project->Build will rebuild the file.



Writing an Interrupt Handler

Unlike some other CPUs such as the Atmel AVR, you can write ARM Cortex interrupt handlers as normal C functions with no assembly code or other extended keywords. Simply declare them similar to this:


void myInterruptHandler(void);      // declaration


void myInterruptHandler(void)       // definition / implementation


      // do something



NOTE: be careful and not define an interrupt handler function in a .cpp file, as that would make it into a C++ function, and the internal function name is “mangled” and will not be the same as the name you write. You will get a link error when you build your project. If you must define the function in a .cpp file, then you must prefix it with the extern “C” keywords:


// extern “C” needed only if defined in a .cpp file

extern “C” void mySysTick_Handler(void)




Example of Adding an Interrupt Handler

For example, let’s say you want to change the SysTick handler to a function named mySysTick_Handler. First, you would modify the vector table in the startup file to as follows:


GCC Weak Attribute

Normally, you can only provide a single definition of a function, and if a function is defined multiple times, the compiler/linker will complain.


GCC allows a function to be defined as “weak”. For example:


void SomeFunction(void) __attribute((weak))__




When linking the program, if the only definition of a function is a weak one, then that definition will be used. However, if there is another definition of the same function but without the weak attribute, then the non-weak definition will be used instead of the weak one, and the linker will not complain about multiple definitions.


This is particularly useful for filling in entries for the interrupt vector table. If you define handlers with weak attributes as stubs for the entries, then you can write “real handler code” at any time, without having to modify the interrupt vector table, or having to remove stub functions from the project.


For example, in the startup file, you may see




 *  Weak definition for exceptions.

 *  Any function with the same name will override the weak definition.


/*  Macro to define default handlers. Default handler

 *  will be weak symbol and just dead loops. They can be

 *  overwritten by other handlers */

    .macro    def_default_handler    handler_name

    .align 1


    .weak    \handler_name

    .type    \handler_name, %function

\handler_name :

    b    .

    .size    \handler_name, . - \handler_name



    def_default_handler    NMI_Handler

    def_default_handler    HardFault_Handler

    def_default_handler    MemManage_Handler

    def_default_handler    BusFault_Handler

    def_default_handler    UsageFault_Handler

    def_default_handler    SVC_Handler

    def_default_handler    DebugMon_Handler

    def_default_handler    PendSV_Handler

    def_default_handler    SysTick_Handler

    def_default_handler    Default_Handler


    .weak   DEF_IRQHandler

    .set    DEF_IRQHandler, Default_Handler




We will not go over the assembly macros in detail, but the effect of this block of code is to define the weak definitions for functions named NMI_Handler, HardFault_Handler, etc. You can then either use the default one as provided (which does an infinite loop), or you can provide a “real” function with the same name in your .c file, and it will automatically override the weak one defined in the startup file.