From 641c5cdc61e5967c682a24e2df1c3b1765d60dab Mon Sep 17 00:00:00 2001 From: hreintke Date: Tue, 19 Jun 2018 23:26:57 +0200 Subject: [PATCH] Scheduled Interrupt (#4609) * Scheduled Interrupt * use capital letter for Schedule.h * Prevent memory leak when attach is called multiple times without detach * Add improved schedule_function * WIP : Integrate FunctionalInterrupt & ScheduledInterrupt * Fix travis error --- cores/esp8266/FunctionalInterrupt.cpp | 60 ++++++++-- cores/esp8266/FunctionalInterrupt.h | 22 ++++ cores/esp8266/ScheduledFunctions.cpp | 117 ++++++++++++++++++++ cores/esp8266/ScheduledFunctions.h | 51 +++++++++ cores/esp8266/core_esp8266_wiring_digital.c | 30 ++++- 5 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 cores/esp8266/ScheduledFunctions.cpp create mode 100644 cores/esp8266/ScheduledFunctions.h diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index b7c7dd1434..84ae8f4289 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -1,24 +1,70 @@ #include - +#include +#include "Arduino.h" +#include // 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 ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp , int mode); -// Structure for communication -struct ArgStructure { - std::function reqFunction; -}; void interruptFunctional(void* arg) { - ((ArgStructure*)arg)->reqFunction(); + ArgStructure* localArg = (ArgStructure*)arg; + if (localArg->functionInfo->reqScheduledFunction) + { + scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true); + } + 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 - __attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, new ArgStructure{intRoutine}, mode); + + InterruptInfo* ii = nullptr; + + FunctionInfo* fi = new FunctionInfo; + fi->reqFunction = intRoutine; + + ArgStructure* as = new ArgStructure; + as->interruptInfo = ii; + as->functionInfo = fi; + + __attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode); +} + +void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) +{ + if (!scheduledInterrupts) + { + scheduledInterrupts = new ScheduledFunctions(32); + } + InterruptInfo* ii = new InterruptInfo; + + FunctionInfo* fi = new FunctionInfo; + fi->reqScheduledFunction = scheduledIntRoutine; + + ArgStructure* as = new ArgStructure; + as->interruptInfo = ii; + as->functionInfo = fi; + + __attachInterruptArg (pin, (voidFuncPtr)interruptFunctional, as, mode); } diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index fedc20ad68..a6d53188ae 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -4,12 +4,34 @@ #include #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; +}; + +static ScheduledFunctions* scheduledInterrupts; 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/ScheduledFunctions.cpp b/cores/esp8266/ScheduledFunctions.cpp new file mode 100644 index 0000000000..25bc58db61 --- /dev/null +++ b/cores/esp8266/ScheduledFunctions.cpp @@ -0,0 +1,117 @@ +/* + * ScheduledFunctions.cpp + * + * Created on: 27 apr. 2018 + * Author: Herman + */ +#include "ScheduledFunctions.h" + +std::list ScheduledFunctions::scheduledFunctions; + +ScheduledFunctions::ScheduledFunctions() +:ScheduledFunctions(UINT_MAX) +{ +} + +ScheduledFunctions::ScheduledFunctions(unsigned int reqMax) +{ + maxElements = reqMax; +} + +ScheduledFunctions::~ScheduledFunctions() { +} + +ScheduledRegistration ScheduledFunctions::insertElement(ScheduledElement se, bool front) +{ + if (countElements >= maxElements) + { + return nullptr; + } + else + { + countElements++; + if (front) + { + scheduledFunctions.push_front(se); + return scheduledFunctions.begin()->registration; + } + else + { + scheduledFunctions.push_back(se); + return scheduledFunctions.rbegin()->registration; + } + } +} + +std::list::iterator ScheduledFunctions::eraseElement(std::list::iterator it) +{ + countElements--; + return scheduledFunctions.erase(it); +} + +bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf, bool continuous, bool front) +{ + return (insertElement({this,continuous,nullptr,sf}, front) == nullptr); +} + +bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf) +{ + return scheduleFunction(sf, false, false); +} + +ScheduledRegistration ScheduledFunctions::scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front) +{ + return insertElement({this,continuous,std::make_shared(1),sf},front); +} + +void ScheduledFunctions::runScheduledFunctions() +{ + auto lastElement = scheduledFunctions.end(); // do not execute elements added during runScheduledFunctions + auto it = scheduledFunctions.begin(); + while (it != lastElement) + { + bool erase = false; + if (it->registration == nullptr) + { + it->function(); + } + else + { + if (it->registration.use_count() > 1) + { + it->function(); + } + else + { + erase = true; + } + } + if ((!it->continuous) || (erase)) + { + it = it->_this->eraseElement(it); + } + else + { + it++; + } + } +} + +void ScheduledFunctions::removeFunction(ScheduledRegistration sr) +{ + auto it = scheduledFunctions.begin(); + bool removed = false; + while ((!removed) && (it != scheduledFunctions.end())) + { + if (it->registration == sr) + { + it = eraseElement(it); + removed = true; + } + else + { + it++; + } + } +} + diff --git a/cores/esp8266/ScheduledFunctions.h b/cores/esp8266/ScheduledFunctions.h new file mode 100644 index 0000000000..0129635364 --- /dev/null +++ b/cores/esp8266/ScheduledFunctions.h @@ -0,0 +1,51 @@ +/* + * ScheduledFunctions.h + * + * Created on: 27 apr. 2018 + * Author: Herman + */ +#include "Arduino.h" +#include "Schedule.h" + +#include +#include +#include +#include + +#ifndef SCHEDULEDFUNCTIONS_H_ +#define SCHEDULEDFUNCTIONS_H_ + +typedef std::function ScheduledFunction; +typedef std::shared_ptr ScheduledRegistration; + +class ScheduledFunctions { + +public: + ScheduledFunctions(); + ScheduledFunctions(unsigned int reqMax); + virtual ~ScheduledFunctions(); + + struct ScheduledElement + { + ScheduledFunctions* _this; + bool continuous; + ScheduledRegistration registration; + ScheduledFunction function; + }; + + ScheduledRegistration insertElement(ScheduledElement se, bool front); + std::list::iterator eraseElement(std::list::iterator); + bool scheduleFunction(ScheduledFunction sf, bool continuous, bool front); + bool scheduleFunction(ScheduledFunction sf); + ScheduledRegistration scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front); + static void runScheduledFunctions(); + void removeFunction(ScheduledRegistration sr); + + + static std::list scheduledFunctions; + unsigned int maxElements; + unsigned int countElements = 0; + +}; + +#endif /* SCHEDULEDFUNCTIONS_H_ */ diff --git a/cores/esp8266/core_esp8266_wiring_digital.c b/cores/esp8266/core_esp8266_wiring_digital.c index 4ee398d615..68b8055f00 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.c +++ b/cores/esp8266/core_esp8266_wiring_digital.c @@ -112,6 +112,17 @@ typedef struct { void * arg; } interrupt_handler_t; +//duplicate from functionalInterrupt.h keep in sync +typedef struct InterruptInfo { + uint8_t pin; + uint8_t value; + uint32_t micro; +} InterruptInfo; + +typedef struct { + InterruptInfo* interruptInfo; + void* functionInfo; +} ArgStructure; static interrupt_handler_t interrupt_handlers[16]; static uint32_t interrupt_reg = 0; @@ -134,7 +145,14 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) { (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 - uint32_t savedPS = xt_rsil(15); // stop other interrupts + uint32_t savedPS = xt_rsil(15); // stop other interrupts + ArgStructure* localArg = (ArgStructure*)handler->arg; + if (localArg->interruptInfo) + { + localArg->interruptInfo->pin = i; + localArg->interruptInfo->value = __digitalRead(i); + localArg->interruptInfo->micro = micros(); + } if (handler->arg) { ((voidFuncPtrArg)handler->fn)(handler->arg); @@ -149,12 +167,18 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) { ETS_GPIO_INTR_ENABLE(); } +extern void cleanupFunctional(void* arg); + extern void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void *arg, int mode) { if(pin < 16) { ETS_GPIO_INTR_DISABLE(); interrupt_handler_t *handler = &interrupt_handlers[pin]; handler->mode = mode; handler->fn = userFunc; + if (handler->arg) // Clean when new attach without detach + { + cleanupFunctional(handler->arg); + } handler->arg = arg; interrupt_reg |= (1 << pin); GPC(pin) &= ~(0xF << GPCI);//INT mode disabled @@ -179,6 +203,10 @@ extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { interrupt_handler_t *handler = &interrupt_handlers[pin]; handler->mode = 0; handler->fn = 0; + if (handler->arg) + { + cleanupFunctional(handler->arg); + } handler->arg = 0; if (interrupt_reg) ETS_GPIO_INTR_ENABLE();