PDA

View Full Version : how can I know what interrupt (edge) I get in teensy 3.0



ancc
12-08-2012, 05:54 PM
Hello

I'm trying to decode serial signal with teensy 3.0. I'm connecting the receiver to digital pin 10. In my code I turning on interrupt 10 with this command
attachInterrupt(11, test, CHANGE);

I need to measure time before interrupts and I have to know, what interrupt do I have, from rising or from falling edge. How can I know it?

Of course I can do digitalRead (11) after I get interrupt, but I think this is not good idea.

The second way I can use two pins for interrupt and connect them together, for exmaple
attachInterrupt(11, test, RISING);
attachInterrupt(12, test1, FALLING);
but I think that it is not good idea too.

In teensy 2.0 I can use input capture timer, but in teensy 3.0 I don't know yet how to do this.

ancc
12-09-2012, 05:01 AM
I tried to check seconw way with two interrupts. So in this case

attachInterrupt(11, test, RISING);
attachInterrupt(12, test1, FALLING);

woks only one interrupt from most pin.
So it is impossible to attach more then one interrupt. Is it right?

el_supremo
12-09-2012, 02:48 PM
Of course I can do digitalRead (11) after I get interrupt, but I think this is not good idea
Why?

Pete

ancc
12-10-2012, 05:16 AM
some pulses in serial signal are less then 5 microsecords and with digitalRead inside this interrupt I have some errors from time to time. I don't know why, I think that digitalRead works slowly. I think that code like this
#define INPUT_IS_SET (bit_is_set(DATAIN_PIN, DATAIN))
#define INPUT_IS_CLEAR (bit_is_clear(DATAIN_PIN, DATAIN))
instead of digitalRead will work faster, but I'm not shure and this macros from avr, I don't know yet how to read pin without digitalRead in teensy 3.0

cmason
12-10-2012, 06:18 AM
So I'm wondering about the same thing: How do I monitor multiple pins and not have to write a separate function for each?

I can think of two possibilities here:


Modify attachInterrupt so that it (optionally) can pass an argument to the function with the pin number.
Only reset the port status register after invoking the function. This of course requires either checking all port registers or separate function for each port register. So (1) seems like the way to go.


Thoughts?

-c

cmason
12-10-2012, 06:38 AM
I'm basically thinking of something like this in pins_teensy.c:


typedef void (*voidFuncPtr)(void);
typedef void (*intFuncPtr)(int);
volatile static voidFuncPtr intFunc[CORE_NUM_DIGITAL];
volatile static short intFuncArg[CORE_NUM_DIGITAL];

void attachInterruptInternal(uint8_t pin, void (*function)(int), int mode, short 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);

__disable_irq();
cfg = *config;
cfg &= ~0x000F0000; // disable any previous interrupt
*config = cfg;
intFunc[pin] = function; // set the function pointer
intFuncArg[pin] = passPinArg;
cfg |= mask;
*config = cfg; // enable the new interrupt
__enable_irq();
}

void attachInterrupt(uint8_t pin, void (*function)(void), int mode)
{
attachInterruptInternal(pin, function, mode, 0);
}

void attachInterruptWithPin(uint8_t pin, void (*function)(int), int mode)
{
attachInterruptInternal(pin, function, mode, 1);
}

void porta_isr(void)
{
uint32_t isfr = PORTA_ISFR;
PORTA_ISFR = isfr;
if ((isfr & CORE_PIN3_BITMASK) && intFunc[3]) {
if (intFuncArg[3]) {
((intFuncPtr)intFunc[3])(3);
} else {
intFunc[3]();
}
}
if ((isfr & CORE_PIN4_BITMASK) && intFunc[4]) {if (intFuncArg[4]) {((intFuncPtr) intFunc[4])(4);} else {intFunc[4]();}};
if ((isfr & CORE_PIN24_BITMASK) && intFunc[24]) {if (intFuncArg[24]) {((intFuncPtr) intFunc[24])(24);} else {intFunc[24]();}};
if ((isfr & CORE_PIN33_BITMASK) && intFunc[33]) {if (intFuncArg[33]) {((intFuncPtr) intFunc[33])(33);} else {intFunc[33]();}};
}

void portb_isr(void) {
uint32_t isfr = PORTB_ISFR;
PORTB_ISFR = isfr;
if ((isfr & CORE_PIN0_BITMASK) && intFunc[0]) {if (intFuncArg[0]) {((intFuncPtr) intFunc[0])(0);} else {intFunc[0]();}};
if ((isfr & CORE_PIN1_BITMASK) && intFunc[1]) {if (intFuncArg[1]) {((intFuncPtr) intFunc[1])(1);} else {intFunc[1]();}};
if ((isfr & CORE_PIN16_BITMASK) && intFunc[16]) {if (intFuncArg[16]) {((intFuncPtr) intFunc[16])(16);} else {intFunc[16]();}};
if ((isfr & CORE_PIN17_BITMASK) && intFunc[17]) {if (intFuncArg[17]) {((intFuncPtr) intFunc[17])(17);} else {intFunc[17]();}};
if ((isfr & CORE_PIN18_BITMASK) && intFunc[18]) {if (intFuncArg[18]) {((intFuncPtr) intFunc[18])(18);} else {intFunc[18]();}};
if ((isfr & CORE_PIN19_BITMASK) && intFunc[19]) {if (intFuncArg[19]) {((intFuncPtr) intFunc[19])(19);} else {intFunc[19]();}};
if ((isfr & CORE_PIN25_BITMASK) && intFunc[25]) {if (intFuncArg[25]) {((intFuncPtr) intFunc[25])(25);} else {intFunc[25]();}};
if ((isfr & CORE_PIN32_BITMASK) && intFunc[32]) {if (intFuncArg[32]) {((intFuncPtr) intFunc[32])(32);} else {intFunc[32]();}};
}

void portc_isr(void) {
// TODO: these are inefficent. Use CLZ somehow....
uint32_t isfr = PORTC_ISFR;
PORTC_ISFR = isfr;
if ((isfr & CORE_PIN9_BITMASK) && intFunc[9]) {if (intFuncArg[9]) {((intFuncPtr) intFunc[9])(9);} else {intFunc[9]();}};
if ((isfr & CORE_PIN10_BITMASK) && intFunc[10]) {if (intFuncArg[10]) {((intFuncPtr) intFunc[10])(10);} else {intFunc[10]();}};
if ((isfr & CORE_PIN11_BITMASK) && intFunc[11]) {if (intFuncArg[11]) {((intFuncPtr) intFunc[11])(11);} else {intFunc[11]();}};
if ((isfr & CORE_PIN12_BITMASK) && intFunc[12]) {if (intFuncArg[12]) {((intFuncPtr) intFunc[12])(12);} else {intFunc[12]();}};
if ((isfr & CORE_PIN13_BITMASK) && intFunc[13]) {if (intFuncArg[13]) {((intFuncPtr) intFunc[13])(13);} else {intFunc[13]();}};
if ((isfr & CORE_PIN15_BITMASK) && intFunc[15]) {if (intFuncArg[15]) {((intFuncPtr) intFunc[15])(15);} else {intFunc[15]();}};
if ((isfr & CORE_PIN22_BITMASK) && intFunc[22]) {if (intFuncArg[22]) {((intFuncPtr) intFunc[22])(22);} else {intFunc[22]();}};
if ((isfr & CORE_PIN23_BITMASK) && intFunc[23]) {if (intFuncArg[23]) {((intFuncPtr) intFunc[23])(23);} else {intFunc[23]();}};
if ((isfr & CORE_PIN27_BITMASK) && intFunc[27]) {if (intFuncArg[27]) {((intFuncPtr) intFunc[27])(27);} else {intFunc[27]();}};
if ((isfr & CORE_PIN28_BITMASK) && intFunc[28]) {if (intFuncArg[28]) {((intFuncPtr) intFunc[28])(28);} else {intFunc[28]();}};
if ((isfr & CORE_PIN29_BITMASK) && intFunc[29]) {if (intFuncArg[29]) {((intFuncPtr) intFunc[29])(29);} else {intFunc[29]();}};
if ((isfr & CORE_PIN30_BITMASK) && intFunc[30]) {if (intFuncArg[30]) {((intFuncPtr) intFunc[30])(30);} else {intFunc[30]();}};
}

void portd_isr(void) {
uint32_t isfr = PORTD_ISFR;
PORTD_ISFR = isfr;
if ((isfr & CORE_PIN2_BITMASK) && intFunc[2]) {if (intFuncArg[2]) {((intFuncPtr) intFunc[2])(2);} else {intFunc[2]();}};
if ((isfr & CORE_PIN5_BITMASK) && intFunc[5]) {if (intFuncArg[5]) {((intFuncPtr) intFunc[5])(5);} else {intFunc[5]();}};
if ((isfr & CORE_PIN6_BITMASK) && intFunc[6]) {if (intFuncArg[6]) {((intFuncPtr) intFunc[6])(6);} else {intFunc[6]();}};
if ((isfr & CORE_PIN7_BITMASK) && intFunc[7]) {if (intFuncArg[7]) {((intFuncPtr) intFunc[7])(7);} else {intFunc[7]();}};
if ((isfr & CORE_PIN8_BITMASK) && intFunc[8]) {if (intFuncArg[8]) {((intFuncPtr) intFunc[8])(8);} else {intFunc[8]();}};
if ((isfr & CORE_PIN14_BITMASK) && intFunc[14]) {if (intFuncArg[14]) {((intFuncPtr) intFunc[14])(14);} else {intFunc[14]();}};
if ((isfr & CORE_PIN20_BITMASK) && intFunc[20]) {if (intFuncArg[20]) {((intFuncPtr) intFunc[20])(20);} else {intFunc[20]();}};
if ((isfr & CORE_PIN21_BITMASK) && intFunc[21]) {if (intFuncArg[21]) {((intFuncPtr) intFunc[21])(21);} else {intFunc[21]();}};
}

void porte_isr(void) {
uint32_t isfr = PORTE_ISFR;
PORTE_ISFR = isfr;
if ((isfr & CORE_PIN26_BITMASK) && intFunc[26]) {if (intFuncArg[26]) {((intFuncPtr) intFunc[26])(26);} else {intFunc[26]();}};
if ((isfr & CORE_PIN31_BITMASK) && intFunc[31]) {if (intFuncArg[31]) {((intFuncPtr) intFunc[31])(31);} else {intFunc[31]();}};
}

stepl
12-21-2016, 03:17 PM
Probably version of attachInterruptWithPin in pins_teensy.c for Teensyduino since v1.31.



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(); } }