@@ -0,0 +1,161 @@ | |||
/* EventResponder - Simple event-based programming for Arduino | |||
* Copyright 2017 Paul Stoffregen | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
* SOFTWARE. | |||
*/ | |||
/* EventResponder is an experimental API, almost certain to | |||
* incompatibly change as it develops. Please understand any | |||
* programs you write now using EventResponder may need to be | |||
* updated as EventResponder develops. | |||
* | |||
* Please post EventResponder post your feedback here: | |||
* https://forum.pjrc.com/threads/44723-Arduino-Events | |||
*/ | |||
#include "EventResponder.h" | |||
EventResponder * EventResponder::firstYield = nullptr; | |||
EventResponder * EventResponder::lastYield = nullptr; | |||
EventResponder * EventResponder::firstInterrupt = nullptr; | |||
EventResponder * EventResponder::lastInterrupt = nullptr; | |||
bool EventResponder::runningFromYield = false; | |||
// TODO: interrupt disable/enable needed in many places!!! | |||
void EventResponder::triggerEventNotImmediate() | |||
{ | |||
if (_pending) { | |||
// already triggered | |||
return; | |||
} | |||
if (_type == EventTypeYield) { | |||
// normal type, called from yield() | |||
if (firstYield == nullptr) { | |||
_next = nullptr; | |||
_prev = nullptr; | |||
firstYield = this; | |||
lastYield = this; | |||
} else { | |||
_next = nullptr; | |||
_prev = lastYield; | |||
lastYield = this; | |||
} | |||
} else if (_type == EventTypeInterrupt) { | |||
// interrupt, called from software interrupt | |||
if (firstInterrupt == nullptr) { | |||
_next = nullptr; | |||
_prev = nullptr; | |||
firstInterrupt = this; | |||
lastInterrupt = this; | |||
} else { | |||
_next = nullptr; | |||
_prev = lastInterrupt; | |||
lastInterrupt = this; | |||
} | |||
// TODO set interrupt pending | |||
} else { | |||
// detached, easy :-) | |||
} | |||
_pending = true; | |||
} | |||
void pendablesrvreq_isr(void) | |||
{ | |||
EventResponder::runFromInterrupt(); | |||
} | |||
void EventResponder::runFromInterrupt() | |||
{ | |||
for (EventResponder *first=firstInterrupt; first; first = first->_next) { | |||
first->_pending = false; | |||
(*(first->_function))(*first); | |||
} | |||
firstInterrupt = nullptr; | |||
lastInterrupt = nullptr; | |||
} | |||
bool EventResponder::clearEvent() | |||
{ | |||
if (_pending) { | |||
if (_type == EventTypeYield) { | |||
if (_prev) { | |||
_prev->_next = _next; | |||
} else { | |||
firstYield = _next; | |||
} | |||
if (_next) { | |||
_next->_prev = _prev; | |||
} else { | |||
lastYield = _prev; | |||
} | |||
} else if (_type == EventTypeInterrupt) { | |||
if (_prev) { | |||
_prev->_next = _next; | |||
} else { | |||
firstInterrupt = _next; | |||
} | |||
if (_next) { | |||
_next->_prev = _prev; | |||
} else { | |||
lastInterrupt = _prev; | |||
} | |||
} | |||
_pending = false; | |||
return true; | |||
} | |||
return false; | |||
} | |||
void EventResponder::detach() | |||
{ | |||
if (_type == EventTypeYield) { | |||
if (_pending) { | |||
if (_prev) { | |||
_prev->_next = _next; | |||
} else { | |||
firstYield = _next; | |||
} | |||
if (_next) { | |||
_next->_prev = _prev; | |||
} else { | |||
lastYield = _prev; | |||
} | |||
} | |||
_type = EventTypeDetached; | |||
} else if (_type == EventTypeInterrupt) { | |||
if (_pending) { | |||
if (_prev) { | |||
_prev->_next = _next; | |||
} else { | |||
firstInterrupt = _next; | |||
} | |||
if (_next) { | |||
_next->_prev = _prev; | |||
} else { | |||
lastInterrupt = _prev; | |||
} | |||
} | |||
_type = EventTypeDetached; | |||
} | |||
} | |||
@@ -0,0 +1,124 @@ | |||
/* EventResponder - Simple event-based programming for Arduino | |||
* Copyright 2017 Paul Stoffregen | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
* SOFTWARE. | |||
*/ | |||
/* EventResponder is an experimental API, almost certain to | |||
* incompatibly change as it develops. Please understand any | |||
* programs you write now using EventResponder may need to be | |||
* updated as EventResponder develops. | |||
* | |||
* Please post EventResponder post your feedback here: | |||
* https://forum.pjrc.com/threads/44723-Arduino-Events | |||
*/ | |||
#if !defined(EventResponder_h) && defined(__cplusplus) | |||
#define EventResponder_h | |||
#include <Arduino.h> | |||
class EventResponder; | |||
typedef EventResponder& EventResponderRef; | |||
typedef void (*EventResponderFunction)(EventResponderRef); | |||
class EventResponder | |||
{ | |||
public: | |||
constexpr EventResponder() { | |||
} | |||
~EventResponder() { | |||
detach(); | |||
} | |||
enum EventType { | |||
EventTypeDetached = 0, // no function is called | |||
EventTypeYield, // function is called from yield() | |||
EventTypeImmediate, // function is called immediately | |||
EventTypeInterrupt, // function is called from interrupt | |||
EventTypeThread // function is run as a new thread | |||
}; | |||
void attach(EventResponderFunction function) { | |||
detach(); | |||
_function = function; | |||
_type = EventTypeYield; | |||
} | |||
void attachImmediate(EventResponderFunction function) { | |||
detach(); | |||
_function = function; | |||
_type = EventTypeImmediate; | |||
} | |||
void attachInterrupt(EventResponderFunction function) { | |||
detach(); | |||
_function = function; | |||
_type = EventTypeInterrupt; | |||
// TODO: configure PendSV | |||
} | |||
void attachThread(EventResponderFunction function, void *param=nullptr) { | |||
attach(function); // for non-RTOS usage, compile as default attach | |||
} | |||
void detach(); | |||
virtual void triggerEvent(int status=0, void *data=nullptr) { | |||
_status = status; | |||
_data = data; | |||
if (_type == EventTypeImmediate) { | |||
(*_function)(*this); | |||
} else { | |||
triggerEventNotImmediate(); | |||
} | |||
} | |||
bool clearEvent(); | |||
int getStatus() { return _status; } | |||
void setContext(void *context) { _context = context; } | |||
void * getContext() { return _context; } | |||
void * getData() { return _data; } | |||
bool waitForEvent(EventResponderRef event, int timeout); | |||
EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout); | |||
static void runFromYield() { | |||
EventResponder *first = firstYield; | |||
if (first && !runningFromYield) { | |||
runningFromYield = true; | |||
firstYield = first->_next; | |||
if (firstYield) firstYield->_prev = nullptr; | |||
first->_pending = false; | |||
(*(first->_function))(*first); | |||
runningFromYield = false; | |||
} | |||
} | |||
static void runFromInterrupt(); | |||
operator bool() { return _pending; } | |||
protected: | |||
void triggerEventNotImmediate(); | |||
int _status = 0; | |||
EventResponderFunction _function = nullptr; | |||
void *_data = nullptr; | |||
void *_context = nullptr; | |||
EventResponder *_next = nullptr; | |||
EventResponder *_prev = nullptr; | |||
EventType _type = EventTypeDetached; | |||
bool _pending = false; | |||
static EventResponder *firstYield; | |||
static EventResponder *lastYield; | |||
static EventResponder *firstInterrupt; | |||
static EventResponder *lastInterrupt; | |||
static bool runningFromYield; | |||
}; | |||
#endif |
@@ -32,6 +32,7 @@ | |||
#include "HardwareSerial.h" | |||
#include "usb_serial.h" | |||
#include "usb_seremu.h" | |||
#include "EventResponder.h" | |||
void yield(void) __attribute__ ((weak)); | |||
void yield(void) | |||
@@ -54,4 +55,5 @@ void yield(void) | |||
if (Serial6.available()) serialEvent6(); | |||
#endif | |||
running = 0; | |||
EventResponder::runFromYield(); | |||
}; |