diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index bcdd6c3..2a9554b 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -121,6 +121,29 @@ void enablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcint #endif } + // Special case for Attiny x61 where PCMSK0 and PCMSK1 registers + // have initial values of 1 for some reason. + // See datasheet section 9.3.4 and 9.3.5 + // https://ww1.microchip.com/downloads/en/devicedoc/atmel-2588-8-bit-avr-microcontrollers-tinyavr-attiny261-attiny461-attiny861_datasheet.pdf +#if defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) +#if (PCINT_USE_PORT1 == true) + // PCIE0 case + if (!(GIMSK & (1 << PCIE0))) { + // Clear PCINT11:8 + PCMSK1 &= ~0x0F; + } + // PCIE1 case +#endif + if (!(GIMSK & (1 << PCIE1))) { +#if (PCINT_USE_PORT1 == true) + // Clear PCINT15:12 + PCMSK1 &= ~0xF0; +#endif + // Clear PCINT7:0 + PCMSK0 = 0x00; + } +#endif + // Pin change mask registers decide which pins are ENABLE as triggers #ifdef PCMSK0 #ifdef PCMSK1 @@ -165,6 +188,21 @@ void enablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcint PCICR |= (1 << (pcintPort + PCIE0)); #elif defined(GICR) /* e.g. ATmega162 */ GICR |= (1 << (pcintPort + PCIE0)); +#elif defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) + // This is a special case for Attiny x61 series which was very weird PCINT mapping. + // See datasheet section 9.3.2: http://ww1.microchip.com/downloads/en/devicedoc/atmel-2588-8-bit-avr-microcontrollers-tinyavr-attiny261-attiny461-attiny861_datasheet.pdf +#if (PCINT_USE_PORT1 == true) + if (pcintPort == 1 && pcintMask < (1 << 4)) { + // PCINT11:8 will be enabled with PCIE0 + GIMSK |= (1 << PCIE0); + } + else { +#endif + // PCINT7:0 and PCINT15:12 will be enabled with PCIE1 + GIMSK |= (1 << PCIE1); +#if (PCINT_USE_PORT1 == true) + } +#endif #elif defined(GIMSK) && defined(PCIE0) /* e.g. ATtiny X4 */ GIMSK |= (1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE) /* e.g. ATtiny X5 */ @@ -247,9 +285,31 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin // if that's the last one, disable the interrupt. if (*(&PCMSK + pcintPort) == 0) { disable = true; - } #endif // ifdef PCMSK0 + // This is a special case for Attiny x61 series which was very weird PCINT mapping. + // See datasheet section 9.3.2: + // http://ww1.microchip.com/downloads/en/devicedoc/atmel-2588-8-bit-avr-microcontrollers-tinyavr-attiny261-attiny461-attiny861_datasheet.pdf +#if defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) +#if (PCINT_USE_PORT1 == true) + if (pcintPort == 1 && pcintMask < (1 << 4)) { + //PCINT11:8 will be disabled with PCIE0 + if (!(PCMSK1 & 0x0F)) { + GIMSK &= ~(1 << PCIE0); + } + } + else { + //PCINT7:0 and PCINT15:12 will be disabled with PCIE1 + if (!PCMSK0 && !(PCMSK1 & 0xF0)) { +#else + if (!PCMSK0) { +#endif + GIMSK &= ~(1 << PCIE1); + } +#if (PCINT_USE_PORT1 == true) + } +#endif +#else if(disable) { #ifdef PCICR @@ -264,6 +324,7 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin #error MCU has no such a register #endif } +#endif } /* diff --git a/src/PinChangeInterrupt0.cpp b/src/PinChangeInterrupt0.cpp index e95da51..b48285c 100644 --- a/src/PinChangeInterrupt0.cpp +++ b/src/PinChangeInterrupt0.cpp @@ -36,8 +36,17 @@ THE SOFTWARE. void attachPinChangeInterrupt0(void) { // fake function to make the IDE link this file } +#ifdef PCINT_COMBINE_PORT0_PORT1 +void attachPinChangeInterrupt1(void) { + // fake function to make the IDE link this file +} +#endif +#ifdef PCINT_COMBINE_PORT0_PORT1 +ISR(PCINT_vect) { +#else ISR(PCINT0_vect) { +#endif // get the new and old pin states for port uint8_t newPort = PCINT_INPUT_PORT0; @@ -71,6 +80,42 @@ ISR(PCINT0_vect) { #else PCINT_CALLBACK_PORT0 #endif + +#if defined(PCINT_COMBINE_PORT0_PORT1) && (PCINT_USE_PORT1 == true) + // get the new and old pin states for port + newPort = PCINT_INPUT_PORT1; + + // compare with the old value to detect a rising or falling + arrayPos = getArrayPosPCINT(1); + change = newPort ^ oldPorts[arrayPos]; + rising = change & newPort; + falling = change & oldPorts[arrayPos]; + + // check which pins are triggered, compared with the settings + risingTrigger = rising & risingPorts[arrayPos]; + fallingTrigger = falling & fallingPorts[arrayPos]; + trigger = risingTrigger | fallingTrigger; + + // save the new state for next comparison + oldPorts[arrayPos] = newPort; + + // Execute all functions that should be triggered + // This way we can exclude a single function + // and the calling is also much faster + // We may also reorder the pins for different priority +#if !defined(PCINT_CALLBACK_PORT1) + PCINT_CALLBACK(0, 8); + PCINT_CALLBACK(1, 9); + PCINT_CALLBACK(2, 10); + PCINT_CALLBACK(3, 11); + PCINT_CALLBACK(4, 12); + PCINT_CALLBACK(5, 13); + PCINT_CALLBACK(6, 14); + PCINT_CALLBACK(7, 15); +#else + PCINT_CALLBACK_PORT1 +#endif +#endif // defined(PCINT_COMBINE_PORT0_PORT1) && (PCINT_USE_PORT1 == true) } #if defined(PCINT_API) @@ -142,6 +187,57 @@ void PinChangeInterruptEventPCINT7(void) { } #endif +#ifdef PCINT_COMBINE_PORT0_PORT1 +#if (PCINT_USE_PCINT8 == true) +volatile callback callbackPCINT8 = pcint_null_callback; +void PinChangeInterruptEventPCINT8(void) { + callbackPCINT8(); +} +#endif +#if (PCINT_USE_PCINT9 == true) +volatile callback callbackPCINT9 = pcint_null_callback; +void PinChangeInterruptEventPCINT9(void) { + callbackPCINT9(); +} +#endif +#if (PCINT_USE_PCINT10 == true) +volatile callback callbackPCINT10 = pcint_null_callback; +void PinChangeInterruptEventPCINT10(void) { + callbackPCINT10(); +} +#endif +#if (PCINT_USE_PCINT11 == true) +volatile callback callbackPCINT11 = pcint_null_callback; +void PinChangeInterruptEventPCINT11(void) { + callbackPCINT11(); +} +#endif +#if (PCINT_USE_PCINT12 == true) +volatile callback callbackPCINT12 = pcint_null_callback; +void PinChangeInterruptEventPCINT12(void) { + callbackPCINT12(); +} +#endif +#if (PCINT_USE_PCINT13 == true) +volatile callback callbackPCINT13 = pcint_null_callback; +void PinChangeInterruptEventPCINT13(void) { + callbackPCINT13(); +} +#endif +#if (PCINT_USE_PCINT14 == true) +volatile callback callbackPCINT14 = pcint_null_callback; +void PinChangeInterruptEventPCINT14(void) { + callbackPCINT14(); +} +#endif +#if (PCINT_USE_PCINT15 == true) +volatile callback callbackPCINT15 = pcint_null_callback; +void PinChangeInterruptEventPCINT15(void) { + callbackPCINT15(); +} +#endif +#endif // PCINT_COMBINE_PORT0_PORT1 + #endif // PCINT_API #endif // PCINT_USE_PORT0 diff --git a/src/PinChangeInterrupt1.cpp b/src/PinChangeInterrupt1.cpp index fd90e19..fba7993 100644 --- a/src/PinChangeInterrupt1.cpp +++ b/src/PinChangeInterrupt1.cpp @@ -31,7 +31,7 @@ THE SOFTWARE. #if defined(PCINT_ALINKAGE) && defined(PCINT_COMPILE_ENABLED_ISR) && defined(PCINT_INCLUDE_FROM_CPP) \ || !defined(PCINT_ALINKAGE) || !defined(PCINT_COMPILE_ENABLED_ISR) -#if (PCINT_USE_PORT1 == true) +#if (PCINT_USE_PORT1 == true) && !defined(PCINT_COMBINE_PORT0_PORT1) void attachPinChangeInterrupt1(void) { // fake function to make the IDE link this file diff --git a/src/PinChangeInterruptBoards.h b/src/PinChangeInterruptBoards.h index fdcfa65..d3c44f1 100644 --- a/src/PinChangeInterruptBoards.h +++ b/src/PinChangeInterruptBoards.h @@ -142,6 +142,10 @@ THE SOFTWARE. // PORTB has Reset, clock and SPI while PORTA has I2C and Analog Pins. We just enable all pins. #define PCINT_INPUT_PORT0 PINA #define PCINT_INPUT_PORT1 PINB +// Attiny x61 Series has a very special interrupt vector and port mapping. +// It combines PCINT0_vect and PCINT1_vect into PCINT_vect +// See datasheet section 9.1: http://ww1.microchip.com/downloads/en/devicedoc/atmel-2588-8-bit-avr-microcontrollers-tinyavr-attiny261-attiny461-attiny861_datasheet.pdf +#define PCINT_COMBINE_PORT0_PORT1 true #else // Microcontroller not supported #error PinChangeInterrupt library does not support this MCU. diff --git a/src/PinChangeInterruptPins.h b/src/PinChangeInterruptPins.h index f000870..93fc33a 100644 --- a/src/PinChangeInterruptPins.h +++ b/src/PinChangeInterruptPins.h @@ -371,12 +371,14 @@ Serial.println(); // Hardware Definitions //================================================================================ -#if defined(PCINT0_vect) +//special case for the x61 where a single interrupt vector is used for multiple ports +#if defined(PCINT0_vect) || (defined(PCINT_vect) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__))) #define PCINT_HAS_PORT0 true #else #define PCINT_HAS_PORT0 false #endif -#if defined(PCINT1_vect) +//special case for the x61 where a single interrupt vector is used for multiple ports +#if defined(PCINT1_vect) || (defined(PCINT_vect) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__))) #define PCINT_HAS_PORT1 true #else #define PCINT_HAS_PORT1 false