As an example, here's a cut down part of out "interrupts.h" file. This is for a gcc-based build, so may not translate directly into whatever you're using. This is running "bare metal" on an MCF5329. Now I'm looking at it I'm a bit surprised how steep the learning curve is, and how much code you have to get right before it will work at all.
First you need an assembly file to define the vector areas and to fill in some default interrupt vectors. In our case we have the ROM-based ones which are copied to static RAM:
vector.S: .section .vector_rom, "x"__ROMVEC: .long __stack /* Reset: Initial Stack Pointer */ .long start /* Reset: Initial Program Counter */ .long BusError /* Bus Error, Jim, but not as we know it. */ .long AddrError /* Address Error */... And so on ... .org 0x100 .long INTC0Default /* User-Defined Interrupt 0 */ .long INTC0Default /* User-Defined Interrupt 1 */
.long INTC0Default /* QSPI */ .long INTC0Default /* TMR0 */... .long INTC0Default /* User-Defined Interrupt 62 */ .long INTC0Default /* User-Defined Interrupt 63 */ .long INTC1Default /* User-Defined Interrupt 64 */
.long INTC1Default /* User-Defined Interrupt 65 */
...
.long INTC1Default /* CAN1 buf 0 */
.long INTC1Default /* CAN1 buf 1 */
.long INTC1Default /* CAN1 buf 2 */
...
... .long IRQDefault /* User-Defined Interrupt 185 */... .long IRQDefault /* User-Defined Interrupt 191 */ .org 0x00000400 .section .vector_ram__RAMVEC: .space 0x400 .section .textVecDefault: halt bra VecDefaultIRQDefault: halt bra IRQDefault... And so on for all the other default interrupts like INTC0Default and INTC1Default.".vector_ram" and ".vector_rom" are defined in the linker control script.
This header file is included into every source file that needs to set and operate on interrupts:
interrupt.h:extern void ( *__RAMVEC[] ) ( void );/* * USAGE * * To register an interrupt, do the following IN THIS ORDER. * * INT_SET_VECTOR( int_lcd, LcdIsr ); * INT_SET_PRIORITY( int_lcd ); * INT_UNMASK( int_lcd ); * * To deregister an interrupt it is only necessary to call the * INT_MASK() function. Disable ints during this call. */// All of these conditionals compile out to a constant#define INT_SET_VECTOR( x, isr ) \ do { \ __RAMVEC[( x.controller + 1 ) * 64 + x.source] = isr; \ } while ( 0 );#define INT_LEVEL( x ) ( x.level )#define INT_SET_PRIORITY( x ) \ do { \ if ( x.controller == 0 ) \ { \ MCF_INTC0_ICR( x.source ) = MCF_INTC_ICR_IL( x.level ); \ } \ else \ { \ MCF_INTC1_ICR( x.source ) = MCF_INTC_ICR_IL( x.level ); \ } \ } while ( 0 );#define INT_UNMASK( x ) \ do { \ if ( x.controller == 0 ) \ { \ MCF_INTC0_CIMR = x.source; \ } \ else \ { \ MCF_INTC1_CIMR = x.source; \ } \ } while ( 0 );... And so on, also similar definitions for:#define INT_MASK( x ) \#define INT_FORCE( x ) \#define INT_UNFORCE( x ) \#define INT_PENDING( pending, x ) \#define INT_MASKED( masked, x ) \typedef struct{ uint8_t controller; uint8_t source; uint8_t level;} INT_INFO;/* * Being "static const" these expans in-line when called and * are surprisingly efficient. * * INTERRUPT PRIORITY * * First, the IPL is considered. * Then any interrupt from INTC0 wins over INTC1. * Then the higher numbered interrupts win over the lower ones. */// controller [0, 1], source [0, 63], level IPL/SPL [1-7]static const INT_INFO int_qspi = { 0, 31, 5 };static const INT_INFO int_pit[PIT_NUM_CHANNELS] ={ { 1, 43, 6 }, /* PIT_TIMER_MAIN_TIMER: IPL6 Reload */ { 1, 44, 7 }, /* PIT_TIMER_SOFT_WATCHDOG: IPL7 One-shot */ { 1, 45, 7 }, /* PIT_TIMER_POWER_HOLD: IPL7 Reload */ { 1, 46, 1 }, /* PIT_TIMER_PSEUDO_MAIN: IPL1 Reload */};static const INT_INFO int_i2c = { 0, 30, 4 };static const INT_INFO int_uart0 = { 0, 26, 4 };static const INT_INFO int_lcd = { 1, 51, 3 };static const INT_INFO int_usb = { 1, 48, 2 };static const INT_INFO int_dma[DMA_NUM_CHANNELS] ={ { 0, 32, 7 }, /* DMA Timer 0, used for profiling */ { 0, 33, 0 }, /* DMA Timer 1, used for system microsecond counter */ { 0, 34, 0 }, /* DMA Timer 2, Unused */ { 0, 35, 0 }, /* DMA Timer 3, Unused */};Note that the above "controller [0, 1], source [0, 63], level IPL/SPL [1-7]" information is lifted directly from the Reference Manual for all of the peripherals in use, and is conveniently defined in one place, rather than being scattered in all of the code that sets the interrupt vectors.
The "level" is selected by YOU depending on what are the most important interrupts in your design, and considering what interrupt service routnes can safely interrupt other interrupt service routines.
Here's an example call to set up the LCD interrupt, using the "int_lcd" structure defined above, and using the "LcdIsr()" service routine:
void VideoModuleInitHw(sVideoHdwrConfig_t *a_pConfig){ ... // Initialise LCD interrupt INT_SET_VECTOR(int_lcd, LcdIsr); INT_SET_PRIORITY(int_lcd); INT_UNMASK(int_lcd); ...__attribute__((interrupt_handler)) static void LcdIsr(void){ uint32_t dummy; dummy = MCF_LCDC_LISR; /* clear hardware flags */...To disable interrupts to the level of the LCD controller (so in can't interrupt, but higher ones can):
uint16_t old_ipl = asm_set_ipl(INT_LEVEL(int_lcd));
... Do whatever has to be done without the interrupt ...
asm_set_ipl(old_ipl);
Tom