diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 60737e0195..bcaa3ebfd2 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -275,6 +275,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, 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, 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..613fc2e027 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -30,236 +30,244 @@ 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; + std::function 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, std::function&& userFunc, uint8_t mode) + { + interrupt_handler_t& handler = interrupt_handlers[pin]; + handler.userFunc = std::move(userFunc); + handler.mode = handler.userFunc ? mode : 0; + } -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, std::bind(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, std::function userFunc, int mode) +{ + // isr_iram_assertion cannot inspect std::function objects 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..e5eff172e9 --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/Functional/Functional.ino @@ -0,0 +1,71 @@ +// Hint: On ESP8266, this no longer depends on FunctionalInterrupt, but is rather a feature of +// the standard API in Arduino.h + +//#include + +#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, std::bind(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..fc769b2027 --- /dev/null +++ b/libraries/FunctionalInterrupt/examples/ScheduledFunctional/ScheduledFunctional.ino @@ -0,0 +1,68 @@ +#include + +#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..030c5b171f --- /dev/null +++ b/libraries/FunctionalInterrupt/src/FunctionalInterrupt.cpp @@ -0,0 +1,36 @@ +#include "FunctionalInterrupt.h" +#include +#include + +namespace +{ + + struct InterruptScheduleFunctionalArg + { + uint8_t pin; + std::function scheduledIntRoutine; + }; + struct ScheduleLambdaArg + { + std::function scheduledIntRoutine; + InterruptInfo interruptInfo; + }; + + void IRAM_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 std::function& 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..ca1d0e8b0c --- /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 std::function& scheduledIntRoutine, int mode); + +#endif //FUNCTIONALINTERRUPT_H