In Cortex-M3, there are 255 interrupt vectors, and it’s relocateable. During the initial boot, the interrupt vector table are located at 0×00, but, then, if you want to, you can move it to somewhere else.
The first ten interrupts are fixed by ARM (means you will always find them eventhough you’re using NXP, Atmel, STMicro, Luminary Micro, etc). The rest is up to the silicon vendors implementation.
The interrupt handler is a bit different from any other MCUs. You just need to write the interrupt handler address, without any additional code (such as: B <interrupt_handler_addr> to jump to specific address). Also, the first thing on your interrupt table is the stack address. The Cortex-M3 interrupt controller (NVIC) will need stack address before it can jump to the handler. Hence, it’s put as the first thing on the interrupt table.
Here’s how to implement it
/* Simple startup file for Cortex-M3 */
/* Thumb-2 instructions are only supported in unified syntax mode */
.syntax unified
/* Vector table definition */
.section “.cs3.interrupt_vector”
.long __cs3_stack /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler */
.long HardFault_Handler /* Hard Fault Handler */
.long MemManage_Handler /* MPU Fault Handler */
.long BusFault_Handler /* Bus Fault Handler */
.long UsageFault_Handler /* Usage Fault Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long SVC_Handler /* SVCall Handler */
.long DebugMon_Handler /* Debug Monitor Handler */
.long 0 /* Reserved */
.long PendSV_Handler /* PendSV Handler */
.long SysTick_Handler /* SysTick Handler */
/* Vendor specific interrupts
* —- Not implemented
*/
/* Program section */
.section “.text”
/* Declare as thumb function. Otherwise it will not be linked
* correctly
*/
.thumb_func
/* Export the symbol so linker can see this */
.global Reset_Handler
Reset_Handler:
/* Jump to main(), a thumb function */
LDR R0, =main
BX R0
/* If main() ever exit, this should hold MCU from running wild */
B .
/* This is how the lazy guy doing it: by aliasing all the
* interrupts into single address
*/
.thumb_func
NMI_Handler:
.thumb_func
HardFault_Handler:
.thumb_func
MemManage_Handler:
.thumb_func
BusFault_Handler:
.thumb_func
UsageFault_Handler:
.thumb_func
SVC_Handler:
.thumb_func
DebugMon_Handler:
.thumb_func
PendSV_Handler:
.thumb_func
SysTick_Handler:
B . /* while(1); */
You don’t need to write your interrupt handler in assembly. Here’s simple way to write your Reset_Handler() in C:
/*
* Don’t forget to take out the Reset_Handler() on the startup file first
*/
extern int main(void);
void Reset_Handler(void) {
main();
// If main() ever exit, this should hold MCU from running wild
while(1);
}
But some people has found a way to code everything in C (instead of assembly and C) using callback function mechanism
/*
* startup_LPC1766.c – Startup file for LPC1766 Cortex-M3
*
* @author: kunilkuda_at_gmail_dot_com
*/
extern void __cs3_stack; ///< This is global variable from linker script
/* We gave ‘weak’ attribute, so these functions can be aliased into a single
* function
*/
void __attribute__((weak)) Reset_Handler(void); /* Reset Handler */
void __attribute__((weak)) NMI_Handler(void); /* NMI Handler */
void __attribute__((weak)) HardFault_Handler(void); /* Hard Fault Handler */
void __attribute__((weak)) MemManage_Handler(void); /* MPU Fault Handler */
void __attribute__((weak)) BusFault_Handler(void); /* Bus Fault Handler */
void __attribute__((weak)) UsageFault_Handler(void);/* Usage Fault Handler */
void __attribute__((weak)) SVC_Handler(void); /* SVCall Handler */
void __attribute__((weak)) DebugMon_Handler(void); /* Debug Monitor Handler */
void __attribute__((weak)) PendSV_Handler(void); /* PendSV Handler */
void __attribute__((weak)) SysTick_Handler(void); /* SysTick Handler */
/*
* Ask GCC to put this array into .cs3.interrupt_vector section
*/
__attribute__ ((section(“.cs3.interrupt_vector”)))
void (* const g_pfnVectors[])(void) = {
&__cs3_stack, // The initial stack pointer
Reset_Handler, // The reset handler
NMI_Handler, // The NMI handler
HardFault_Handler, // The hard fault handler
MemManage_Handler, // The MPU fault handler
BusFault_Handler, // The bus fault handler
UsageFault_Handler, // The usage fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
SVC_Handler, // SVCall handler
DebugMon_Handler, // Debug monitor handler
0, // Reserved
PendSV_Handler, // The PendSV handler
SysTick_Handler, // The SysTick handler
};
/* Here’s how to alias multiple functions into single function
*/
void Default_Handler(void);
#pragma weak NMI_Handler = Default_Handler /* NMI handler */
#pragma weak HardFault_Handler = Default_Handler /* Hard Fault handler */
#pragma weak MemManage_Handler = Default_Handler /* MPU Fault Handler */
#pragma weak BusFault_Handler = Default_Handler /* Bus Fault Handler */
#pragma weak UsageFault_Handler = Default_Handler /* Usage Fault Handler */
#pragma weak SVC_Handler = Default_Handler /* SVCall Handler */
#pragma weak DebugMon_Handler = Default_Handler /* Debug Monitor Handler */
#pragma weak PendSV_Handler = Default_Handler /* PendSV Handler */
#pragma weak SysTick_Handler = Default_Handler /* SysTick Handler */
void Default_Handler(void) {
while (1);
}
extern int main(void);
void Reset_Handler(void) {
main();
// If main() ever exit, this should hold MCU from running wild
while(1);
}
For any method that you use, the most important part is defining your linker script to layout the code into the correct place.
/*
* Define the supported output formats – elf32-littlearm is the
* default
*/
OUTPUT_FORMAT(“elf32-littlearm”, “elf32-bigarm”, “elf32-littlearm”)
/* Define the target architecture */
OUTPUT_ARCH(arm)
/* Define the system entry point */
ENTRY(Reset_Handler)
/* Define the memory layout for the board */
SECTIONS
{
/* LPC1766 flash is at 0×0000_0000 */
.flash 0×00000000 :
{
/* Tell the linker to collect any .cs3.interrupt_vector section to this
address */
KEEP(*(.cs3.interrupt_vector));
/* Collect the executable parts here */
*(.text .text.*);
/* Collect the const variables here */
*(.rodata .rodata.*);
__sidata = .;
}
/*
* This section refers to variables with init value. The init values are
* stored on ROM, but addressed on RAM (otherwise the variables cannot be
* manipulated).
*
* This section will be loaded at 0×10000000, the start of LPC1766 RAM
*/
.data_at_ram 0×10000000: AT (LOADADDR(.flash) + SIZEOF(.flash))
{
__sdata = .;
*(.data .data.*);
__edata = .;
}
.ram :
{
__sbss = .;
/* Collect the global / static variables, without init value here */
*(.bss .bss.*);
__ebss = .;
}
/* Set __cs3_stack to point the end of memory
* This one depends on your MCU. I’m using LPC1766 with 256kB flash and
* 32kB RAM
*/
__cs3_stack = 0×10000000 + (32 * 1024);
}
This is just an example that does nothing. For more complete demo, you can download the original source code from CMSIS website (http://www.onarm.com/download/download389.asp).