diff options
| author | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-10-01 09:34:10 -0400 |
|---|---|---|
| committer | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-10-01 09:34:10 -0400 |
| commit | 332d0cf661ff7d86640773168113ffe5e47de505 (patch) | |
| tree | 527af3ac3deaaaafdffdaced2512b2d9b01a4b49 /src | |
| parent | 1efab7eae2d24629f819e4b0ae5aa7aa0b7bcf43 (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.cpp | 291 | ||||
| -rw-r--r-- | src/TaskScheduler.h | 137 |
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_ */ |
