aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnatoli Arkhipenko <arkhipenko@hotmail.com>2015-10-01 09:34:10 -0400
committerAnatoli Arkhipenko <arkhipenko@hotmail.com>2015-10-01 09:34:10 -0400
commit332d0cf661ff7d86640773168113ffe5e47de505 (patch)
tree527af3ac3deaaaafdffdaced2512b2d9b01a4b49 /src
parent1efab7eae2d24629f819e4b0ae5aa7aa0b7bcf43 (diff)
Updated library to be arduino 1.5 comliant
* Included library.properties file * Updated version numbers to be semver compliant * Tagged lib files with the version number
Diffstat (limited to 'src')
-rw-r--r--src/TaskScheduler.cpp291
-rw-r--r--src/TaskScheduler.h137
2 files changed, 428 insertions, 0 deletions
diff --git a/src/TaskScheduler.cpp b/src/TaskScheduler.cpp
new file mode 100644
index 0000000..43cb976
--- /dev/null
+++ b/src/TaskScheduler.cpp
@@ -0,0 +1,291 @@
+// Cooperative multitasking library for Arduino version 1.6.0
+// Copyright (c) 2015 Anatoli Arkhipenko
+//
+
+/* ============================================
+Cooperative multitasking library code is placed under the MIT license
+Copyright (c) 2015 Anatoli Arkhipenko
+
+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.
+===============================================
+*/
+
+#include "TaskScheduler.h"
+
+// ------------------ Task implementation --------------------
+
+/** Constructor, uses default default values for the parameters
+ * so could be called with no parameters.
+ */
+Task::Task(unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, boolean aEnable) {
+ reset();
+ set(aInterval, aIterations, aCallback);
+ if (aScheduler) aScheduler->addTask(*this);
+ if (aEnable) enable();
+}
+
+/** Resets (initializes) the task/
+ * Task is not enabled and is taken out
+ * out of the execution chain as a result
+ */
+void Task::reset() {
+ iEnabled = false;
+ iPreviousMillis = 0;
+ iPrev = NULL;
+ iNext = NULL;
+ iScheduler = NULL;
+#ifdef _TASK_TIMECRITICAL
+ iOverrun = 0;
+#endif
+}
+
+/** Explicitly set Task execution parameters
+ * @param aInterval - execution interval in ms
+ * @param aIterations - number of iterations, use -1 for no limit
+ * @param aCallback - pointer to the callback function which executes the task actions
+ */
+void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)()) {
+ iInterval = aInterval;
+ iSetIterations = iIterations = aIterations;
+ iCallback = aCallback;
+}
+
+/** Sets number of iterations for the task
+ * if task is enabled, schedule for immediate execution
+ * @param aIterations - number of iterations, use -1 for no limit
+ */
+void Task::setIterations(long aIterations) {
+ iSetIterations = iIterations = aIterations;
+}
+
+/** Enables the task
+ * and schedules it for execution as soon as possible
+ */
+void Task::enable() {
+ iEnabled = true;
+ iPreviousMillis = millis() - iInterval;
+}
+
+/** Enables the task
+ * and schedules it for execution after a delay = aInterval
+ */
+void Task::enableDelayed(unsigned long aDelay) {
+ iEnabled = true;
+ delay(aDelay);
+}
+
+/** Delays Task for execution after a delay = aInterval (if task is enabled).
+ * leaves task enabled or disabled
+ * if aDelay is zero, delays for the original scheduling interval from now
+ */
+void Task::delay(unsigned long aDelay) {
+ if (!aDelay) aDelay = iInterval;
+ iPreviousMillis = millis() - iInterval + aDelay;
+}
+
+/** Sets the execution interval.
+ * Task execution is delayed for aInterval
+ * Use enable() to schedule execution ASAP
+ * @param aInterval - new execution interval
+ */
+void Task::setInterval (unsigned long aInterval) {
+ iInterval = aInterval;
+ delay();
+}
+
+/** Disables task
+ * Task will no loner be executed by the scheduler
+ */
+void Task::disable() {
+ iEnabled = false;
+}
+
+/** Restarts task
+ * Task will run number of iterations again
+ */
+void Task::restart() {
+ iIterations = iSetIterations;
+ enable();
+}
+
+/** Restarts task delayed
+ * Task will run number of iterations again
+ */
+void Task::restartDelayed(unsigned long aDelay) {
+ iIterations = iSetIterations;
+ enableDelayed(aDelay);
+}
+
+// ------------------ Scheduler implementation --------------------
+
+/** Default constructor.
+ * Creates a scheduler with an empty execution chain.
+ */
+Scheduler::Scheduler() {
+ init();
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ iAllowSleep = true;
+#endif
+}
+
+/** Appends task aTask to the tail of the execution chain.
+ * @param &aTask - reference to the Task to be appended.
+ * @note Task can only be part of the chain once.
+ */
+ void Scheduler::addTask(Task& aTask) {
+
+ aTask.iScheduler = this;
+// First task situation:
+ if (iFirst == NULL) {
+ iFirst = &aTask;
+ aTask.iPrev = NULL;
+ }
+ else {
+// This task gets linked back to the previous last one
+ aTask.iPrev = iLast;
+ iLast->iNext = &aTask;
+ }
+// "Previous" last task gets linked to this one - as this one becomes the last one
+ aTask.iNext = NULL;
+ iLast = &aTask;
+}
+
+/** Deletes specific Task from the execution chain
+ * @param &aTask - reference to the task to be deleted from the chain
+ */
+void Scheduler::deleteTask(Task& aTask) {
+ if (aTask.iPrev == NULL) {
+ if (aTask.iNext == NULL) {
+ iFirst = NULL;
+ iLast = NULL;
+ return;
+ }
+ else {
+ aTask.iNext->iPrev = NULL;
+ iFirst = aTask.iNext;
+ aTask.iNext = NULL;
+ return;
+ }
+ }
+
+ if (aTask.iNext == NULL) {
+ aTask.iPrev->iNext = NULL;
+ iLast = aTask.iPrev;
+ aTask.iPrev = NULL;
+ return;
+ }
+
+ aTask.iPrev->iNext = aTask.iNext;
+ aTask.iNext->iPrev = aTask.iPrev;
+ aTask.iPrev = NULL;
+ aTask.iNext = NULL;
+}
+
+/** Disables all tasks in the execution chain
+ * Convenient for error situations, when the only
+ * task remaining active is an error processing task
+ */
+void Scheduler::disableAll() {
+ Task *current = iFirst;
+ while (current) {
+ current->disable();
+ current = current->iNext;
+ }
+}
+
+
+/** Enables all the tasks in the execution chain
+ */
+ void Scheduler::enableAll() {
+ Task *current = iFirst;
+ while (current) {
+ current->enable();
+ current = current->iNext;
+ }
+}
+
+/** Makes one pass through the execution chain.
+ * Tasks are executed in the order they were added to the chain
+ * There is no concept of priority
+ * Different pseudo "priority" could be achieved
+ * by running task more frequently
+ */
+void Scheduler::execute() {
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ bool idleRun = true;
+#endif
+
+ iCurrent = iFirst;
+
+ while (iCurrent) {
+ do {
+ if (iCurrent->iEnabled) {
+ if (iCurrent->iIterations == 0) {
+ iCurrent->disable();
+ break;
+ }
+ if (iCurrent->iInterval > 0) {
+ unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
+ if (targetMillis <= millis()) {
+ if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
+ iCurrent->iPreviousMillis += iCurrent->iInterval;
+
+ #ifdef _TASK_TIMECRITICAL
+ // Updated_previous+current should put us into the future, so iOverrun should be positive or zero.
+ // If negative - the task is behind (next execution time is already in the past)
+ iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
+ #endif
+
+ if (iCurrent->iCallback) {
+ (*(iCurrent->iCallback))();
+ #ifdef _TASK_SLEEP_ON_IDLE_RUN
+ idleRun = false;
+ #endif
+ }
+ break;
+ }
+ }
+ else {
+ if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
+ if (iCurrent->iCallback) {
+ (*(iCurrent->iCallback))();
+ #ifdef _TASK_SLEEP_ON_IDLE_RUN
+ idleRun = false;
+ #endif
+ }
+ }
+ }
+ } while (0); //guaranteed single run - allows use of "break" to exit
+ iCurrent = iCurrent->iNext;
+ }
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ if (idleRun && iAllowSleep) {
+ set_sleep_mode(SLEEP_MODE_IDLE);
+ sleep_enable();
+ /* Now enter sleep mode. */
+ sleep_mode();
+
+ /* The program will continue from here after the timer timeout ~1 ms */
+ sleep_disable(); /* First thing to do is disable sleep. */
+ }
+#endif
+}
+
+
diff --git a/src/TaskScheduler.h b/src/TaskScheduler.h
new file mode 100644
index 0000000..90f8132
--- /dev/null
+++ b/src/TaskScheduler.h
@@ -0,0 +1,137 @@
+// Cooperative multitasking library for Arduino version 1.6.0
+// Copyright (c) 2015 Anatoli Arkhipenko
+//
+// Changelog:
+// 2015-02-24 - Initial release
+// 2015-02-28 - added delay() and disableOnLastIteration() functions
+// 2015-03-25 - changed scheduler execute() function for a more precise delay calculation:
+// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
+// 2. Delay is invoked only if none of the tasks ran
+// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself.
+// 2015-05-11 - added restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations
+// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead
+// v1.4.1:
+// 2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE)
+// sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined
+// v1.5.0:
+// 2015-09-20 - access to currently executing task (for callback functions)
+// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
+// 2015-09-20 - option to create a task already enabled
+// v1.5.1:
+// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
+// Thanks to Hannes Morgenstern for catching this one
+// v1.6.0:
+// 2015-09-22 - revert back to having all tasks disable on last iteration.
+// 2015-09-22 - deprecated disableOnLastIteration method as a result
+// 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
+// 2015-10-01 - made version numbers semver compliant (documentation only)
+
+
+
+/* ============================================
+Cooperative multitasking library code is placed under the MIT license
+Copyright (c) 2015 Anatoli Arkhipenko
+
+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.
+===============================================
+*/
+
+
+#include <Arduino.h>
+
+
+#ifndef _TASKSCHEDULER_H_
+#define _TASKSCHEDULER_H_
+
+//#define _TASK_DEBUG
+//#define _TASK_TIMECRITICAL
+//#define _TASK_SLEEP_ON_IDLE_RUN
+
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+#include <avr/sleep.h>
+#include <avr/power.h>
+#endif
+
+
+class Task;
+
+class Scheduler {
+ public:
+ Scheduler();
+ inline void init() { iFirst = NULL; iLast = NULL; iCurrent = NULL; }
+ void addTask(Task& aTask);
+ void deleteTask(Task& aTask);
+ void disableAll();
+ void enableAll();
+ void execute();
+ inline Task& currentTask() {return *iCurrent; }
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ void allowSleep(bool aState) { iAllowSleep = aState; }
+#endif
+
+ private:
+ Task *iFirst, *iLast, *iCurrent;
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+ bool iAllowSleep;
+#endif
+};
+
+class Task {
+ friend class Scheduler;
+ public:
+ Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false);
+
+ void enable();
+ void enableDelayed(unsigned long aDelay=0);
+ void delay(unsigned long aDelay=0);
+ void restart();
+ void restartDelayed(unsigned long aDelay=0);
+ void disable();
+ inline bool isEnabled() { return iEnabled; }
+ void set(unsigned long aInterval, long aIterations, void (*aCallback)());
+ void setInterval(unsigned long aInterval);
+ inline unsigned long getInterval() { return iInterval; }
+ void setIterations(long aIterations);
+ inline long getIterations() { return iIterations; }
+ inline void setCallback(void (*aCallback)()) { iCallback = aCallback; }
+#ifdef _TASK_TIMECRITICAL
+ inline long getOverrun() { return iOverrun; }
+#endif
+ inline bool isFirstIteration() { return (iIterations >= iSetIterations-1); }
+ inline bool isLastIteration() { return (iIterations == 0); }
+
+ private:
+ void reset();
+
+ volatile bool iEnabled;
+ volatile unsigned long iInterval;
+ volatile unsigned long iPreviousMillis;
+#ifdef _TASK_TIMECRITICAL
+ volatile long iOverrun;
+#endif
+ volatile long iIterations;
+ long iSetIterations;
+ void (*iCallback)();
+ Task *iPrev, *iNext;
+ Scheduler *iScheduler;
+};
+
+
+#endif /* _TASKSCHEDULER_H_ */