/* 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 your EventResponder feedback here: * https://forum.pjrc.com/threads/44723-Arduino-Events */ #if !defined(EventResponder_h) && defined(__cplusplus) #define EventResponder_h #include /* EventResponder lets you control how your program responds to an event. * Imagine a basketball or football (American soccer) player who gets the * ball. Usually they will pass to another player who has the best * opportunity to score. Similarly in Arduino programming, events are * often triggered within interrupts or other timing sensitive code. * EventResponder can call your function a short time later, giving you * the ability to use Arduino functions and libraries which would not * be safe to use from an interrupt. However, some situations do call * for the most immediate response, even if doing so is more difficult. * EventResponder lets you choose how your function will be called, * without editing the timers or libraries which trigger the events. * * Event handling functions called by EventResponder should complete * their work quickly. Avoid delays or operations which may take * substantial time. While your function runs, no other event functions * (attached the same way) are able to run. * * If your EventResponder is triggered more than once before your * function can run, only the last trigger is used. Prior triggering, * including the status integer and data pointer, are overwritten and * your function is called only one time, based on the last trigger * event. */ extern "C" void systick_isr_with_timer_events(void); class EventResponder; typedef EventResponder& EventResponderRef; typedef void (*EventResponderFunction)(EventResponderRef); class EventResponder { public: constexpr EventResponder() { } ~EventResponder() { detach(); } enum EventType { // these are not meant for public consumption... 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 }; // Attach a function to be called from yield(). This should be the // default way to use EventResponder. Calls from yield() allow use // of Arduino libraries, String, Serial, etc. void attach(EventResponderFunction function, uint8_t priority=128) { bool irq = disableInterrupts(); detachNoInterrupts(); _function = function; _type = EventTypeYield; yield_active_check_flags |= YIELD_CHECK_EVENT_RESPONDER; // user setup a yield type... enableInterrupts(irq); } // Attach a function to be called immediately. This provides the // fastest possible response, but your function must be carefully // designed. void attachImmediate(EventResponderFunction function) { bool irq = disableInterrupts(); detachNoInterrupts(); _function = function; _type = EventTypeImmediate; enableInterrupts(irq); } // Attach a function to be called from a low priority interrupt. // Boards not supporting software triggered interrupts will implement // this as attachImmediate. On ARM and other platforms with software // interrupts, this allow fast interrupt-based response, but with less // disruption to other libraries requiring their own interrupts. void attachInterrupt(EventResponderFunction function, uint8_t priority=128) { bool irq = disableInterrupts(); detachNoInterrupts(); _function = function; _type = EventTypeInterrupt; SCB_SHPR3 |= 0x00FF0000; // configure PendSV, lowest priority // Make sure we are using the systic ISR that process this _VectorsRam[15] = systick_isr_with_timer_events; enableInterrupts(irq); } // Attach a function to be called as its own thread. Boards not running // a RTOS or pre-emptive scheduler shall implement this as attach(). void attachThread(EventResponderFunction function, void *param=nullptr) { attach(function); // for non-RTOS usage, compile as default attach } // Do not call any function. The user's program must occasionally check // whether the event has occurred, or use one of the wait functions. void detach() { bool irq = disableInterrupts(); detachNoInterrupts(); enableInterrupts(irq); } // Trigger the event. An optional status code and data may be provided. // The code triggering the event does NOT control which of the above // response methods will be used. virtual void triggerEvent(int status=0, void *data=nullptr) { _status = status; _data = data; if (_type == EventTypeImmediate) { (*_function)(*this); } else { triggerEventNotImmediate(); } } // Clear an event which has been triggered, but has not yet caused a // function to be called. bool clearEvent(); // Get the event's status code. Typically this will indicate if the event was // triggered due to successful completion, or how much data was successfully // processed (positive numbers) or an error (negative numbers). The // exact meaning of this status code depends on the code or library which // triggers the event. int getStatus() { return _status; } // Get the optional data pointer associated with the event. Often this // will be NULL, or will be the object instance which triggered the event. // Some libraries may use this to pass data associated with the event. void * getData() { return _data; } // An optional "context" may be associated with each EventResponder. // When more than one EventResponder has the same function attached, these // may be used to allow the function to obtain extra information needed // depending on which EventResponder called it. void setContext(void *context) { _context = context; } void * getContext() { return _context; } // Wait for event(s) to occur. These are most likely to be useful when // used with a scheduler or RTOS. bool waitForEvent(EventResponderRef event, int timeout); EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout); static void runFromYield() { if (!firstYield) return; // First, check if yield was called from an interrupt // never call normal handler functions from any interrupt context uint32_t ipsr; __asm__ volatile("mrs %0, ipsr\n" : "=r" (ipsr)::); if (ipsr != 0) return; // Next, check if any events have been triggered bool irq = disableInterrupts(); EventResponder *first = firstYield; if (first == nullptr) { enableInterrupts(irq); return; } // Finally, make sure we're not being recursively called, // which can happen if the user's function does anything // that calls yield. if (runningFromYield) { enableInterrupts(irq); return; } // Ok, update the runningFromYield flag and process event runningFromYield = true; firstYield = first->_next; if (firstYield) { firstYield->_prev = nullptr; } else { lastYield = nullptr; } enableInterrupts(irq); first->_triggered = false; (*(first->_function))(*first); runningFromYield = false; } static void runFromInterrupt(); operator bool() { return _triggered; } protected: void triggerEventNotImmediate(); void detachNoInterrupts(); int _status = 0; EventResponderFunction _function = nullptr; void *_data = nullptr; void *_context = nullptr; EventResponder *_next = nullptr; EventResponder *_prev = nullptr; EventType _type = EventTypeDetached; bool _triggered = false; static EventResponder *firstYield; static EventResponder *lastYield; static EventResponder *firstInterrupt; static EventResponder *lastInterrupt; static bool runningFromYield; private: static bool disableInterrupts() { uint32_t primask; __asm__ volatile("mrs %0, primask\n" : "=r" (primask)::); __disable_irq(); return (primask == 0) ? true : false; } static void enableInterrupts(bool doit) { if (doit) __enable_irq(); } }; class MillisTimer { public: constexpr MillisTimer() { } ~MillisTimer() { end(); } void begin(unsigned long milliseconds, EventResponderRef event); void beginRepeating(unsigned long milliseconds, EventResponderRef event); void end(); static void runFromTimer(); private: void addToWaitingList(); void addToActiveList(); unsigned long _ms = 0; unsigned long _reload = 0; MillisTimer *_next = nullptr; MillisTimer *_prev = nullptr; EventResponder *_event = nullptr; enum TimerStateType { TimerOff = 0, TimerWaiting, TimerActive }; volatile TimerStateType _state = TimerOff; static MillisTimer *listWaiting; // single linked list of waiting to start timers static MillisTimer *listActive; // double linked list of running timers static bool disableTimerInterrupt() { uint32_t primask; __asm__ volatile("mrs %0, primask\n" : "=r" (primask)::); __disable_irq(); return (primask == 0) ? true : false; } static void enableTimerInterrupt(bool doit) { if (doit) __enable_irq(); } }; #endif