diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index f468595787..cba4d0e0a7 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -1,65 +1,109 @@ -#include -#include +// core_esp8266_wiring_digital helper for std::function<...> dispatch + #include "Arduino.h" +#include "Schedule.h" + +#include "FunctionalInterrupt.h" + +extern "C" { +#include "c_types.h" +#include "ets_sys.h" +} + +namespace esp8266::FunctionalInterrupt { +namespace { -// Duplicate typedefs from core_esp8266_wiring_digital_c -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); +struct FunctionInfo { + Function reqFunction; + ScheduledFunction reqScheduledFunction; +}; -// Helper functions for Functional interrupt routines -extern "C" void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode, bool functional); +struct ArgStructure { + InterruptInfo* interruptInfo; + FunctionInfo* functionInfo; +}; +void IRAM_ATTR updateArg(void* varg, uint8_t i, uint8_t value, uint32_t micro) { + ArgStructure* arg = reinterpret_cast(varg); + if (arg && arg->interruptInfo) + { + arg->interruptInfo->pin = i; + arg->interruptInfo->value = value; + arg->interruptInfo->micro = micro; + } +} -void IRAM_ATTR interruptFunctional(void* arg) +void IRAM_ATTR dispatchFunction(void* varg) { - 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(); - } + ArgStructure* arg = reinterpret_cast(varg); + arg->functionInfo->reqFunction(Tag{}); } -extern "C" +void IRAM_ATTR dispatchScheduled(void* varg) { - void cleanupFunctional(void* arg) - { - ArgStructure* localArg = (ArgStructure*)arg; - delete (FunctionInfo*)localArg->functionInfo; - delete (InterruptInfo*)localArg->interruptInfo; - delete localArg; - } + ArgStructure* arg = reinterpret_cast(varg); + schedule_function(std::bind(arg->functionInfo->reqScheduledFunction, InterruptInfo(*(arg->interruptInfo)))); +} + } -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) +// see core_esp8266_wiring_digital.cpp, these are referenced there +void IRAM_ATTR cleanup(void* varg) { - // use the local interrupt routine which takes the ArgStructure as argument + ArgStructure* arg = reinterpret_cast(varg); + delete (FunctionInfo*)arg->functionInfo; + delete (InterruptInfo*)arg->interruptInfo; + delete arg; +} + +// these would be used by the the local interrupt routine which takes the ArgStructure as argument +void prepareFunction(void** funcPtr, void** argPtr, Function intRoutine) +{ InterruptInfo* ii = nullptr; FunctionInfo* fi = new FunctionInfo; - fi->reqFunction = intRoutine; + fi->reqFunction = std::move(intRoutine); ArgStructure* as = new ArgStructure; as->interruptInfo = ii; as->functionInfo = fi; - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); + (*funcPtr) = (void*)&dispatchFunction; + (*argPtr) = as; } -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) +void prepareScheduledFunction(void** funcPtr, void** argPtr, ScheduledFunction intRoutine) { InterruptInfo* ii = new InterruptInfo; FunctionInfo* fi = new FunctionInfo; - fi->reqScheduledFunction = scheduledIntRoutine; + fi->reqScheduledFunction = std::move(intRoutine); ArgStructure* as = new ArgStructure; as->interruptInfo = ii; as->functionInfo = fi; - __attachInterruptFunctionalArg(pin, (voidFuncPtr)interruptFunctional, as, mode, true); + (*funcPtr) = (void*)&dispatchScheduled; + (*argPtr) = as; +} + +} // namespace esp8266::FunctionalInterrupt + +extern "C" void __attachInterruptFunctionalArg(uint8_t, void (*)(void*), void*, int, bool functional); + +void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function intRoutine, int mode) +{ + void* func; + void* arg; + esp8266::FunctionalInterrupt::prepareFunction(&func, &arg, std::move(intRoutine)); + __attachInterruptFunctionalArg(pin, reinterpret_cast(func), arg, mode, true); +} + +void attachScheduledInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::ScheduledFunction intRoutine, int mode) +{ + void* func; + void* arg; + esp8266::FunctionalInterrupt::prepareScheduledFunction(&func, &arg, std::move(intRoutine)); + __attachInterruptFunctionalArg(pin, reinterpret_cast(func), arg, mode, true); } diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index 968793e499..5bd1fa4673 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -1,35 +1,34 @@ -#ifndef FUNCTIONALINTERRUPT_H -#define FUNCTIONALINTERRUPT_H +// core_esp8266_wiring_digital helper for std::function<...> dispatch -#include -#include +#pragma once + +#include +#include #include -extern "C" { -#include "c_types.h" -#include "ets_sys.h" -} +namespace esp8266::FunctionalInterrupt { -// Structures for communication +using PlainFunction = std::function; -struct InterruptInfo { - uint8_t pin = 0; - uint8_t value = 0; - uint32_t micro = 0; +struct Tag { }; -struct FunctionInfo { - std::function reqFunction = nullptr; - std::function reqScheduledFunction = nullptr; -}; +using Function = std::function; -struct ArgStructure { - InterruptInfo* interruptInfo = nullptr; - FunctionInfo* functionInfo = nullptr; +struct InterruptInfo { + uint8_t pin; + uint8_t value; + uint32_t micro; }; -void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); -void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); +using ScheduledFunction = std::function; + +} // namespace esp8266::FunctionalInterrupt +void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::Function, int mode); +void attachScheduledInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::ScheduledFunction, int mode); -#endif //INTERRUPTS_H +[[deprecated("3.2.x: use function type w/ ::Tag as argument")]] +inline void attachInterrupt(uint8_t pin, esp8266::FunctionalInterrupt::PlainFunction func, int mode) { + attachInterrupt(pin, [func = std::move(func)](auto) { func(); }, mode); +} diff --git a/cores/esp8266/abi.cpp b/cores/esp8266/abi.cpp index 72f3fb0a06..e6022d5abb 100644 --- a/cores/esp8266/abi.cpp +++ b/cores/esp8266/abi.cpp @@ -21,6 +21,8 @@ #include #include +#include + using __cxxabiv1::__guard; // Debugging helper, last allocation which returned NULL @@ -33,7 +35,7 @@ extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); #if !defined(__cpp_exceptions) -// overwrite weak operators new/new[] definitions +// overwrite weak operators new/new[] void* operator new(size_t size) { @@ -79,6 +81,22 @@ void* operator new[] (size_t size, const std::nothrow_t&) #endif // !defined(__cpp_exceptions) +// overwrite weak operators delete/sized delete/delete[] +// todo: current implementation simply links std::free +// todo: functionalinterrupt needs this to be in iram + +void IRAM_ATTR operator delete(void* ptr) noexcept +{ + free(ptr); +} + +void IRAM_ATTR operator delete(void* ptr, size_t) noexcept +{ + ::delete(ptr); +} + +// pure virtual functions + void __cxa_pure_virtual(void) { panic(); diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 6b42c3ee36..b13d34c2a7 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -28,6 +28,20 @@ #include "core_esp8266_waveform.h" #include "interrupts.h" +namespace esp8266::FunctionalInterrupt { + +// in-place stubs, in case functional interrupts arent used + +void updateArg(void*, uint8_t, uint8_t, uint32_t) __attribute__((weak)); +void IRAM_ATTR updateArg(void*, uint8_t, uint8_t, uint32_t) { +} + +void cleanup(void*) __attribute__((weak)); +void IRAM_ATTR cleanup(void*) { +} + +} // namespace esp8266::FunctionalInterrupt + 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 }; @@ -106,27 +120,15 @@ extern int IRAM_ATTR __digitalRead(uint8_t pin) { GPIO INTERRUPTS */ -typedef void (*voidFuncPtr)(void); -typedef void (*voidFuncPtrArg)(void*); +using voidFuncPtr = void(*)(); +using voidFuncPtrArg = void(*)(void*); -typedef struct { +struct interrupt_handler_t { uint8_t mode; voidFuncPtr fn; - void * arg; + void* arg; bool functional; -} 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] = { {0, 0, 0, 0}, }; static uint32_t interrupt_reg = 0; @@ -154,13 +156,8 @@ void IRAM_ATTR interrupt_handler(void *arg, void *frame) 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(); - } + esp8266::FunctionalInterrupt::updateArg( + handler->arg, i, __digitalRead(i), micros()); } if (handler->arg) { @@ -175,16 +172,14 @@ void IRAM_ATTR interrupt_handler(void *arg, void *frame) ETS_GPIO_INTR_ENABLE(); } -extern void cleanupFunctional(void* arg); - -static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional) +static void IRAM_ATTR 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); + esp8266::FunctionalInterrupt::cleanup(handler->arg); } handler->arg = arg; handler->functional = functional; @@ -225,7 +220,7 @@ extern void IRAM_ATTR __detachInterrupt(uint8_t pin) { 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); + set_interrupt_handlers(pin, nullptr, nullptr, 0, false); if (interrupt_reg) { ETS_GPIO_INTR_ENABLE(); @@ -256,10 +251,12 @@ extern void initPins() { 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 "C" diff --git a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino index 3b8bf0e01c..543e759007 100644 --- a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino +++ b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino @@ -13,6 +13,9 @@ the ESP8266 WILL FAIL to boot! See SPISlave_SafeMaster example for possible workaround + Warning! sketch unsafely executes SPISlave callbacks in ISR context + For real application, make sure to place these functions into IRAM + */ #include "SPISlave.h" diff --git a/libraries/SPISlave/src/SPISlave.cpp b/libraries/SPISlave/src/SPISlave.cpp index 3d58a86721..66609c4dae 100644 --- a/libraries/SPISlave/src/SPISlave.cpp +++ b/libraries/SPISlave/src/SPISlave.cpp @@ -90,6 +90,10 @@ void SPISlaveClass::setData(uint8_t * data, size_t len) } hspi_slave_setData(data, len); } +void SPISlaveClass::setData(const char* data) +{ + setData((uint8_t *)data, strlen(data)); +} void SPISlaveClass::setStatus(uint32_t status) { hspi_slave_setStatus(status); diff --git a/libraries/SPISlave/src/SPISlave.h b/libraries/SPISlave/src/SPISlave.h index 86b30875f2..3feb3e7870 100644 --- a/libraries/SPISlave/src/SPISlave.h +++ b/libraries/SPISlave/src/SPISlave.h @@ -21,20 +21,21 @@ #ifndef _SPISLAVE_H_INCLUDED #define _SPISLAVE_H_INCLUDED -#include "Arduino.h" -#include +#include +#include +#include -typedef std::function SpiSlaveDataHandler; -typedef std::function SpiSlaveStatusHandler; -typedef std::function SpiSlaveSentHandler; +using SpiSlaveDataHandler = void(*)(uint8_t *, size_t ); +using SpiSlaveStatusHandler = void(*)(uint32_t status); +using SpiSlaveSentHandler = void(*)(); class SPISlaveClass { protected: - SpiSlaveDataHandler _data_cb; - SpiSlaveStatusHandler _status_cb; - SpiSlaveSentHandler _data_sent_cb; - SpiSlaveSentHandler _status_sent_cb; + SpiSlaveDataHandler _data_cb = nullptr; + SpiSlaveStatusHandler _status_cb = nullptr; + SpiSlaveSentHandler _data_sent_cb = nullptr; + SpiSlaveSentHandler _status_sent_cb = nullptr; void _data_rx(uint8_t * data, uint8_t len); void _status_rx(uint32_t data); void _data_tx(void); @@ -44,21 +45,14 @@ class SPISlaveClass static void _s_data_tx(void *arg); static void _s_status_tx(void *arg); public: - SPISlaveClass() - : _data_cb(NULL) - , _status_cb(NULL) - , _data_sent_cb(NULL) - , _status_sent_cb(NULL) - {} - ~SPISlaveClass() {} + SPISlaveClass() = default; + ~SPISlaveClass() = default; + void begin(); void begin(uint8_t statusLength); void end(); void setData(uint8_t * data, size_t len); - void setData(const char * data) - { - setData((uint8_t *)data, strlen(data)); - } + void setData(const char * data); void setStatus(uint32_t status); void onData(SpiSlaveDataHandler cb); void onDataSent(SpiSlaveSentHandler cb); diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index e6b415c50b..bfde051f16 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -140,8 +140,12 @@ SECTIONS *(.text.gdbstub* .text.gdb_init) /* Any GDB hooks */ - /* all functional callers are placed in IRAM (including SPI/IRQ callbacks/etc) here */ - *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ + /* functional interrupt & related funcs placed in IRAM */ + *(.text._ZNKSt8functionIFvN7esp826619FunctionalInterrupt3TagEE*) + *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_invoke*) + *(.text._ZNSt17_Function_handlerIFvN7esp826619FunctionalInterrupt3TagEE*_M_manager*) + + *(.text._ZNSt14_Function_baseD2Ev) /* std::function dtor */ #ifdef FP_IN_IRAM *libgcc.a:*f2.o(.literal .text)