typedef void (*voidFuncPtr)(void);
#if defined(KINETISK)
#ifdef NO_PORT_ISR_FASTRUN
static void port_A_isr(void);
static void port_B_isr(void);
static void port_C_isr(void);
static void port_D_isr(void);
static void port_E_isr(void);
#else
static void port_A_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone ));
static void port_B_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone ));
static void port_C_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone ));
static void port_D_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone ));
static void port_E_isr(void) __attribute__ ((section(".fastrun"), noinline, noclone ));
#endif
typedef void(*intFuncPtr)(uint8_t);
typedef struct
{
voidFuncPtr func;
uint8_t pin;
uint8_t passPinArg;
} isr_entry_t;
static const isr_entry_t dummy_isr_entry = { dummy_isr };
isr_entry_t isr_table_portA[CORE_MAX_PIN_PORTA+1] = { [0 ... CORE_MAX_PIN_PORTA] = { dummy_isr } };
isr_entry_t isr_table_portB[CORE_MAX_PIN_PORTB+1] = { [0 ... CORE_MAX_PIN_PORTB] = { dummy_isr } };
isr_entry_t isr_table_portC[CORE_MAX_PIN_PORTC+1] = { [0 ... CORE_MAX_PIN_PORTC] = { dummy_isr } };
isr_entry_t isr_table_portD[CORE_MAX_PIN_PORTD+1] = { [0 ... CORE_MAX_PIN_PORTD] = { dummy_isr } };
isr_entry_t isr_table_portE[CORE_MAX_PIN_PORTE+1] = { [0 ... CORE_MAX_PIN_PORTE] = { dummy_isr } };
// The Pin Config Register is used to look up the correct interrupt table
// for the corresponding port.
inline isr_entry_t* getIsrTable(volatile uint32_t *config) {
isr_entry_t* isr_table = NULL;
if(&PORTA_PCR0 <= config && config <= &PORTA_PCR31) isr_table = isr_table_portA;
else if(&PORTB_PCR0 <= config && config <= &PORTB_PCR31) isr_table = isr_table_portB;
else if(&PORTC_PCR0 <= config && config <= &PORTC_PCR31) isr_table = isr_table_portC;
else if(&PORTD_PCR0 <= config && config <= &PORTD_PCR31) isr_table = isr_table_portD;
else if(&PORTE_PCR0 <= config && config <= &PORTE_PCR31) isr_table = isr_table_portE;
return isr_table;
}
inline uint32_t getPinIndex(volatile uint32_t *config) {
uintptr_t v = (uintptr_t) config;
// There are 32 pin config registers for each port, each port starting at a round address.
// They are spaced 4 bytes apart.
return (v % 128) / 4;
}
#elif defined(KINETISL)
volatile static isr_entry_t intFunc[CORE_NUM_DIGITAL] = { [0 ... CORE_NUM_DIGITAL-1] = { dummy_isr } };
static void porta_interrupt(void);
static void portcd_interrupt(void);
#endif
void attachInterruptVector(enum IRQ_NUMBER_t irq, void (*function)(void))
{
_VectorsRam[irq + 16] = function;
}
void attachInterruptInternal(uint8_t pin, void (*function)(void), int mode, uint8_t passPinArg)
{
volatile uint32_t *config;
uint32_t cfg, mask;
if (pin >= CORE_NUM_DIGITAL) return;
switch (mode) {
case CHANGE: mask = 0x0B; break;
case RISING: mask = 0x09; break;
case FALLING: mask = 0x0A; break;
case LOW: mask = 0x08; break;
case HIGH: mask = 0x0C; break;
default: return;
}
mask = (mask << 16) | 0x01000000;
config = portConfigRegister(pin);
#if defined(KINETISK)
attachInterruptVector(IRQ_PORTA, port_A_isr);
attachInterruptVector(IRQ_PORTB, port_B_isr);
attachInterruptVector(IRQ_PORTC, port_C_isr);
attachInterruptVector(IRQ_PORTD, port_D_isr);
attachInterruptVector(IRQ_PORTE, port_E_isr);
isr_entry_t* isr_table = getIsrTable(config);
if(!isr_table) return;
uint32_t pin_index = getPinIndex(config);
__disable_irq();
cfg = *config;
cfg &= ~0x000F0000; // disable any previous interrupt
*config = cfg;
isr_table[pin_index] = (isr_entry_t){ function, pin, passPinArg }; // set the function pointer
cfg |= mask;
*config = cfg; // enable the new interrupt
__enable_irq();
#elif defined(KINETISL)
attachInterruptVector(IRQ_PORTA, porta_interrupt);
attachInterruptVector(IRQ_PORTCD, portcd_interrupt);
__disable_irq();
cfg = *config;
cfg &= ~0x000F0000; // disable any previous interrupt
*config = cfg;
intFunc[pin] = { function, pin, passPinArg }; // set the function pointer
cfg |= mask;
*config = cfg; // enable the new interrupt
__enable_irq();
#endif
}
void attachInterrupt(uint8_t pin, void(*function)(void), int mode)
{
attachInterruptInternal(pin, function, mode, 0);
}
void attachInterruptWithPin(uint8_t pin, void(*function)(uint8_t), int mode)
{
attachInterruptInternal(pin, (voidFuncPtr)function, mode, 1);
}
void detachInterrupt(uint8_t pin)
{
volatile uint32_t *config;
config = portConfigRegister(pin);
#if defined(KINETISK)
isr_entry_t* isr_table = getIsrTable(config);
if(!isr_table) return;
uint32_t pin_index = getPinIndex(config);
__disable_irq();
*config = ((*config & ~0x000F0000) | 0x01000000);
isr_table[pin_index] = dummy_isr_entry;
__enable_irq();
#elif defined(KINETISL)
__disable_irq();
*config = ((*config & ~0x000F0000) | 0x01000000);
intFunc[pin] = dummy_isr_entry;
__enable_irq();
#endif
}
// Using CTZ instead of CLZ is faster, since it allows more efficient bit
// clearing and fast indexing into the pin ISR table.
#define PORT_ISR_FUNCTION_CLZ(port_name) \
static void port_ ## port_name ## _isr(void) { \
uint32_t isfr = PORT ## port_name ##_ISFR; \
PORT ## port_name ##_ISFR = isfr; \
isr_entry_t* isr_table = isr_table_port ## port_name; \
isr_entry_t isr_entry; \
while(isfr) { \
isr_entry = isr_table[__builtin_ctz(isfr)]; \
if (isr_entry.passPinArg) \
((intFuncPtr)isr_entry.func)(isr_entry.pin); \
else \
isr_entry.func(); \
isfr = isfr & (isfr-1); \
if(!isfr) return; \
} \
}
// END PORT_ISR_FUNCTION_CLZ
#if defined(KINETISK)
PORT_ISR_FUNCTION_CLZ(A)
PORT_ISR_FUNCTION_CLZ(B)
PORT_ISR_FUNCTION_CLZ(C)
PORT_ISR_FUNCTION_CLZ(D)
PORT_ISR_FUNCTION_CLZ(E)
#elif defined(KINETISL)
// Kinetis L (Teensy LC) is based on Cortex M0 and doesn't have hardware
// support for CLZ.
#define DISPATCH_PIN_ISR(pin_nr) { isr_entry_t isr_entry = intFunc[pin_nr]; \
if(isfr & CORE_PIN ## pin_nr ## _BITMASK) { if(isr_entry.passPinArg) ((intFuncPtr)isr_entry.func)(pin_nr); else isr_entry.func(); } }