From b93af7eced8129996cd17a390e6d321c825e4757 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 25 Jun 2021 21:03:58 -0500 Subject: [PATCH 01/12] Working with ATtinyx61 Had to merge PCINT0_vect and PCINT1_vect into the single available interrupt callback, PCINT_vect. Added an initialization method to clear out interrupts in both PCMSK0 and PCMSK1 on ATtinyx61 as interrupts on all pins are enabled by default. The split between PCIE0 and PCIE1 isn't even so a manual range check is done so that PCIE0 is only used on port 1 for the first 4 pins. --- keywords.txt | 1 + src/PinChangeInterrupt.cpp | 7 +++ src/PinChangeInterrupt.h | 6 +++ src/PinChangeInterrupt0.cpp | 96 ++++++++++++++++++++++++++++++++++ src/PinChangeInterrupt1.cpp | 2 +- src/PinChangeInterruptBoards.h | 1 + 6 files changed, 112 insertions(+), 1 deletion(-) diff --git a/keywords.txt b/keywords.txt index 1af2b5d..46cdae5 100644 --- a/keywords.txt +++ b/keywords.txt @@ -22,6 +22,7 @@ disablePCINT KEYWORD2 disablePinChangeInterrupt KEYWORD2 getPCINTTrigger KEYWORD2 getPinChangeInterruptTrigger KEYWORD2 +initPinChangeInterrupt KEYWORD2 ####################################### # Instances (KEYWORD2) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index d83ef6b..61b5f17 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -165,6 +165,13 @@ 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__)) + if (pcintPort == 1 && pcintMask < 16) { + GIMSK |= (1 << PCIE0); + } + else { + GIMSK |= (1 << PCIE1); + } #elif defined(GIMSK) && defined(PCIE0) /* e.g. ATtiny X4 */ GIMSK |= (1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE) /* e.g. ATtiny X5 */ diff --git a/src/PinChangeInterrupt.h b/src/PinChangeInterrupt.h index b30fb62..45b7951 100644 --- a/src/PinChangeInterrupt.h +++ b/src/PinChangeInterrupt.h @@ -107,6 +107,12 @@ PinChangeInterruptEventPCINT ## pcint PCINT_MACRO_BRACKETS #error MCU has no such a register #endif +#if defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__) +#define initPinChangeInterrupt() PCMSK0 = 0; PCMSK1 = 0 +#else +#define initPinChangeInterrupt() +#endif + // alias for shorter writing #define PCINTEvent(n) PinChangeInterruptEvent_Wrapper(n) #define digitalPinToPCINT digitalPinToPinChangeInterrupt diff --git a/src/PinChangeInterrupt0.cpp b/src/PinChangeInterrupt0.cpp index c8dfba8..287ab4b 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_PORT01 +void attachPinChangeInterrupt1(void) { + // fake function to make the IDE link this file +} +#endif +#ifdef PCINT_COMBINE_PORT01 +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 + +#ifdef PCINT_COMBINE_PORT01 + // 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 // PCINT_COMBINE_PORT01 } #if defined(PCINT_API) @@ -142,6 +187,57 @@ void PinChangeInterruptEventPCINT7(void) { } #endif +#ifdef PCINT_COMBINE_PORT01 +#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_PORT01 + #endif // PCINT_API #endif // PCINT_USE_PORT0 diff --git a/src/PinChangeInterrupt1.cpp b/src/PinChangeInterrupt1.cpp index e9953cd..9abce31 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_PORT01) void attachPinChangeInterrupt1(void) { // fake function to make the IDE link this file diff --git a/src/PinChangeInterruptBoards.h b/src/PinChangeInterruptBoards.h index fdcfa65..0f135d9 100644 --- a/src/PinChangeInterruptBoards.h +++ b/src/PinChangeInterruptBoards.h @@ -142,6 +142,7 @@ 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 +#define PCINT_COMBINE_PORT01 1 #else // Microcontroller not supported #error PinChangeInterrupt library does not support this MCU. From 74e0fdd8dc5d4c588b034dd3f158e9b555e3426a Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 02:03:45 -0500 Subject: [PATCH 02/12] Update option name Rename PCINT_COMBINE_PORT01 to PCINT_COMBINE_PORT0_PORT1 to be more explicit with what is being combined. --- src/PinChangeInterrupt0.cpp | 12 ++++++------ src/PinChangeInterrupt1.cpp | 2 +- src/PinChangeInterruptBoards.h | 5 ++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/PinChangeInterrupt0.cpp b/src/PinChangeInterrupt0.cpp index 287ab4b..f9e9f4f 100644 --- a/src/PinChangeInterrupt0.cpp +++ b/src/PinChangeInterrupt0.cpp @@ -36,13 +36,13 @@ THE SOFTWARE. void attachPinChangeInterrupt0(void) { // fake function to make the IDE link this file } -#ifdef PCINT_COMBINE_PORT01 +#ifdef PCINT_COMBINE_PORT0_PORT1 void attachPinChangeInterrupt1(void) { // fake function to make the IDE link this file } #endif -#ifdef PCINT_COMBINE_PORT01 +#ifdef PCINT_COMBINE_PORT0_PORT1 ISR(PCINT_vect) { #else ISR(PCINT0_vect) { @@ -81,7 +81,7 @@ ISR(PCINT0_vect) { PCINT_CALLBACK_PORT0 #endif -#ifdef PCINT_COMBINE_PORT01 +#ifdef PCINT_COMBINE_PORT0_PORT1 // get the new and old pin states for port newPort = PCINT_INPUT_PORT1; @@ -115,7 +115,7 @@ ISR(PCINT0_vect) { #else PCINT_CALLBACK_PORT1 #endif -#endif // PCINT_COMBINE_PORT01 +#endif // PCINT_COMBINE_PORT0_PORT1 } #if defined(PCINT_API) @@ -187,7 +187,7 @@ void PinChangeInterruptEventPCINT7(void) { } #endif -#ifdef PCINT_COMBINE_PORT01 +#ifdef PCINT_COMBINE_PORT0_PORT1 #if (PCINT_USE_PCINT8 == true) volatile callback callbackPCINT8 = pcint_null_callback; void PinChangeInterruptEventPCINT8(void) { @@ -236,7 +236,7 @@ void PinChangeInterruptEventPCINT15(void) { callbackPCINT15(); } #endif -#endif // PCINT_COMBINE_PORT01 +#endif // PCINT_COMBINE_PORT0_PORT1 #endif // PCINT_API diff --git a/src/PinChangeInterrupt1.cpp b/src/PinChangeInterrupt1.cpp index 9abce31..3adfa64 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) && !defined(PCINT_COMBINE_PORT01) +#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 0f135d9..7a46f9a 100644 --- a/src/PinChangeInterruptBoards.h +++ b/src/PinChangeInterruptBoards.h @@ -142,7 +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 -#define PCINT_COMBINE_PORT01 1 +// 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 1 #else // Microcontroller not supported #error PinChangeInterrupt library does not support this MCU. From 94f77386953111862d784767325a3a4733d431f2 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 02:11:08 -0500 Subject: [PATCH 03/12] Check if PORT1 is to be used Always check if PORT1 is enabled along with the combined ports for completeness. --- src/PinChangeInterrupt0.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PinChangeInterrupt0.cpp b/src/PinChangeInterrupt0.cpp index f9e9f4f..95f030a 100644 --- a/src/PinChangeInterrupt0.cpp +++ b/src/PinChangeInterrupt0.cpp @@ -81,7 +81,7 @@ ISR(PCINT0_vect) { PCINT_CALLBACK_PORT0 #endif -#ifdef PCINT_COMBINE_PORT0_PORT1 +#if defined(PCINT_COMBINE_PORT0_PORT1) && (PCINT_USE_PORT1 == true) // get the new and old pin states for port newPort = PCINT_INPUT_PORT1; @@ -115,7 +115,7 @@ ISR(PCINT0_vect) { #else PCINT_CALLBACK_PORT1 #endif -#endif // PCINT_COMBINE_PORT0_PORT1 +#endif // defined(PCINT_COMBINE_PORT0_PORT1) && (PCINT_USE_PORT1 == true) } #if defined(PCINT_API) From 3c2bcc9443d0c54db1166024b640a193e798f110 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 02:15:48 -0500 Subject: [PATCH 04/12] Conditionally allow PORT1 to be disabled When enabling PCIE0 and PCIE1 on x61 devices check if PORT1 should be used so that it can still be enabled/disabled separately from PORT0. --- src/PinChangeInterrupt.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 61b5f17..631c207 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -166,12 +166,20 @@ void enablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcint #elif defined(GICR) /* e.g. ATmega162 */ GICR |= (1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) - if (pcintPort == 1 && pcintMask < 16) { - GIMSK |= (1 << PCIE0); - } - else { - GIMSK |= (1 << PCIE1); - } + // 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 */ From 033777433eb088b79f2a18743081a732c039edbb Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 02:39:49 -0500 Subject: [PATCH 05/12] Add disable for x61 Added logic to disable the interrupts entirely if all pin change interrupts are disabled on either port. Allows PORT1 to be disabled separately from PORT0. --- src/PinChangeInterrupt.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 631c207..2ffb5cd 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -210,17 +210,34 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin // disable the mask. PCMSK0 &= ~pcintMask; +#if (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true) + // x61 devices uses the same interrupt enable for all of PCMSK0 (PCINT7:0) and the top half of PCMSK1 (PCINT15:12) + if (!PCMSK0 && !(PCMSK1 & 0b11110000)) + GIMSK &= ~(1 << PCIE1); +#else // if that's the last one, disable the interrupt. if (!PCMSK0) disable = true; +#endif break; case 1: // disable the mask. PCMSK1 &= ~pcintMask; +#if (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true) + // x61 devices use an interrupt specially for the bottom half of PCMASK1 (PCINT11:8) + if (pcintMask < (1 << 4)) { + if (!(PCMSK1 & 0b00001111)) + GIMSK &= ~(1 << PCIE0); + } + // x61 devices uses the same interrupt enable for all of PCMSK0 (PCINT7:0) and the top half of PCMSK1 (PCINT15:12) + else if (!PCMSK0 && !(PCMSK1 & 0b11110000)) + GIMSK &= ~(1 << PCIE1); +#else // if that's the last one, disable the interrupt. if (!PCMSK1) disable = true; +#endif break; #ifdef PCMSK2 case 2: @@ -253,12 +270,18 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin if (*(&PCMSK + pcintPort) == 0) disable = true; #endif + + //the special case for x61 devices will be handled above inline +#if !((defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true)) if(disable) { #ifdef PCICR PCICR &= ~(1 << (pcintPort + PCIE0)); #elif defined(GICR) /* e.g. ATmega162 */ GICR &= ~(1 << (pcintPort + PCIE0)); +// if PORT1 is disabled on a x61 device this will handle the backwards PCIE definitions +#elif defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) + GIMSK &= ~(1 << PCIE1); #elif defined(GIMSK) && defined(PCIE0) /* e.g. ATtiny X4 */ GIMSK &= ~(1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE) /* e.g. ATtiny X5 */ @@ -267,6 +290,7 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin #error MCU has no such a register #endif } +#endif // !((defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true)) } /* From 2c7a9fc1ded03a11c10549ab344421fd6d79748c Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 03:04:48 -0500 Subject: [PATCH 06/12] Remove initPinChangeInterrupt Catch initialization conditions in enablePinChangeInterruptHelper instead of requiring a separate initialization method. --- src/PinChangeInterrupt.cpp | 29 +++++++++++++++++++++++++++++ src/PinChangeInterrupt.h | 6 ------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 2ffb5cd..189c7d9 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -121,6 +121,35 @@ 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 (pcintPort == 1 && pcintMask < (1 << 4)) { + if (!(GIMSK & (1 << PCIE0))) { + // Clear PCINT11:8 + PCMSK1 &= ~0x0F; + } + } + // PCIE1 case + else { +#endif + if (!(GIMSK & (1 << PCIE1))) { +#if (PCINT_USE_PORT1 == true) + // Clear PCINT15:12 + PCMSK1 &= ~0xF0; +#endif + // Clear PCINT7:0 + PCMSK0 = 0x00; + } +#if (PCINT_USE_PORT1 == true) + } +#endif +#endif + // Pin change mask registers decide which pins are ENABLE as triggers #ifdef PCMSK0 #ifdef PCMSK1 diff --git a/src/PinChangeInterrupt.h b/src/PinChangeInterrupt.h index 45b7951..b30fb62 100644 --- a/src/PinChangeInterrupt.h +++ b/src/PinChangeInterrupt.h @@ -107,12 +107,6 @@ PinChangeInterruptEventPCINT ## pcint PCINT_MACRO_BRACKETS #error MCU has no such a register #endif -#if defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__) -#define initPinChangeInterrupt() PCMSK0 = 0; PCMSK1 = 0 -#else -#define initPinChangeInterrupt() -#endif - // alias for shorter writing #define PCINTEvent(n) PinChangeInterruptEvent_Wrapper(n) #define digitalPinToPCINT digitalPinToPinChangeInterrupt From 6b5d48e27d689e4987b4097546401b3794bb9598 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 03:06:49 -0500 Subject: [PATCH 07/12] Use true instead of 1 Increase consistency and use true while defining PCINT_COMBINE_PORT0_PORT1 instead of 1. Shouldn't affect uses elsewhere. --- src/PinChangeInterruptBoards.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PinChangeInterruptBoards.h b/src/PinChangeInterruptBoards.h index 7a46f9a..d3c44f1 100644 --- a/src/PinChangeInterruptBoards.h +++ b/src/PinChangeInterruptBoards.h @@ -145,7 +145,7 @@ THE SOFTWARE. // 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 1 +#define PCINT_COMBINE_PORT0_PORT1 true #else // Microcontroller not supported #error PinChangeInterrupt library does not support this MCU. From d129ff55f8c0e527e97f13d7dbc4e4d8f8cc0bd1 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Fri, 9 Jul 2021 03:21:53 -0500 Subject: [PATCH 08/12] Remove initPinChangeInterrupt keyword The initialization method has been removed so the keyword is no longer required. --- keywords.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/keywords.txt b/keywords.txt index 46cdae5..1af2b5d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -22,7 +22,6 @@ disablePCINT KEYWORD2 disablePinChangeInterrupt KEYWORD2 getPCINTTrigger KEYWORD2 getPinChangeInterruptTrigger KEYWORD2 -initPinChangeInterrupt KEYWORD2 ####################################### # Instances (KEYWORD2) From 7ef2dbeec096d85e9eb9bebeb90ded7f4032d614 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Mon, 12 Jul 2021 00:04:09 -0500 Subject: [PATCH 09/12] Rearrange disable helper for x61 Move some of the logic for determining when an interrupt flag can be cleared from the switch statement to the if (disabled) at the end. --- src/PinChangeInterrupt.cpp | 49 ++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 189c7d9..8d151b3 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -239,34 +239,25 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin // disable the mask. PCMSK0 &= ~pcintMask; -#if (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true) - // x61 devices uses the same interrupt enable for all of PCMSK0 (PCINT7:0) and the top half of PCMSK1 (PCINT15:12) - if (!PCMSK0 && !(PCMSK1 & 0b11110000)) - GIMSK &= ~(1 << PCIE1); -#else // if that's the last one, disable the interrupt. if (!PCMSK0) disable = true; -#endif break; case 1: // disable the mask. PCMSK1 &= ~pcintMask; + // if that's the last one, disable the interrupt. #if (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true) - // x61 devices use an interrupt specially for the bottom half of PCMASK1 (PCINT11:8) - if (pcintMask < (1 << 4)) { - if (!(PCMSK1 & 0b00001111)) - GIMSK &= ~(1 << PCIE0); - } - // x61 devices uses the same interrupt enable for all of PCMSK0 (PCINT7:0) and the top half of PCMSK1 (PCINT15:12) - else if (!PCMSK0 && !(PCMSK1 & 0b11110000)) - GIMSK &= ~(1 << PCIE1); + //have to check each half of the register separately to know if something may be disabled. If it is + //checked all together then an interrupt enabled on pins 15:12 would stop PCIE0 from being disabled + //and an interrupt enabled on pins 11:8 would stop PCIE1 from being disabled if the last interrupt + //disabled was on one of pins 15:12 (it would still work if the last one disabled was in pins 7:0). + if (!(PCMSK1 & 0x0F) || !(PCMSK1 & 0xF0)) #else - // if that's the last one, disable the interrupt. if (!PCMSK1) - disable = true; #endif + disable = true; break; #ifdef PCMSK2 case 2: @@ -300,17 +291,34 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin disable = true; #endif - //the special case for x61 devices will be handled above inline -#if !((defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true)) if(disable) { #ifdef PCICR PCICR &= ~(1 << (pcintPort + PCIE0)); #elif defined(GICR) /* e.g. ATmega162 */ GICR &= ~(1 << (pcintPort + PCIE0)); -// if PORT1 is disabled on a x61 device this will handle the backwards PCIE definitions #elif defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) - GIMSK &= ~(1 << PCIE1); + // 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 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 #elif defined(GIMSK) && defined(PCIE0) /* e.g. ATtiny X4 */ GIMSK &= ~(1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE) /* e.g. ATtiny X5 */ @@ -319,7 +327,6 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin #error MCU has no such a register #endif } -#endif // !((defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true)) } /* From 4a28a87ddf4b35cc48fc2730c251de9be208daf1 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Mon, 12 Jul 2021 01:36:04 -0500 Subject: [PATCH 10/12] getArrayPosPCINT returning incorrect value The count of the available ports was incorrect because of the change in interrupt vector names from other versions causing getArrayPosPCINT to always return 0. Add in special cases for checking PORT0 and PORT1. --- src/PinChangeInterruptPins.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PinChangeInterruptPins.h b/src/PinChangeInterruptPins.h index 832b6bd..1bd8e86 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 From 487761615182273f459ab00442ccad68e250cfda Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Mon, 12 Jul 2021 01:48:37 -0500 Subject: [PATCH 11/12] Simplify disable logic Remove the need for the disable flag and directly check the status of the masks instead. --- src/PinChangeInterrupt.cpp | 54 +++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 8d151b3..14da8e2 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -248,15 +248,7 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin PCMSK1 &= ~pcintMask; // if that's the last one, disable the interrupt. -#if (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) && (PCINT_USE_PORT1 == true) - //have to check each half of the register separately to know if something may be disabled. If it is - //checked all together then an interrupt enabled on pins 15:12 would stop PCIE0 from being disabled - //and an interrupt enabled on pins 11:8 would stop PCIE1 from being disabled if the last interrupt - //disabled was on one of pins 15:12 (it would still work if the last one disabled was in pins 7:0). - if (!(PCMSK1 & 0x0F) || !(PCMSK1 & 0xF0)) -#else if (!PCMSK1) -#endif disable = true; break; #ifdef PCMSK2 @@ -291,34 +283,35 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin disable = true; #endif - if(disable) - { -#ifdef PCICR - 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 + // 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); - } + 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 { + //PCINT7:0 and PCINT15:12 will be disabled with PCIE1 + if (!PCMSK0 && !(PCMSK1 & 0xF0)) { #else - if (!PCMSK0) { + if (!PCMSK0) { #endif - GIMSK &= ~(1 << PCIE1); - } -#if (PCINT_USE_PORT1 == true) + GIMSK &= ~(1 << PCIE1); } +#if (PCINT_USE_PORT1 == true) + } #endif +#else + if(disable) + { +#ifdef PCICR + PCICR &= ~(1 << (pcintPort + PCIE0)); +#elif defined(GICR) /* e.g. ATmega162 */ + GICR &= ~(1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE0) /* e.g. ATtiny X4 */ GIMSK &= ~(1 << (pcintPort + PCIE0)); #elif defined(GIMSK) && defined(PCIE) /* e.g. ATtiny X5 */ @@ -327,6 +320,7 @@ void disablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcin #error MCU has no such a register #endif } +#endif } /* From 13219eb4ba2a5788a295c192229dd5403a3e7a31 Mon Sep 17 00:00:00 2001 From: Travis Redpath Date: Mon, 12 Jul 2021 01:54:31 -0500 Subject: [PATCH 12/12] Always clear interrupt masks on enable When the user is enabling interrupts always clear the interrupt mask if the pin change interrupt flag is not yet set. This will cause redundant clears of the registers but removes some of the overhead of checking which pins the user is attempting to set. --- src/PinChangeInterrupt.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/PinChangeInterrupt.cpp b/src/PinChangeInterrupt.cpp index 14da8e2..b5999f9 100644 --- a/src/PinChangeInterrupt.cpp +++ b/src/PinChangeInterrupt.cpp @@ -128,26 +128,20 @@ void enablePinChangeInterruptHelper(const uint8_t pcintPort, const uint8_t pcint #if defined(GIMSK) && (defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)) #if (PCINT_USE_PORT1 == true) // PCIE0 case - if (pcintPort == 1 && pcintMask < (1 << 4)) { - if (!(GIMSK & (1 << PCIE0))) { - // Clear PCINT11:8 - PCMSK1 &= ~0x0F; - } + if (!(GIMSK & (1 << PCIE0))) { + // Clear PCINT11:8 + PCMSK1 &= ~0x0F; } // PCIE1 case - else { #endif - if (!(GIMSK & (1 << PCIE1))) { + if (!(GIMSK & (1 << PCIE1))) { #if (PCINT_USE_PORT1 == true) - // Clear PCINT15:12 - PCMSK1 &= ~0xF0; + // Clear PCINT15:12 + PCMSK1 &= ~0xF0; #endif - // Clear PCINT7:0 - PCMSK0 = 0x00; - } -#if (PCINT_USE_PORT1 == true) + // Clear PCINT7:0 + PCMSK0 = 0x00; } -#endif #endif // Pin change mask registers decide which pins are ENABLE as triggers