Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 76 additions & 32 deletions cores/esp8266/FunctionalInterrupt.cpp
Original file line number Diff line number Diff line change
@@ -1,65 +1,109 @@
#include <FunctionalInterrupt.h>
#include <Schedule.h>
// 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<ArgStructure*>(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<ArgStructure*>(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<ArgStructure*>(varg);
schedule_function(std::bind(arg->functionInfo->reqScheduledFunction, InterruptInfo(*(arg->interruptInfo))));
}

}

void attachInterrupt(uint8_t pin, std::function<void(void)> 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<ArgStructure*>(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<void(InterruptInfo)> 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<void(*)(void*)>(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<void(*)(void*)>(func), arg, mode, true);
}
45 changes: 22 additions & 23 deletions cores/esp8266/FunctionalInterrupt.h
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
#ifndef FUNCTIONALINTERRUPT_H
#define FUNCTIONALINTERRUPT_H
// core_esp8266_wiring_digital helper for std::function<...> dispatch

#include <stddef.h>
#include <stdint.h>
#pragma once

#include <cstddef>
#include <cstdint>
#include <functional>

extern "C" {
#include "c_types.h"
#include "ets_sys.h"
}
namespace esp8266::FunctionalInterrupt {

// Structures for communication
using PlainFunction = std::function<void()>;

struct InterruptInfo {
uint8_t pin = 0;
uint8_t value = 0;
uint32_t micro = 0;
struct Tag {
};

struct FunctionInfo {
std::function<void(void)> reqFunction = nullptr;
std::function<void(InterruptInfo)> reqScheduledFunction = nullptr;
};
using Function = std::function<void(Tag)>;

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<void(void)> intRoutine, int mode);
void attachScheduledInterrupt(uint8_t pin, std::function<void(InterruptInfo)> scheduledIntRoutine, int mode);
using ScheduledFunction = std::function<void(InterruptInfo)>;

} // 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);
}
20 changes: 19 additions & 1 deletion cores/esp8266/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <Arduino.h>
#include <cxxabi.h>

#include <c_types.h>

using __cxxabiv1::__guard;

// Debugging helper, last allocation which returned NULL
Expand All @@ -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)
{
Expand Down Expand Up @@ -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();
Expand Down
57 changes: 27 additions & 30 deletions cores/esp8266/core_esp8266_wiring_digital.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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"
3 changes: 3 additions & 0 deletions libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions libraries/SPISlave/src/SPISlave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading