From 0e5192ce69a2592f0954e9ceee18b3fe420adb45 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 1 May 2019 10:49:15 +0300 Subject: [PATCH 1/8] ESP8266 / ESP32: Use FunctionalInterrupt by default --- Encoder.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Encoder.h b/Encoder.h index 6e0a2c3..4b25faa 100644 --- a/Encoder.h +++ b/Encoder.h @@ -51,6 +51,22 @@ #define ENCODER_ARGLIST_SIZE 0 #endif +// We need to manually place interrupt handlers in RAM for these platforms +#if defined(ESP8266) || defined(ESP32) + +#ifndef ENCODER_DO_NOT_USE_FUNCTIONAL_INTERRUPTS +#include +#undef ENCODER_USE_INTERRUPTS +#define ENCODER_USE_FUNCTIONAL_INTERRUPTS +#endif + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#else // !defined(ESP32) && !defined(ESP8266) +#define IRAM_ATTR +#endif // All the data needed by interrupts is consolidated into this ugly struct @@ -203,7 +219,7 @@ class Encoder // update() is not meant to be called from outside Encoder, // but it is public to allow static interrupt routines. // DO NOT call update() directly from sketches. - static void update(Encoder_internal_state_t *arg) { + static void IRAM_ATTR update(Encoder_internal_state_t *arg) { #if defined(__AVR__) // The compiler believes this is just 1 line of code, so // it will inline this function into each interrupt @@ -371,6 +387,12 @@ class Encoder #endif */ +#ifdef ENCODER_USE_FUNCTIONAL_INTERRUPTS + static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) { + attachInterrupt(pin, std::bind(update, state), CHANGE); + return 1; + } +#endif #ifdef ENCODER_USE_INTERRUPTS // this giant function is an unfortunate consequence of Arduino's From a011aced354daf6816d79eda6f63d92f6d6eae01 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 1 May 2019 10:56:00 +0300 Subject: [PATCH 2/8] No use for Encoder::interruptArgs when using functional interrupts --- Encoder.cpp | 2 ++ Encoder.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Encoder.cpp b/Encoder.cpp index 6911b4f..ab141c8 100644 --- a/Encoder.cpp +++ b/Encoder.cpp @@ -5,6 +5,8 @@ // configure options with #define (before they include it), and // to facilitate some crafty optimizations! +#ifndef ENCODER_USE_FUNCTIONAL_INTERRUPTS Encoder_internal_state_t * Encoder::interruptArgs[]; +#endif diff --git a/Encoder.h b/Encoder.h index 4b25faa..0c6dc63 100644 --- a/Encoder.h +++ b/Encoder.h @@ -166,7 +166,9 @@ class Encoder uint8_t interrupts_in_use; #endif public: +#ifndef ENCODER_USE_FUNCTIONAL_INTERRUPTS static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE]; +#endif // _______ _______ // Pin1 ______| |_______| |______ Pin1 From 4b5bcde0661da4371c26cd2da1b29cded6f289e0 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 1 May 2019 11:49:41 +0300 Subject: [PATCH 3/8] Force to use functional interrupt --- Encoder.cpp | 2 -- Encoder.h | 21 ++++++++------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Encoder.cpp b/Encoder.cpp index ab141c8..6911b4f 100644 --- a/Encoder.cpp +++ b/Encoder.cpp @@ -5,8 +5,6 @@ // configure options with #define (before they include it), and // to facilitate some crafty optimizations! -#ifndef ENCODER_USE_FUNCTIONAL_INTERRUPTS Encoder_internal_state_t * Encoder::interruptArgs[]; -#endif diff --git a/Encoder.h b/Encoder.h index 0c6dc63..fb7d4df 100644 --- a/Encoder.h +++ b/Encoder.h @@ -54,11 +54,8 @@ // We need to manually place interrupt handlers in RAM for these platforms #if defined(ESP8266) || defined(ESP32) -#ifndef ENCODER_DO_NOT_USE_FUNCTIONAL_INTERRUPTS #include -#undef ENCODER_USE_INTERRUPTS #define ENCODER_USE_FUNCTIONAL_INTERRUPTS -#endif #ifndef IRAM_ATTR #define IRAM_ATTR ICACHE_RAM_ATTR @@ -166,9 +163,7 @@ class Encoder uint8_t interrupts_in_use; #endif public: -#ifndef ENCODER_USE_FUNCTIONAL_INTERRUPTS static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE]; -#endif // _______ _______ // Pin1 ______| |_______| |______ Pin1 @@ -389,14 +384,13 @@ class Encoder #endif */ -#ifdef ENCODER_USE_FUNCTIONAL_INTERRUPTS - static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) { - attachInterrupt(pin, std::bind(update, state), CHANGE); - return 1; - } -#endif - #ifdef ENCODER_USE_INTERRUPTS +#ifdef ENCODER_USE_FUNCTIONAL_INTERRUPTS + static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) { + attachInterrupt(pin, std::bind(update, state), CHANGE); + return 1; + } +#else // this giant function is an unfortunate consequence of Arduino's // attachInterrupt function not supporting any way to pass a pointer // or other context to the attached function. @@ -768,10 +762,11 @@ class Encoder } return 1; } +#endif // !ENCODER_USE_FUNCTIONAL_INTERRUPTS #endif // ENCODER_USE_INTERRUPTS -#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS) +#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS) && !defined(ENCODER_USE_FUNCTIONAL_INTERRUPTS) #ifdef CORE_INT0_PIN static void isr0(void) { update(interruptArgs[0]); } #endif From 7c55bdb2754b8a7238bf04ec66960a959b1acb05 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 1 May 2019 20:16:50 +0300 Subject: [PATCH 4/8] Use namespace, move update outside of the class --- Encoder.h | 322 +++++++++++++++++++++++++++--------------------------- 1 file changed, 163 insertions(+), 159 deletions(-) diff --git a/Encoder.h b/Encoder.h index fb7d4df..ff7d07d 100644 --- a/Encoder.h +++ b/Encoder.h @@ -65,6 +65,7 @@ #define IRAM_ATTR #endif +namespace EncoderInternal { // All the data needed by interrupts is consolidated into this ugly struct // to facilitate assembly language optimizing of the speed critical update. @@ -79,6 +80,164 @@ typedef struct { int32_t position; } Encoder_internal_state_t; +// _______ _______ +// Pin1 ______| |_______| |______ Pin1 +// negative <--- _______ _______ __ --> positive +// Pin2 __| |_______| |_______| Pin2 + + // new new old old + // pin2 pin1 pin2 pin1 Result + // ---- ---- ---- ---- ------ + // 0 0 0 0 no movement + // 0 0 0 1 +1 + // 0 0 1 0 -1 + // 0 0 1 1 +2 (assume pin1 edges only) + // 0 1 0 0 -1 + // 0 1 0 1 no movement + // 0 1 1 0 -2 (assume pin1 edges only) + // 0 1 1 1 +1 + // 1 0 0 0 +1 + // 1 0 0 1 -2 (assume pin1 edges only) + // 1 0 1 0 no movement + // 1 0 1 1 -1 + // 1 1 0 0 +2 (assume pin1 edges only) + // 1 1 0 1 -1 + // 1 1 1 0 +1 + // 1 1 1 1 no movement +/* + // Simple, easy-to-read "documentation" version :-) + // + void update(void) { + uint8_t s = state & 3; + if (digitalRead(pin1)) s |= 4; + if (digitalRead(pin2)) s |= 8; + switch (s) { + case 0: case 5: case 10: case 15: + break; + case 1: case 7: case 8: case 14: + position++; break; + case 2: case 4: case 11: case 13: + position--; break; + case 3: case 12: + position += 2; break; + default: + position -= 2; break; + } + state = (s >> 2); + } +*/ + +// update() is not meant to be called from outside Encoder, +// DO NOT call update() directly from sketches. +void IRAM_ATTR update(Encoder_internal_state_t *arg) { +#if defined(__AVR__) + // The compiler believes this is just 1 line of code, so + // it will inline this function into each interrupt + // handler. That's a tiny bit faster, but grows the code. + // Especially when used with ENCODER_OPTIMIZE_INTERRUPTS, + // the inline nature allows the ISR prologue and epilogue + // to only save/restore necessary registers, for very nice + // speed increase. + asm volatile ( + "ld r30, X+" "\n\t" + "ld r31, X+" "\n\t" + "ld r24, Z" "\n\t" // r24 = pin1 input + "ld r30, X+" "\n\t" + "ld r31, X+" "\n\t" + "ld r25, Z" "\n\t" // r25 = pin2 input + "ld r30, X+" "\n\t" // r30 = pin1 mask + "ld r31, X+" "\n\t" // r31 = pin2 mask + "ld r22, X" "\n\t" // r22 = state + "andi r22, 3" "\n\t" + "and r24, r30" "\n\t" + "breq L%=1" "\n\t" // if (pin1) + "ori r22, 4" "\n\t" // state |= 4 + "L%=1:" "and r25, r31" "\n\t" + "breq L%=2" "\n\t" // if (pin2) + "ori r22, 8" "\n\t" // state |= 8 + "L%=2:" "ldi r30, lo8(pm(L%=table))" "\n\t" + "ldi r31, hi8(pm(L%=table))" "\n\t" + "add r30, r22" "\n\t" + "adc r31, __zero_reg__" "\n\t" + "asr r22" "\n\t" + "asr r22" "\n\t" + "st X+, r22" "\n\t" // store new state + "ld r22, X+" "\n\t" + "ld r23, X+" "\n\t" + "ld r24, X+" "\n\t" + "ld r25, X+" "\n\t" + "ijmp" "\n\t" // jumps to update_finishup() + // TODO move this table to another static function, + // so it doesn't get needlessly duplicated. Easier + // said than done, due to linker issues and inlining + "L%=table:" "\n\t" + "rjmp L%=end" "\n\t" // 0 + "rjmp L%=plus1" "\n\t" // 1 + "rjmp L%=minus1" "\n\t" // 2 + "rjmp L%=plus2" "\n\t" // 3 + "rjmp L%=minus1" "\n\t" // 4 + "rjmp L%=end" "\n\t" // 5 + "rjmp L%=minus2" "\n\t" // 6 + "rjmp L%=plus1" "\n\t" // 7 + "rjmp L%=plus1" "\n\t" // 8 + "rjmp L%=minus2" "\n\t" // 9 + "rjmp L%=end" "\n\t" // 10 + "rjmp L%=minus1" "\n\t" // 11 + "rjmp L%=plus2" "\n\t" // 12 + "rjmp L%=minus1" "\n\t" // 13 + "rjmp L%=plus1" "\n\t" // 14 + "rjmp L%=end" "\n\t" // 15 + "L%=minus2:" "\n\t" + "subi r22, 2" "\n\t" + "sbci r23, 0" "\n\t" + "sbci r24, 0" "\n\t" + "sbci r25, 0" "\n\t" + "rjmp L%=store" "\n\t" + "L%=minus1:" "\n\t" + "subi r22, 1" "\n\t" + "sbci r23, 0" "\n\t" + "sbci r24, 0" "\n\t" + "sbci r25, 0" "\n\t" + "rjmp L%=store" "\n\t" + "L%=plus2:" "\n\t" + "subi r22, 254" "\n\t" + "rjmp L%=z" "\n\t" + "L%=plus1:" "\n\t" + "subi r22, 255" "\n\t" + "L%=z:" "sbci r23, 255" "\n\t" + "sbci r24, 255" "\n\t" + "sbci r25, 255" "\n\t" + "L%=store:" "\n\t" + "st -X, r25" "\n\t" + "st -X, r24" "\n\t" + "st -X, r23" "\n\t" + "st -X, r22" "\n\t" + "L%=end:" "\n" + : : "x" (arg) : "r22", "r23", "r24", "r25", "r30", "r31"); +#else + uint8_t p1val = DIRECT_PIN_READ(arg->pin1_register, arg->pin1_bitmask); + uint8_t p2val = DIRECT_PIN_READ(arg->pin2_register, arg->pin2_bitmask); + uint8_t state = arg->state & 3; + if (p1val) state |= 4; + if (p2val) state |= 8; + arg->state = (state >> 2); + switch (state) { + case 1: case 7: case 8: case 14: + arg->position++; + return; + case 2: case 4: case 11: case 13: + arg->position--; + return; + case 3: case 12: + arg->position += 2; + return; + case 6: case 9: + arg->position -= 2; + return; + } +#endif +} + class Encoder { public: @@ -165,165 +324,6 @@ class Encoder public: static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE]; -// _______ _______ -// Pin1 ______| |_______| |______ Pin1 -// negative <--- _______ _______ __ --> positive -// Pin2 __| |_______| |_______| Pin2 - - // new new old old - // pin2 pin1 pin2 pin1 Result - // ---- ---- ---- ---- ------ - // 0 0 0 0 no movement - // 0 0 0 1 +1 - // 0 0 1 0 -1 - // 0 0 1 1 +2 (assume pin1 edges only) - // 0 1 0 0 -1 - // 0 1 0 1 no movement - // 0 1 1 0 -2 (assume pin1 edges only) - // 0 1 1 1 +1 - // 1 0 0 0 +1 - // 1 0 0 1 -2 (assume pin1 edges only) - // 1 0 1 0 no movement - // 1 0 1 1 -1 - // 1 1 0 0 +2 (assume pin1 edges only) - // 1 1 0 1 -1 - // 1 1 1 0 +1 - // 1 1 1 1 no movement -/* - // Simple, easy-to-read "documentation" version :-) - // - void update(void) { - uint8_t s = state & 3; - if (digitalRead(pin1)) s |= 4; - if (digitalRead(pin2)) s |= 8; - switch (s) { - case 0: case 5: case 10: case 15: - break; - case 1: case 7: case 8: case 14: - position++; break; - case 2: case 4: case 11: case 13: - position--; break; - case 3: case 12: - position += 2; break; - default: - position -= 2; break; - } - state = (s >> 2); - } -*/ - -public: - // update() is not meant to be called from outside Encoder, - // but it is public to allow static interrupt routines. - // DO NOT call update() directly from sketches. - static void IRAM_ATTR update(Encoder_internal_state_t *arg) { -#if defined(__AVR__) - // The compiler believes this is just 1 line of code, so - // it will inline this function into each interrupt - // handler. That's a tiny bit faster, but grows the code. - // Especially when used with ENCODER_OPTIMIZE_INTERRUPTS, - // the inline nature allows the ISR prologue and epilogue - // to only save/restore necessary registers, for very nice - // speed increase. - asm volatile ( - "ld r30, X+" "\n\t" - "ld r31, X+" "\n\t" - "ld r24, Z" "\n\t" // r24 = pin1 input - "ld r30, X+" "\n\t" - "ld r31, X+" "\n\t" - "ld r25, Z" "\n\t" // r25 = pin2 input - "ld r30, X+" "\n\t" // r30 = pin1 mask - "ld r31, X+" "\n\t" // r31 = pin2 mask - "ld r22, X" "\n\t" // r22 = state - "andi r22, 3" "\n\t" - "and r24, r30" "\n\t" - "breq L%=1" "\n\t" // if (pin1) - "ori r22, 4" "\n\t" // state |= 4 - "L%=1:" "and r25, r31" "\n\t" - "breq L%=2" "\n\t" // if (pin2) - "ori r22, 8" "\n\t" // state |= 8 - "L%=2:" "ldi r30, lo8(pm(L%=table))" "\n\t" - "ldi r31, hi8(pm(L%=table))" "\n\t" - "add r30, r22" "\n\t" - "adc r31, __zero_reg__" "\n\t" - "asr r22" "\n\t" - "asr r22" "\n\t" - "st X+, r22" "\n\t" // store new state - "ld r22, X+" "\n\t" - "ld r23, X+" "\n\t" - "ld r24, X+" "\n\t" - "ld r25, X+" "\n\t" - "ijmp" "\n\t" // jumps to update_finishup() - // TODO move this table to another static function, - // so it doesn't get needlessly duplicated. Easier - // said than done, due to linker issues and inlining - "L%=table:" "\n\t" - "rjmp L%=end" "\n\t" // 0 - "rjmp L%=plus1" "\n\t" // 1 - "rjmp L%=minus1" "\n\t" // 2 - "rjmp L%=plus2" "\n\t" // 3 - "rjmp L%=minus1" "\n\t" // 4 - "rjmp L%=end" "\n\t" // 5 - "rjmp L%=minus2" "\n\t" // 6 - "rjmp L%=plus1" "\n\t" // 7 - "rjmp L%=plus1" "\n\t" // 8 - "rjmp L%=minus2" "\n\t" // 9 - "rjmp L%=end" "\n\t" // 10 - "rjmp L%=minus1" "\n\t" // 11 - "rjmp L%=plus2" "\n\t" // 12 - "rjmp L%=minus1" "\n\t" // 13 - "rjmp L%=plus1" "\n\t" // 14 - "rjmp L%=end" "\n\t" // 15 - "L%=minus2:" "\n\t" - "subi r22, 2" "\n\t" - "sbci r23, 0" "\n\t" - "sbci r24, 0" "\n\t" - "sbci r25, 0" "\n\t" - "rjmp L%=store" "\n\t" - "L%=minus1:" "\n\t" - "subi r22, 1" "\n\t" - "sbci r23, 0" "\n\t" - "sbci r24, 0" "\n\t" - "sbci r25, 0" "\n\t" - "rjmp L%=store" "\n\t" - "L%=plus2:" "\n\t" - "subi r22, 254" "\n\t" - "rjmp L%=z" "\n\t" - "L%=plus1:" "\n\t" - "subi r22, 255" "\n\t" - "L%=z:" "sbci r23, 255" "\n\t" - "sbci r24, 255" "\n\t" - "sbci r25, 255" "\n\t" - "L%=store:" "\n\t" - "st -X, r25" "\n\t" - "st -X, r24" "\n\t" - "st -X, r23" "\n\t" - "st -X, r22" "\n\t" - "L%=end:" "\n" - : : "x" (arg) : "r22", "r23", "r24", "r25", "r30", "r31"); -#else - uint8_t p1val = DIRECT_PIN_READ(arg->pin1_register, arg->pin1_bitmask); - uint8_t p2val = DIRECT_PIN_READ(arg->pin2_register, arg->pin2_bitmask); - uint8_t state = arg->state & 3; - if (p1val) state |= 4; - if (p2val) state |= 8; - arg->state = (state >> 2); - switch (state) { - case 1: case 7: case 8: case 14: - arg->position++; - return; - case 2: case 4: case 11: case 13: - arg->position--; - return; - case 3: case 12: - arg->position += 2; - return; - case 6: case 9: - arg->position -= 2; - return; - } -#endif - } private: /* #if defined(__AVR__) @@ -986,3 +986,7 @@ ISR(INT7_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(7)]); #endif +} + +// We only need the Encoder class exported +using Encoder = EncoderInternal::Encoder; From a16c209053512cc71bba9138af6eb36bc4d66e41 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 4 May 2019 14:53:10 +0300 Subject: [PATCH 5/8] state is already in internal ns --- Encoder.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Encoder.h b/Encoder.h index ff7d07d..9eb7b1e 100644 --- a/Encoder.h +++ b/Encoder.h @@ -78,7 +78,7 @@ typedef struct { IO_REG_TYPE pin2_bitmask; uint8_t state; int32_t position; -} Encoder_internal_state_t; +} Encoder_state_t; // _______ _______ // Pin1 ______| |_______| |______ Pin1 @@ -129,7 +129,7 @@ typedef struct { // update() is not meant to be called from outside Encoder, // DO NOT call update() directly from sketches. -void IRAM_ATTR update(Encoder_internal_state_t *arg) { +void IRAM_ATTR update(Encoder_state_t *arg) { #if defined(__AVR__) // The compiler believes this is just 1 line of code, so // it will inline this function into each interrupt @@ -317,12 +317,12 @@ class Encoder } #endif private: - Encoder_internal_state_t encoder; + Encoder_state_t encoder; #ifdef ENCODER_USE_INTERRUPTS uint8_t interrupts_in_use; #endif public: - static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE]; + static Encoder_state_t * interruptArgs[ENCODER_ARGLIST_SIZE]; private: /* @@ -386,7 +386,7 @@ class Encoder #ifdef ENCODER_USE_INTERRUPTS #ifdef ENCODER_USE_FUNCTIONAL_INTERRUPTS - static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) { + static uint8_t attach_interrupt(uint8_t pin, Encoder_state_t *state) { attachInterrupt(pin, std::bind(update, state), CHANGE); return 1; } @@ -394,7 +394,7 @@ class Encoder // this giant function is an unfortunate consequence of Arduino's // attachInterrupt function not supporting any way to pass a pointer // or other context to the attached function. - static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) { + static uint8_t attach_interrupt(uint8_t pin, Encoder_state_t *state) { switch (pin) { #ifdef CORE_INT0_PIN case CORE_INT0_PIN: From 8c0cef91ca92a30f03b15db7eeae2705c6e8beb8 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 4 May 2019 14:53:18 +0300 Subject: [PATCH 6/8] fix initialization --- Encoder.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Encoder.cpp b/Encoder.cpp index 6911b4f..314e270 100644 --- a/Encoder.cpp +++ b/Encoder.cpp @@ -5,6 +5,5 @@ // configure options with #define (before they include it), and // to facilitate some crafty optimizations! -Encoder_internal_state_t * Encoder::interruptArgs[]; - +EncoderInternal::Encoder_state_t* EncoderInternal::Encoder::interruptArgs[]; From e288a8533026d643ce6d698a769af4a43190b394 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 4 May 2019 15:22:36 +0300 Subject: [PATCH 7/8] arduino-esp32 defines ICACHE_RAM_ATTR --- Encoder.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Encoder.h b/Encoder.h index 9eb7b1e..74fbe43 100644 --- a/Encoder.h +++ b/Encoder.h @@ -62,7 +62,7 @@ #endif #else // !defined(ESP32) && !defined(ESP8266) -#define IRAM_ATTR +#define ICACHE_RAM_ATTR #endif namespace EncoderInternal { @@ -129,7 +129,7 @@ typedef struct { // update() is not meant to be called from outside Encoder, // DO NOT call update() directly from sketches. -void IRAM_ATTR update(Encoder_state_t *arg) { +void ICACHE_RAM_ATTR update(Encoder_state_t *arg) { #if defined(__AVR__) // The compiler believes this is just 1 line of code, so // it will inline this function into each interrupt From 85ea407d11b6fb8ca1f534cbd0a7a9c9d5553b2d Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Sat, 4 May 2019 16:09:36 +0300 Subject: [PATCH 8/8] Use attachInterruptArg for ESP32 --- Encoder.h | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Encoder.h b/Encoder.h index 74fbe43..b5caab1 100644 --- a/Encoder.h +++ b/Encoder.h @@ -54,11 +54,12 @@ // We need to manually place interrupt handlers in RAM for these platforms #if defined(ESP8266) || defined(ESP32) +// ESP8266 Does not expose attachInterruptArg (yet) +#if defined(ESP8266) #include -#define ENCODER_USE_FUNCTIONAL_INTERRUPTS - -#ifndef IRAM_ATTR -#define IRAM_ATTR ICACHE_RAM_ATTR +#define ENCODER_USE_FUNCTIONAL_INTERRUPT +#else +#define ENCODER_USE_ATTACH_INTERRUPT_ARG #endif #else // !defined(ESP32) && !defined(ESP8266) @@ -129,7 +130,12 @@ typedef struct { // update() is not meant to be called from outside Encoder, // DO NOT call update() directly from sketches. +#if defined(ENCODER_USE_ATTACH_INTERRUPT_ARG) +void ICACHE_RAM_ATTR update(void * varg) { + Encoder_state_t *arg = reinterpret_cast(varg); +#else void ICACHE_RAM_ATTR update(Encoder_state_t *arg) { +#endif #if defined(__AVR__) // The compiler believes this is just 1 line of code, so // it will inline this function into each interrupt @@ -385,11 +391,16 @@ class Encoder */ #ifdef ENCODER_USE_INTERRUPTS -#ifdef ENCODER_USE_FUNCTIONAL_INTERRUPTS +#if defined(ENCODER_USE_FUNCTIONAL_INTERRUPT) static uint8_t attach_interrupt(uint8_t pin, Encoder_state_t *state) { attachInterrupt(pin, std::bind(update, state), CHANGE); return 1; } +#elif defined(ENCODER_USE_ATTACH_INTERRUPT_ARG) + static uint8_t attach_interrupt(uint8_t pin, Encoder_state_t *state) { + attachInterruptArg(pin, update, state, CHANGE); + return 1; + } #else // this giant function is an unfortunate consequence of Arduino's // attachInterrupt function not supporting any way to pass a pointer @@ -762,11 +773,12 @@ class Encoder } return 1; } -#endif // !ENCODER_USE_FUNCTIONAL_INTERRUPTS +#endif // !ENCODER_USE_FUNCTIONAL_INTERRUPT && !ENCODER_USE_ATTACH_INTERRUPT_ARG #endif // ENCODER_USE_INTERRUPTS -#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS) && !defined(ENCODER_USE_FUNCTIONAL_INTERRUPTS) +#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS) && \ + !defined(ENCODER_USE_FUNCTIONAL_INTERRUPT) && !defined(ENCODER_USE_ATTACH_INTERRUPT_ARG) #ifdef CORE_INT0_PIN static void isr0(void) { update(interruptArgs[0]); } #endif