From f6d7f403b7a33e14c21eb6f7b079cf1dbae4f494 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 1 Jan 2021 18:20:31 +0100 Subject: [PATCH 1/5] all-in functional squash commit. --- cores/esp8266/Arduino.h | 3 + cores/esp8266/FunctionalInterrupt.cpp | 65 --- cores/esp8266/FunctionalInterrupt.h | 35 -- cores/esp8266/core_esp8266_wiring_digital.cpp | 419 +++++++++--------- .../examples/Functional/Functional.ino | 74 ++++ .../ScheduledFunctional.ino | 69 +++ libraries/FunctionalInterrupt/keywords.txt | 11 + .../FunctionalInterrupt/library.properties | 10 + .../src/FunctionalInterrupt.cpp | 36 ++ .../src/FunctionalInterrupt.h | 18 + 10 files changed, 435 insertions(+), 305 deletions(-) delete mode 100644 cores/esp8266/FunctionalInterrupt.cpp delete mode 100644 cores/esp8266/FunctionalInterrupt.h create mode 100644 libraries/FunctionalInterrupt/examples/Functional/Functional.ino create mode 100644 libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino create mode 100644 libraries/FunctionalInterrupt/keywords.txt create mode 100644 libraries/FunctionalInterrupt/library.properties create mode 100644 libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp create mode 100644 libraries/FunctionalInterrupt/src/FunctionalInterrupt.h diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 60737e0195..0465779d2d 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -229,6 +229,7 @@ void optimistic_yield(uint32_t interval_us); #include #include #include +#include "Delegate.h" #include "mmu_iram.h" @@ -275,6 +276,8 @@ void setTZ(const char* tz); void configTime(const char* tz, const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); +void attachInterrupt(uint8_t pin, Delegate userFunc, int mode); + // configures with approximated TZ value. part of the old api, prefer configTime with TZ variable void configTime(int timezone, int daylightOffset_sec, const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp deleted file mode 100644 index f468595787..0000000000 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include "Arduino.h" - -// Duplicate typedefs from core_esp8266_wiring_digital_c -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); - -// Helper functions for Functional interrupt routines -extern "C" void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode, bool functional); - - -void IRAM_ATTR interruptFunctional(void* arg) -{ - ArgStructure* localArg = (ArgStructure*)arg; - if (localArg->functionInfo->reqScheduledFunction) - { - schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo)))); - } - if (localArg->functionInfo->reqFunction) - { - localArg->functionInfo->reqFunction(); - } -} - -extern "C" -{ - void cleanupFunctional(void* arg) - { - ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; - delete localArg; - } -} - -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) -{ - // use the local interrupt routine which takes the ArgStructure as argument - - InterruptInfo* ii = nullptr; - - FunctionInfo* fi = new FunctionInfo; - fi->reqFunction = intRoutine; - - ArgStructure* as = new ArgStructure; - as->interruptInfo = ii; - as->functionInfo = fi; - - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); -} - -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) -{ - InterruptInfo* ii = new InterruptInfo; - - FunctionInfo* fi = new FunctionInfo; - fi->reqScheduledFunction = scheduledIntRoutine; - - ArgStructure* as = new ArgStructure; - as->interruptInfo = ii; - as->functionInfo = fi; - - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); -} diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h deleted file mode 100644 index 968793e499..0000000000 --- a/cores/esp8266/FunctionalInterrupt.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef FUNCTIONALINTERRUPT_H -#define FUNCTIONALINTERRUPT_H - -#include -#include -#include - -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -} - -// Structures for communication - -struct InterruptInfo { - uint8_t pin = 0; - uint8_t value = 0; - uint32_t micro = 0; -}; - -struct FunctionInfo { - std::function reqFunction = nullptr; - std::function reqScheduledFunction = nullptr; -}; - -struct ArgStructure { - InterruptInfo* interruptInfo = nullptr; - FunctionInfo* functionInfo = nullptr; -}; - -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); - - -#endif //INTERRUPTS_H diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 6b42c3ee36..9232f178ce 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -30,236 +30,245 @@ extern "C" { -volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 }; + volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 }; -extern void __pinMode(uint8_t pin, uint8_t mode) { - if(pin < 16){ - if(mode == SPECIAL){ - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - GPEC = (1 << pin); //Disable - GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) - if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX - } else if(mode & FUNCTION_0){ - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - GPEC = (1 << pin); //Disable - GPF(pin) = GPFFS((mode >> 4) & 0x07); - if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX - } else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD); - GPES = (1 << pin); //Enable - } else if(mode == INPUT || mode == INPUT_PULLUP){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPEC = (1 << pin); //Disable - GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) - if(mode == INPUT_PULLUP) { - GPF(pin) |= (1 << GPFPU); // Enable Pullup - } - } else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){ - GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPEC = (1 << pin); //Disable - if(mode == WAKEUP_PULLUP) { - GPF(pin) |= (1 << GPFPU); // Enable Pullup - GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) - } else { - GPF(pin) |= (1 << GPFPD); // Enable Pulldown - GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) - } + extern void __pinMode(uint8_t pin, uint8_t mode) { + if (pin < 16) { + if (mode == SPECIAL) { + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) + if (pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX + } + else if (mode & FUNCTION_0) { + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS((mode >> 4) & 0x07); + if (pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX + } + else if (mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN) { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if (mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD); + GPES = (1 << pin); //Enable + } + else if (mode == INPUT || mode == INPUT_PULLUP) { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if (mode == INPUT_PULLUP) { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + } + } + else if (mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN) { + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + if (mode == WAKEUP_PULLUP) { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) + } + else { + GPF(pin) |= (1 << GPFPD); // Enable Pulldown + GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) + } + } + } + else if (pin == 16) { + GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC16 = 0; + if (mode == INPUT || mode == INPUT_PULLDOWN_16) { + if (mode == INPUT_PULLDOWN_16) { + GPF16 |= (1 << GP16FPD);//Enable Pulldown + } + GP16E &= ~1; + } + else if (mode == OUTPUT) { + GP16E |= 1; + } + } } - } else if(pin == 16){ - GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO - GPC16 = 0; - if(mode == INPUT || mode == INPUT_PULLDOWN_16){ - if(mode == INPUT_PULLDOWN_16){ - GPF16 |= (1 << GP16FPD);//Enable Pulldown - } - GP16E &= ~1; - } else if(mode == OUTPUT){ - GP16E |= 1; + + extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { + stopWaveform(pin); // Disable any Tone or startWaveform on this pin + _stopPWM(pin); // and any analogWrites (PWM) + if (pin < 16) { + if (val) GPOS = (1 << pin); + else GPOC = (1 << pin); + } + else if (pin == 16) { + if (val) GP16O |= 1; + else GP16O &= ~1; + } } - } -} -extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { - stopWaveform(pin); // Disable any Tone or startWaveform on this pin - _stopPWM(pin); // and any analogWrites (PWM) - if(pin < 16){ - if(val) GPOS = (1 << pin); - else GPOC = (1 << pin); - } else if(pin == 16){ - if(val) GP16O |= 1; - else GP16O &= ~1; - } -} + extern int IRAM_ATTR __digitalRead(uint8_t pin) { + if (pin < 16) { + return GPIP(pin); + } + else if (pin == 16) { + return GP16I & 0x01; + } + return 0; + } -extern int IRAM_ATTR __digitalRead(uint8_t pin) { - if(pin < 16){ - return GPIP(pin); - } else if(pin == 16){ - return GP16I & 0x01; - } - return 0; -} + /* + GPIO INTERRUPTS + */ -/* - GPIO INTERRUPTS -*/ + typedef void (*voidFuncPtr)(void); + typedef void (*voidFuncPtrArg)(void*); -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); + namespace + { + struct interrupt_handler_t { + interrupt_handler_t() + { + mode = 0; + } + ~interrupt_handler_t() + { + } + uint8_t mode; + Delegate userFunc; + }; -typedef struct { - uint8_t mode; - voidFuncPtr fn; - void * arg; - bool functional; -} interrupt_handler_t; + static interrupt_handler_t interrupt_handlers[16]; + static uint32_t interrupt_reg = 0; -//duplicate from functionalInterrupt.h keep in sync -typedef struct InterruptInfo { - uint8_t pin; - uint8_t value; - uint32_t micro; -} InterruptInfo; + inline void isr_iram_assertion(voidFuncPtrArg userFunc) + { + // #5780 + // https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map + if ((uint32_t)userFunc >= 0x40200000) + { + // ISR not in IRAM + ::printf((PGM_P)F("ISR not in IRAM!\r\n")); + abort(); + } + } -typedef struct { - InterruptInfo* interruptInfo; - void* functionInfo; -} ArgStructure; + void set_interrupt_handlers(uint8_t pin, Delegate&& userFunc, uint8_t mode) + { + interrupt_handler_t& handler = interrupt_handlers[pin]; + handler.userFunc = std::move(userFunc); + if (handler.userFunc) + handler.mode = mode; + } -static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, }; -static uint32_t interrupt_reg = 0; + void IRAM_ATTR interrupt_handler(void *arg, void *frame); -void IRAM_ATTR interrupt_handler(void *arg, void *frame) -{ - (void) arg; - (void) frame; - uint32_t status = GPIE; - GPIEC = status;//clear them interrupts - uint32_t levels = GPI; - if(status == 0 || interrupt_reg == 0) return; - ETS_GPIO_INTR_DISABLE(); - int i = 0; - uint32_t changedbits = status & interrupt_reg; - while(changedbits){ - while(!(changedbits & (1 << i))) i++; - changedbits &= ~(1 << i); - interrupt_handler_t *handler = &interrupt_handlers[i]; - if (handler->fn && - (handler->mode == CHANGE || - (handler->mode & 1) == !!(levels & (1 << i)))) { - // to make ISR compatible to Arduino AVR model where interrupts are disabled - // we disable them before we call the client ISR - esp8266::InterruptLock irqLock; // stop other interrupts - if (handler->functional) - { - ArgStructure* localArg = (ArgStructure*)handler->arg; - if (localArg && localArg->interruptInfo) - { - localArg->interruptInfo->pin = i; - localArg->interruptInfo->value = __digitalRead(i); - localArg->interruptInfo->micro = micros(); - } - } - if (handler->arg) - { - ((voidFuncPtrArg)handler->fn)(handler->arg); - } - else - { - handler->fn(); - } - } - } - ETS_GPIO_INTR_ENABLE(); -} + void set_interrupt_reg(uint8_t pin, int mode) + { + interrupt_reg |= (1 << pin); + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" + ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); + } -extern void cleanupFunctional(void* arg); + } -static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) -{ - interrupt_handler_t* handler = &interrupt_handlers[pin]; - handler->mode = mode; - handler->fn = userFunc; - if (handler->functional && handler->arg) // Clean when new attach without detach - { - cleanupFunctional(handler->arg); - } - handler->arg = arg; - handler->functional = functional; -} + extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg const userFunc, void* const arg, int mode) + { + isr_iram_assertion(userFunc); + attachInterrupt(pin, { userFunc, arg }, mode); + } -extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode, bool functional) -{ - // #5780 - // https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map - if ((uint32_t)userFunc >= 0x40200000) - { - // ISR not in IRAM - ::printf((PGM_P)F("ISR not in IRAM!\r\n")); - abort(); - } + extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) + { + attachInterruptArg(pin, reinterpret_cast(userFunc), nullptr, mode); + } - if(pin < 16) { - ETS_GPIO_INTR_DISABLE(); - set_interrupt_handlers(pin, (voidFuncPtr)userFunc, arg, mode, functional); - interrupt_reg |= (1 << pin); - GPC(pin) &= ~(0xF << GPCI);//INT mode disabled - GPIEC = (1 << pin); //Clear Interrupt for this pin - GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" - ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); - ETS_GPIO_INTR_ENABLE(); - } -} + extern void IRAM_ATTR __detachInterrupt(uint8_t pin) + { + if (pin < 16) + { + ETS_GPIO_INTR_DISABLE(); + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + interrupt_reg &= ~(1 << pin); + interrupt_handler_t& handler = interrupt_handlers[pin]; + handler.userFunc = nullptr; + handler.mode = 0; + if (interrupt_reg) + { + ETS_GPIO_INTR_ENABLE(); + } + } + } -extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode) -{ - __attachInterruptFunctionalArg(pin, userFunc, arg, mode, false); -} + extern void __resetPins() { + for (int i = 0; i <= 16; ++i) { + if (!isFlashInterfacePin(i)) { + pinMode(i, INPUT); + } + } + } + + extern void initPins() { + //Disable UART interrupts + system_set_os_print(0); + U0IE = 0; + U1IE = 0; + + resetPins(); + } + + extern void resetPins() __attribute__ ((weak, alias("__resetPins"))); + extern void pinMode(uint8_t pin, uint8_t mode) __attribute__((weak, alias("__pinMode"))); + extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__((weak, alias("__digitalWrite"))); + extern int digitalRead(uint8_t pin) __attribute__((weak, alias("__digitalRead"), nothrow)); + extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__((weak, alias("__attachInterrupt"))); + extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg"))); + extern void detachInterrupt(uint8_t pin) __attribute__((weak, alias("__detachInterrupt"))); + +}; -extern void IRAM_ATTR __detachInterrupt(uint8_t pin) { +extern void attachInterrupt(uint8_t pin, Delegate userFunc, int mode) +{ + // isr_iram_assertion cannot inspect std::function objects; Delegate hides if its a std::function or C-style ptr if (pin < 16) { ETS_GPIO_INTR_DISABLE(); - GPC(pin) &= ~(0xF << GPCI);//INT mode disabled - GPIEC = (1 << pin); //Clear Interrupt for this pin - interrupt_reg &= ~(1 << pin); - set_interrupt_handlers(pin, nullptr, nullptr, 0, false); - if (interrupt_reg) - { - ETS_GPIO_INTR_ENABLE(); - } + set_interrupt_handlers(pin, std::move(userFunc), mode); + set_interrupt_reg(pin, mode); + ETS_GPIO_INTR_ENABLE(); } } -extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) -{ - __attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, 0, mode, false); -} +// Speed critical bits +#pragma GCC optimize ("O2") -extern void __resetPins() { - for (int i = 0; i <= 16; ++i) { - if (!isFlashInterfacePin(i)) - pinMode(i, INPUT); - } -} +namespace +{ -extern void initPins() { - //Disable UART interrupts - system_set_os_print(0); - U0IE = 0; - U1IE = 0; + void IRAM_ATTR interrupt_handler(void *arg, void *frame) + { + (void) arg; + (void) frame; + uint32_t status = GPIE; + GPIEC = status;//clear them interrupts + uint32_t levels = GPI; + if (status == 0 || interrupt_reg == 0) return; + ETS_GPIO_INTR_DISABLE(); + uint32_t i = 0; + uint32_t changedbits = status & interrupt_reg; + while (changedbits >= (1UL << i)) { + while (!(changedbits & (1UL << i))) + { + ++i; + } + const interrupt_handler_t& handler = interrupt_handlers[i]; + if (handler.mode == CHANGE || + (handler.mode & 1) == static_cast(levels & (1UL << i))) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + esp8266::InterruptLock irqLock; // stop other interrupts + handler.userFunc(); + } + ++i; + } + ETS_GPIO_INTR_ENABLE(); + } - resetPins(); } - -extern void resetPins() __attribute__ ((weak, alias("__resetPins"))); -extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); -extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); -extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"), nothrow)); -extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); -extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg"))); -extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); - -}; diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino new file mode 100644 index 0000000000..1fa856aead --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -0,0 +1,74 @@ +// Hint: On ESP8266, this no longer depends on FunctionalInterrupt, but is rather a feature of +// the standard API in Arduino.h + +//#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(uint8_t reqPin) : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + attachInterrupt(PIN, { buttonIsr_static, this }, FALLING); + }; + ~Button() { + detachInterrupt(PIN); + } + + void IRAM_ATTR buttonIsr() { + numberKeyPresses += 1; + pressed = true; + } + + static void IRAM_ATTR buttonIsr_static(void* const self) { + static_cast(self)->buttonIsr(); + } + + uint32_t checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; + } + return numberKeyPresses; + } + + private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; +}; + +Button* button1; +Button* button2; + + +void setup() { + Serial.begin(115200); + Serial.println("FunctionalInterrupt test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->checkPressed(); + if (nullptr != button2 && 10 < button2->checkPressed()) { + delete button2; + button2 = nullptr; + } +} diff --git a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino new file mode 100644 index 0000000000..4d42a741a7 --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -0,0 +1,69 @@ +#include + +#ifndef IRAM_ATTR +#define IRAM_ATTR ICACHE_RAM_ATTR +#endif + +#if defined(ESP32) +#define BUTTON1 16 +#define BUTTON2 17 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define BUTTON1 D4 +#define BUTTON2 D3 +#else +#define BUTTON1 2 +#define BUTTON2 0 +#endif + +class Button { + public: + Button(uint8_t reqPin) : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + attachScheduledInterrupt(PIN, [this](const InterruptInfo & ii) { + Serial.print("Pin "); + Serial.println(ii.pin); + numberKeyPresses += 1; + pressed = true; + }, FALLING); // works on ESP8266 + }; + ~Button() { + detachInterrupt(PIN); + } + + uint32_t checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; + } + return numberKeyPresses; + } + + private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; +}; + +Button* button1; +Button* button2; + + +void setup() { + Serial.begin(115200); + while (!Serial) {} + delay(500); + Serial.println("FunctionalInterrupt test/example"); + + button1 = new Button(BUTTON1); + button2 = new Button(BUTTON2); + + Serial.println("setup() complete"); +} + +void loop() { + button1->checkPressed(); + if (nullptr != button2 && 10 < button2->checkPressed()) { + delete button2; + button2 = nullptr; + } +} diff --git a/libraries/FunctionalInterrupt/keywords.txt b/libraries/FunctionalInterrupt/keywords.txt new file mode 100644 index 0000000000..babcb63442 --- /dev/null +++ b/libraries/FunctionalInterrupt/keywords.txt @@ -0,0 +1,11 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +InterruptInfo KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +attachScheduledInterrupt KEYWORD2 diff --git a/libraries/FunctionalInterrupt/library.properties b/libraries/FunctionalInterrupt/library.properties new file mode 100644 index 0000000000..9738f1e971 --- /dev/null +++ b/libraries/FunctionalInterrupt/library.properties @@ -0,0 +1,10 @@ +name=FunctionalInterrupt +version=1.0 +author=hreintke +maintainer=hreintke +sentence=C++ functional and scheduled interrupt handling +paragraph= +category=Other +url=https://github.com/esp8266/Arduino +architectures=esp8266 +dot_a_linkage=true diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp new file mode 100644 index 0000000000..72d497fff6 --- /dev/null +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp @@ -0,0 +1,36 @@ +#include "FunctionalInterrupt.h" +#include +#include + +namespace +{ + + struct InterruptScheduleFunctionalArg + { + uint8_t pin; + Delegate scheduledIntRoutine; + }; + struct ScheduleLambdaArg + { + Delegate scheduledIntRoutine; + InterruptInfo interruptInfo; + }; + + void ICACHE_RAM_ATTR interruptScheduleFunctional(const InterruptScheduleFunctionalArg& arg) + { + ScheduleLambdaArg lambdaArg{ arg.scheduledIntRoutine, { arg.pin } }; + lambdaArg.interruptInfo.value = digitalRead(arg.pin); + lambdaArg.interruptInfo.micro = micros(); + schedule_function(std::bind(lambdaArg.scheduledIntRoutine, lambdaArg.interruptInfo)); + } + +} + +void attachScheduledInterrupt(uint8_t pin, const Delegate& scheduledIntRoutine, int mode) +{ + if (scheduledIntRoutine) + { + InterruptScheduleFunctionalArg arg{ pin, scheduledIntRoutine }; + attachInterrupt(pin, std::bind(interruptScheduleFunctional, arg), mode); + } +} diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h new file mode 100644 index 0000000000..c38482736a --- /dev/null +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h @@ -0,0 +1,18 @@ +#ifndef FUNCTIONALINTERRUPT_H +#define FUNCTIONALINTERRUPT_H + +#include + +// Structures for communication + +struct InterruptInfo +{ + InterruptInfo(uint8_t _pin) : pin(_pin) {} + const uint8_t pin; + uint8_t value = 0; + uint32_t micro = 0; +}; + +void attachScheduledInterrupt(uint8_t pin, const Delegate& scheduledIntRoutine, int mode); + +#endif //FUNCTIONALINTERRUPT_H From 0a1180d1ef6560a9201e6dad30958f98b36a94b1 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Mon, 15 Mar 2021 10:13:49 +0100 Subject: [PATCH 2/5] Fix for 656a33e6f8 --- .../FunctionalInterrupt/examples/Functional/Functional.ino | 4 ---- .../examples/ScheduledFunctional/ScheduledFunctional.ino | 4 ---- libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino index 1fa856aead..764d384271 100644 --- a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -3,10 +3,6 @@ //#include -#ifndef IRAM_ATTR -#define IRAM_ATTR ICACHE_RAM_ATTR -#endif - #if defined(ESP32) #define BUTTON1 16 #define BUTTON2 17 diff --git a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino index 4d42a741a7..ba4d4a5dc5 100644 --- a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino +++ b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -1,9 +1,5 @@ #include -#ifndef IRAM_ATTR -#define IRAM_ATTR ICACHE_RAM_ATTR -#endif - #if defined(ESP32) #define BUTTON1 16 #define BUTTON2 17 diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp index 72d497fff6..5371ae7c0e 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp @@ -16,7 +16,7 @@ namespace InterruptInfo interruptInfo; }; - void ICACHE_RAM_ATTR interruptScheduleFunctional(const InterruptScheduleFunctionalArg& arg) + void IRAM_ATTR interruptScheduleFunctional(const InterruptScheduleFunctionalArg& arg) { ScheduleLambdaArg lambdaArg{ arg.scheduledIntRoutine, { arg.pin } }; lambdaArg.interruptInfo.value = digitalRead(arg.pin); From cf515f310ca627475d14bc78113a0096b7253ca6 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sat, 3 Apr 2021 11:18:17 +0200 Subject: [PATCH 3/5] revert from Delegate to std::function to make it easier to accept PR --- cores/esp8266/Arduino.h | 3 +-- cores/esp8266/core_esp8266_wiring_digital.cpp | 10 +++++----- .../examples/Functional/Functional.ino | 2 +- .../FunctionalInterrupt/src/FunctionalInterrupt.cpp | 6 +++--- .../FunctionalInterrupt/src/FunctionalInterrupt.h | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 0465779d2d..bcaa3ebfd2 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -229,7 +229,6 @@ void optimistic_yield(uint32_t interval_us); #include #include #include -#include "Delegate.h" #include "mmu_iram.h" @@ -276,7 +275,7 @@ void setTZ(const char* tz); void configTime(const char* tz, const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); -void attachInterrupt(uint8_t pin, Delegate userFunc, int mode); +void attachInterrupt(uint8_t pin, std::function userFunc, int mode); // configures with approximated TZ value. part of the old api, prefer configTime with TZ variable void configTime(int timezone, int daylightOffset_sec, const char* server1, diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 9232f178ce..102609af57 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -129,7 +129,7 @@ extern "C" { { } uint8_t mode; - Delegate userFunc; + std::function userFunc; }; static interrupt_handler_t interrupt_handlers[16]; @@ -147,7 +147,7 @@ extern "C" { } } - void set_interrupt_handlers(uint8_t pin, Delegate&& userFunc, uint8_t mode) + void set_interrupt_handlers(uint8_t pin, std::function&& userFunc, uint8_t mode) { interrupt_handler_t& handler = interrupt_handlers[pin]; handler.userFunc = std::move(userFunc); @@ -171,7 +171,7 @@ extern "C" { extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg const userFunc, void* const arg, int mode) { isr_iram_assertion(userFunc); - attachInterrupt(pin, { userFunc, arg }, mode); + attachInterrupt(pin, std::bind(userFunc, arg), mode); } extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) @@ -224,9 +224,9 @@ extern "C" { }; -extern void attachInterrupt(uint8_t pin, Delegate userFunc, int mode) +extern void attachInterrupt(uint8_t pin, std::function userFunc, int mode) { - // isr_iram_assertion cannot inspect std::function objects; Delegate hides if its a std::function or C-style ptr + // isr_iram_assertion cannot inspect std::function objects if (pin < 16) { ETS_GPIO_INTR_DISABLE(); diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino index 764d384271..7fa47f5198 100644 --- a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -18,7 +18,7 @@ class Button { public: Button(uint8_t reqPin) : PIN(reqPin) { pinMode(PIN, INPUT_PULLUP); - attachInterrupt(PIN, { buttonIsr_static, this }, FALLING); + attachInterrupt(PIN, std::bind(buttonIsr_static, this), FALLING); }; ~Button() { detachInterrupt(PIN); diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp index 5371ae7c0e..030c5b171f 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp @@ -8,11 +8,11 @@ namespace struct InterruptScheduleFunctionalArg { uint8_t pin; - Delegate scheduledIntRoutine; + std::function scheduledIntRoutine; }; struct ScheduleLambdaArg { - Delegate scheduledIntRoutine; + std::function scheduledIntRoutine; InterruptInfo interruptInfo; }; @@ -26,7 +26,7 @@ namespace } -void attachScheduledInterrupt(uint8_t pin, const Delegate& scheduledIntRoutine, int mode) +void attachScheduledInterrupt(uint8_t pin, const std::function& scheduledIntRoutine, int mode) { if (scheduledIntRoutine) { diff --git a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h index c38482736a..ca1d0e8b0c 100644 --- a/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.h @@ -1,7 +1,7 @@ #ifndef FUNCTIONALINTERRUPT_H #define FUNCTIONALINTERRUPT_H -#include +#include // Structures for communication @@ -13,6 +13,6 @@ struct InterruptInfo uint32_t micro = 0; }; -void attachScheduledInterrupt(uint8_t pin, const Delegate& scheduledIntRoutine, int mode); +void attachScheduledInterrupt(uint8_t pin, const std::function& scheduledIntRoutine, int mode); #endif //FUNCTIONALINTERRUPT_H From a65f9a28a2f146d0b3414ba2aa4c6d74a137b0e7 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Sun, 13 Mar 2022 20:28:16 +0100 Subject: [PATCH 4/5] Apply suggested style fixes. --- .../examples/Functional/Functional.ino | 51 ++++++++++--------- .../ScheduledFunctional.ino | 41 ++++++++------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino index 7fa47f5198..e5eff172e9 100644 --- a/libraries/FunctionalInterrupt/examples/Functional/Functional.ino +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -15,36 +15,37 @@ #endif class Button { - public: - Button(uint8_t reqPin) : PIN(reqPin) { - pinMode(PIN, INPUT_PULLUP); - attachInterrupt(PIN, std::bind(buttonIsr_static, this), FALLING); - }; - ~Button() { - detachInterrupt(PIN); - } +public: + Button(uint8_t reqPin) + : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + attachInterrupt(PIN, std::bind(buttonIsr_static, this), FALLING); + }; + ~Button() { + detachInterrupt(PIN); + } - void IRAM_ATTR buttonIsr() { - numberKeyPresses += 1; - pressed = true; - } + void IRAM_ATTR buttonIsr() { + numberKeyPresses += 1; + pressed = true; + } - static void IRAM_ATTR buttonIsr_static(void* const self) { - static_cast(self)->buttonIsr(); - } + static void IRAM_ATTR buttonIsr_static(void* const self) { + static_cast(self)->buttonIsr(); + } - uint32_t checkPressed() { - if (pressed) { - Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); - pressed = false; - } - return numberKeyPresses; + uint32_t checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; } + return numberKeyPresses; + } - private: - const uint8_t PIN; - volatile uint32_t numberKeyPresses = 0; - volatile bool pressed = false; +private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; }; Button* button1; diff --git a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino index ba4d4a5dc5..fc769b2027 100644 --- a/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino +++ b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -12,32 +12,35 @@ #endif class Button { - public: - Button(uint8_t reqPin) : PIN(reqPin) { - pinMode(PIN, INPUT_PULLUP); - attachScheduledInterrupt(PIN, [this](const InterruptInfo & ii) { +public: + Button(uint8_t reqPin) + : PIN(reqPin) { + pinMode(PIN, INPUT_PULLUP); + attachScheduledInterrupt( + PIN, [this](const InterruptInfo& ii) { Serial.print("Pin "); Serial.println(ii.pin); numberKeyPresses += 1; pressed = true; - }, FALLING); // works on ESP8266 - }; - ~Button() { - detachInterrupt(PIN); - } + }, + FALLING); // works on ESP8266 + }; + ~Button() { + detachInterrupt(PIN); + } - uint32_t checkPressed() { - if (pressed) { - Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); - pressed = false; - } - return numberKeyPresses; + uint32_t checkPressed() { + if (pressed) { + Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses); + pressed = false; } + return numberKeyPresses; + } - private: - const uint8_t PIN; - volatile uint32_t numberKeyPresses = 0; - volatile bool pressed = false; +private: + const uint8_t PIN; + volatile uint32_t numberKeyPresses = 0; + volatile bool pressed = false; }; Button* button1; From d01cab2176211e6703c962f93e333bdcdcaabf6b Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Tue, 14 Mar 2023 08:30:19 +0100 Subject: [PATCH 5/5] attaching a nullptr should also reliably remove the IRQ mode. --- cores/esp8266/core_esp8266_wiring_digital.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 102609af57..613fc2e027 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -151,8 +151,7 @@ extern "C" { { interrupt_handler_t& handler = interrupt_handlers[pin]; handler.userFunc = std::move(userFunc); - if (handler.userFunc) - handler.mode = mode; + handler.mode = handler.userFunc ? mode : 0; } void IRAM_ATTR interrupt_handler(void *arg, void *frame);