I am working with an STM32 and I am a bit confused about interrupts, specifically the Nested Vectored Interrupts (NVI). As I understand there is a NVI vector (called NVIC) where each interrupt has a priority (sometimes settable), and an address (see page 157 of the ARM reference manual here).

Now I am assuming that for each type of interrupt it is possible to attach a callback function, and I suspect that the attached address to each interrupt is related to the address of the callback.

What exactly is the address attached to a given interrupt? How can I define (in C, say) the callback function for a given interrupt?

  • \$\begingroup\$ don't know 1st-hand about the arm, but many processors have a special return-from-interrupt instruction, that peels the extra information off the stack that usually comes with an interrupt. Only a real concern if you're writing assembler, though; most C compilers allow you to decorate your function with the word 'interrupt', and the compiler takes care of choosing the correct exit code. \$\endgroup\$ – JustJeff Apr 20 '12 at 23:15
  • \$\begingroup\$ @JustJeff: Could you point to documentation regarding this 'interrupt' decoration? \$\endgroup\$ – Randomblue Apr 20 '12 at 23:34
  • \$\begingroup\$ it's usually trivial - just stick the word 'interrupt' ahead of the function return type. So instead of void foo() { ... }, you'd have interrupt void foo() { ... } \$\endgroup\$ – JustJeff Apr 20 '12 at 23:45
  • \$\begingroup\$ @JustJeff Just to note that the syntax for defining an interrupt is dependent on the compiler/linker combo and not the target processor. For instance, the ARM GCC compiler and ARM IAR compiler may define the syntax for an interrupt function differently despite the fact that they're building code for the same processor. \$\endgroup\$ – AngryEE Apr 24 '12 at 23:32
  • \$\begingroup\$ @AngryEE - I don't think I actually said it depends on the target architecture. But yes, the particulars may vary with circumstances. I just wanted to point up the fact that there's usually something you need to do, since the stack gets set up a little different for interrupts than it does for regular calls. \$\endgroup\$ – JustJeff Apr 24 '12 at 23:49

The ARMs implement an interrupt table to store the address for each interrupt handler (or callback, basically the same thing). Basically, all of the addresses of the interrupt handlers are stored in program memory at a predefined location. When an interrupt occurs, the processor knows where in the table that interrupt's entry is, grabs it and branches to the address stored there.

How do you populate this table? Usually all this information is contained in a startup file. Typically you just define a constant array of pointers, fill it with the addresses of the callbacks you want to use. Here is a website detailing how to create a startup file for a specific ARM microcontroller. The tricky thing about this is that almost all of the particulars of making the file depend heavily on the linker, compiler and chip you're using, so you'll either have to find an example out there or muddle through making your own.


the cortex-m3 is very interrupt friendly. You dont need a trampoline in asm or need to add any non-standard compiler syntax to make the compiler do it for you. The hardware conforms to an abi by preserving a certain number of registers for you as well as changing modes. The link register is encoded so that upon return from the function the hardware knows it is actually a return from interrupt. So all the things you had to do on an arm and many other processors you do not have to do.

Likewise, to a level of pain, the cortex-m (and other newer arms) have a zillion vectors in the vector table, dozens to hundreds of interrupts, etc. as mentioned in a comment above see http://github.com/dwelch67/stm32f4d the blinker05 example uses interrupts with a timer, you can see in vectors.s that all you do is place the name of the function:

.word hang
.word tim5_handler
.word hang

And then write the C code:

volatile unsigned int intcounter;
void tim5_handler ( void )

Now as with any interrupt from some peripheral/device you probably have to, in the handler, tell the device to clear the interrupt otherwise you might get stuck constantly re-entering the handler.

My examples dont use an IDE, they use open source tools (gnu gcc and binutils and llvm's clang compiler). Remove the clang binaries from the all: line in the makefile if you dont have/want to use llvm. The gnu tools are easy to come by, both building from sources (I have instructions somewhere at github, probably a number of places) or just get the lite version from codesourcery (now mentor graphics). My examples are developed and tested on linux, but that shouldnt discourage windows users, change a few things like rm -f *.o to del *.o, things like that or just build a batch file from the assembler/compiler/linker commands in the makefile.

I very highly recommend disassembling your binary, esp if you are trying to place a handler in the vector table, with so many it is easy to miscount and not have it at the right address. You need to know the address from the arm docs then check the disassembly. the blinker05 example when disassembled:

 8000100:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000108:       08000179        stmdaeq r0, {r0, r3, r4, r5, r6, r8}

offset 0x108 being the targeted entry. Note that the addresses in the table should be odd 0x178 is the actual address, arm wants the lsbit set to indicate it is a thumb instruction set address (the cortex-m3 only knows thumb and the thumb2 extensions it cannot execute arm instructions).


Your Answer

