Skip to content

Commit

Permalink
Scheduled Interrupt (#4609)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hreintke authored and devyte committed Jun 19, 2018
1 parent 91bb97d commit 641c5cd
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 8 deletions.
60 changes: 53 additions & 7 deletions cores/esp8266/FunctionalInterrupt.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,70 @@
#include <FunctionalInterrupt.h>

#include <Schedule.h>
#include "Arduino.h"
#include <ScheduledFunctions.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 ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, voidFuncPtr userFunc, void*fp , int mode);

// Structure for communication
struct ArgStructure {
std::function<void(void)> 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<void(void)> 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<void(InterruptInfo)> 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);
}
22 changes: 22 additions & 0 deletions cores/esp8266/FunctionalInterrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,34 @@
#include <stddef.h>
#include <stdint.h>
#include <functional>
#include <ScheduledFunctions.h>

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<void(void)> reqFunction = nullptr;
std::function<void(InterruptInfo)> reqScheduledFunction = nullptr;
};

struct ArgStructure {
InterruptInfo* interruptInfo = nullptr;
FunctionInfo* functionInfo = nullptr;
};

static ScheduledFunctions* scheduledInterrupts;
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
void attachScheduledInterrupt(uint8_t pin, std::function<void(InterruptInfo)> scheduledIntRoutine, int mode);


#endif //INTERRUPTS_H
117 changes: 117 additions & 0 deletions cores/esp8266/ScheduledFunctions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* ScheduledFunctions.cpp
*
* Created on: 27 apr. 2018
* Author: Herman
*/
#include "ScheduledFunctions.h"

std::list<ScheduledFunctions::ScheduledElement> 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<ScheduledFunctions::ScheduledElement>::iterator ScheduledFunctions::eraseElement(std::list<ScheduledFunctions::ScheduledElement>::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<int>(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++;
}
}
}

51 changes: 51 additions & 0 deletions cores/esp8266/ScheduledFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* ScheduledFunctions.h
*
* Created on: 27 apr. 2018
* Author: Herman
*/
#include "Arduino.h"
#include "Schedule.h"

#include <functional>
#include <memory>
#include <list>
#include <climits>

#ifndef SCHEDULEDFUNCTIONS_H_
#define SCHEDULEDFUNCTIONS_H_

typedef std::function<void(void)> ScheduledFunction;
typedef std::shared_ptr<void> 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<ScheduledElement>::iterator eraseElement(std::list<ScheduledElement>::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<ScheduledElement> scheduledFunctions;
unsigned int maxElements;
unsigned int countElements = 0;

};

#endif /* SCHEDULEDFUNCTIONS_H_ */
30 changes: 29 additions & 1 deletion cores/esp8266/core_esp8266_wiring_digital.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)

This comment has been minimized.

Copy link
@shimarin

shimarin Jun 30, 2018

Contributor

handler->arg can be null, so should be

if (localArg && localArg->interruptInfo)

#4850

This comment has been minimized.

Copy link
@devyte

devyte Jun 30, 2018

Collaborator

Does that fix #4850 ?

This comment has been minimized.

Copy link
@shimarin

shimarin Jun 30, 2018

Contributor

At least for me, yes.
However I’m not sure if it applies everyone.

This comment has been minimized.

Copy link
@arihantdaga

arihantdaga Jul 2, 2018

This Seems to be working...

This comment has been minimized.

Copy link
@tarzan115

tarzan115 Jul 2, 2018

Contributor

Me too. It's working now

{
localArg->interruptInfo->pin = i;
localArg->interruptInfo->value = __digitalRead(i);
localArg->interruptInfo->micro = micros();
}
if (handler->arg)
{
((voidFuncPtrArg)handler->fn)(handler->arg);
Expand All @@ -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
Expand All @@ -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();
Expand Down

0 comments on commit 641c5cd

Please sign in to comment.