diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index 5a2d8b9ed8cee92efc6f0f459d0eba0c8f05b601..220e0bfde9c64c05da5deff6cc4074a820973659 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -21,12 +21,17 @@ #include <string.h> -static voidFuncPtr callbacksInt[EXTERNAL_NUM_INTERRUPTS]; +static voidFuncPtr ISRcallback[EXTERNAL_NUM_INTERRUPTS]; +static EExt_Interrupts ISRlist[EXTERNAL_NUM_INTERRUPTS]; +static uint32_t nints; // Stores total number of attached interrupts + /* Configure I/O interrupt sources */ static void __initialize() { - memset(callbacksInt, 0, sizeof(callbacksInt)); + memset(ISRlist, 0, sizeof(ISRlist)); + memset(ISRcallback, 0, sizeof(ISRcallback)); + nints = 0; NVIC_DisableIRQ(EIC_IRQn); NVIC_ClearPendingIRQ(EIC_IRQn); @@ -76,42 +81,65 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode) // Assign pin to EIC pinPeripheral(pin, PIO_EXTINT); - // Assign callback to interrupt - callbacksInt[in] = callback; + // Only store when there is really an ISR to call. + // This allow for calling attachInterrupt(pin, NULL, mode), we set up all needed register + // but won't service the interrupt, this way we also don't need to check it inside the ISR. + if (callback) + { + // Store interrupts to service in order of when they were attached + // to allow for first come first serve handler + uint32_t current=0; + + // Check if we already have this interrupt + int id = -1; + for (uint32_t i=0; i<nints; i++) { + if (ISRlist[i] == in) id = in; + } - // Look for right CONFIG register to be addressed - if (in > EXTERNAL_INT_7) { - config = 1; - } else { - config = 0; - } + if (id == -1) { + // Need to make a new entry + current = nints; + nints++; + } else { + // We already have an entry for this pin + current = id; + } + ISRlist[current] = in; // List with nr of interrupt in order of when they were attached + ISRcallback[current] = callback; // List of callback adresses + + // Look for right CONFIG register to be addressed + if (in > EXTERNAL_INT_7) { + config = 1; + } else { + config = 0; + } - // Configure the interrupt mode - pos = (in - (8 * config)) << 2; - EIC->CONFIG[config].reg &=~ (EIC_CONFIG_SENSE0_Msk << pos);//reset sense mode, important when changing trigger mode during runtime - switch (mode) - { - case LOW: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_LOW_Val << pos; - break; + // Configure the interrupt mode + pos = (in - (8 * config)) << 2; + EIC->CONFIG[config].reg &=~ (EIC_CONFIG_SENSE0_Msk << pos); // Reset sense mode, important when changing trigger mode during runtime + switch (mode) + { + case LOW: + EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_LOW_Val << pos; + break; - case HIGH: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_HIGH_Val << pos; - break; + case HIGH: + EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_HIGH_Val << pos; + break; - case CHANGE: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_BOTH_Val << pos; - break; + case CHANGE: + EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_BOTH_Val << pos; + break; - case FALLING: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_FALL_Val << pos; - break; + case FALLING: + EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_FALL_Val << pos; + break; - case RISING: - EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_RISE_Val << pos; - break; + case RISING: + EIC->CONFIG[config].reg |= EIC_CONFIG_SENSE0_RISE_Val << pos; + break; + } } - // Enable the interrupt EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << in); } @@ -133,6 +161,23 @@ void detachInterrupt(uint32_t pin) // Disable wakeup capability on pin during sleep EIC->WAKEUP.reg &= ~(1 << in); + + // Remove callback from the ISR list + int id = -1; + for (uint32_t i=0; i<nints; i++) { + if (ISRlist[i] == in) id = in; + } + if (id == -1) return; // We didn't have it + + // Shift the reminder down + for (uint32_t i=id; i<nints-1; i++) { + ISRlist[i] = ISRlist[i+1]; + ISRcallback[i] = ISRcallback[i+1]; + } + // And remove the top item + ISRlist[nints]=0; + ISRcallback[nints]=NULL; + nints--; } /* @@ -140,18 +185,18 @@ void detachInterrupt(uint32_t pin) */ void EIC_Handler(void) { - // Test the 16 normal interrupts - for (uint32_t i=EXTERNAL_INT_0; i<=EXTERNAL_INT_15; i++) + // Calling the routine directly from -here- takes about 1us + // Depending on where you are in the list it will take longer + + // Loop over all enabled interrupts in the list + for (uint32_t i=0; i<nints; i++) { - if ((EIC->INTFLAG.reg & (1 << i)) != 0) + if ((EIC->INTFLAG.reg & 1<<ISRlist[i]) != 0) { - // Call the callback function if assigned - if (callbacksInt[i]) { - callbacksInt[i](); - } - + // Call the callback function + ISRcallback[i](); // Clear the interrupt - EIC->INTFLAG.reg = 1 << i; + EIC->INTFLAG.reg = 1<<ISRlist[i]; } } }