diff --git a/hardware/arduino/avr/cores/arduino/Arduino.h b/hardware/arduino/avr/cores/arduino/Arduino.h index ac775f13c5c..776b84e3161 100644 --- a/hardware/arduino/avr/cores/arduino/Arduino.h +++ b/hardware/arduino/avr/cores/arduino/Arduino.h @@ -140,6 +140,8 @@ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); void attachInterrupt(uint8_t, void (*)(void), int mode); void detachInterrupt(uint8_t); +void enableInterrupt(uint8_t interruptNum); +void disableInterrupt(uint8_t interruptNum); void setup(void); void loop(void); diff --git a/hardware/arduino/avr/cores/arduino/WInterrupts.c b/hardware/arduino/avr/cores/arduino/WInterrupts.c index d3fbf100e3e..7af25eefdc9 100644 --- a/hardware/arduino/avr/cores/arduino/WInterrupts.c +++ b/hardware/arduino/avr/cores/arduino/WInterrupts.c @@ -19,7 +19,7 @@ Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + Modified 24 November 2006 by David A. Mellis Modified 1 August 2010 by Mark Sproul */ @@ -38,82 +38,168 @@ static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS]; void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { intFunc[interruptNum] = userFunc; - + // Configure the interrupt mode (trigger on low input, any change, rising // edge, or falling edge). The mode constants were chosen to correspond // to the configuration bits in the hardware register, so we simply shift // the mode into place. - - // Enable the interrupt. - + switch (interruptNum) { #if defined(__AVR_ATmega32U4__) // I hate doing this, but the register assignment differs between the 1280/2560 - // and the 32U4. Since avrlib defines registers PCMSK1 and PCMSK2 that aren't + // and the 32U4. Since avrlib defines registers PCMSK1 and PCMSK2 that aren't // even present on the 32U4 this is the only way to distinguish between them. case 0: - EICRA = (EICRA & ~((1<1; t>>=1, pos++) ; - // Set callback function - if (pio == PIOA) - callbacksPioA[pos] = callback; - if (pio == PIOB) - callbacksPioB[pos] = callback; - if (pio == PIOC) - callbacksPioC[pos] = callback; - if (pio == PIOD) - callbacksPioD[pos] = callback; - // Configure the interrupt mode if (mode == CHANGE) { // Disable additional interrupt mode (detects both rising and falling edges) @@ -115,11 +105,62 @@ void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode) } } + // Set callback function and call handler to clear existing + // flags. On the SAM core, the only way to clear pending + // interrupt flags is to read the Interrupt Status Register. + // However, this always clears _all_ flags at once. To not lose + // any interrupts, we call the handler here which reads the + // status register and calls handlers for any pending + // interrupts (the interrupt we are attaching here isn't enabled + // yet and should be skipped). This does mean that if + // attachInterrupt is called with interrupts globally disabled, + // interrupt handlers are called nonetheless. This seems to be + // the lesser of three evils: + // - Not clearing the status register can cause a bogus + // interrupt shortly after calling attachInterrupt (this + // happened in earlier Arduino versions). + // - Reading the status register but not running interrupts can + // cause interrupts to be missed when attachInterrupts is + // called with interrupts disabled (and depending on timing + // probably also with them enabled). + // - Running the handlers (as now) causes interrupt handlers to + // run while calling attachInterrupt with interrupts + // disabled. + if (pio == PIOA) { + callbacksPioA[pos] = callback; + PIOA_Handler(); + } + if (pio == PIOB) { + callbacksPioB[pos] = callback; + PIOD_Handler(); + } + if (pio == PIOC) { + callbacksPioC[pos] = callback; + PIOD_Handler(); + } + if (pio == PIOD) { + callbacksPioD[pos] = callback; + PIOD_Handler(); + } + + enableInterrupt(pin); +} + +void enableInterrupt(uint32_t pin) +{ + Pio *pio = g_APinDescription[pin].pPort; + uint32_t mask = g_APinDescription[pin].ulPin; + // Enable interrupt pio->PIO_IER = mask; } void detachInterrupt(uint32_t pin) +{ + disableInterrupt(pin); +} + +void disableInterrupt(uint32_t pin) { // Retrieve pin information Pio *pio = g_APinDescription[pin].pPort; @@ -134,7 +175,7 @@ extern "C" { #endif void PIOA_Handler(void) { - uint32_t isr = PIOA->PIO_ISR; + uint32_t isr = PIOA->PIO_ISR & PIOA->PIO_IMR; uint32_t i; for (i=0; i<32; i++, isr>>=1) { if ((isr & 0x1) == 0) @@ -145,7 +186,7 @@ void PIOA_Handler(void) { } void PIOB_Handler(void) { - uint32_t isr = PIOB->PIO_ISR; + uint32_t isr = PIOB->PIO_ISR & PIOB->PIO_IMR; uint32_t i; for (i=0; i<32; i++, isr>>=1) { if ((isr & 0x1) == 0) @@ -156,7 +197,7 @@ void PIOB_Handler(void) { } void PIOC_Handler(void) { - uint32_t isr = PIOC->PIO_ISR; + uint32_t isr = PIOC->PIO_ISR & PIOC->PIO_IMR; uint32_t i; for (i=0; i<32; i++, isr>>=1) { if ((isr & 0x1) == 0) @@ -167,7 +208,7 @@ void PIOC_Handler(void) { } void PIOD_Handler(void) { - uint32_t isr = PIOD->PIO_ISR; + uint32_t isr = PIOD->PIO_ISR & PIOD->PIO_IMR; uint32_t i; for (i=0; i<32; i++, isr>>=1) { if ((isr & 0x1) == 0) diff --git a/hardware/arduino/sam/cores/arduino/WInterrupts.h b/hardware/arduino/sam/cores/arduino/WInterrupts.h index bb698cdf384..1ea1679ccc1 100644 --- a/hardware/arduino/sam/cores/arduino/WInterrupts.h +++ b/hardware/arduino/sam/cores/arduino/WInterrupts.h @@ -29,6 +29,9 @@ void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode); void detachInterrupt(uint32_t pin); +void enableInterrupt(uint32_t pin); +void disableInterrupt(uint32_t pin); + #ifdef __cplusplus } #endif