/* 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; | |||||
} | |||||
} | |||||
/* 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 |
#include "HardwareSerial.h" | #include "HardwareSerial.h" | ||||
#include "usb_serial.h" | #include "usb_serial.h" | ||||
#include "usb_seremu.h" | #include "usb_seremu.h" | ||||
#include "EventResponder.h" | |||||
void yield(void) __attribute__ ((weak)); | void yield(void) __attribute__ ((weak)); | ||||
void yield(void) | void yield(void) | ||||
if (Serial6.available()) serialEvent6(); | if (Serial6.available()) serialEvent6(); | ||||
#endif | #endif | ||||
running = 0; | running = 0; | ||||
EventResponder::runFromYield(); | |||||
}; | }; |