summaryrefslogtreecommitdiff
path: root/src/TaskScheduler.h
diff options
context:
space:
mode:
authorAnatoli Arkhipenko <arkhipenko@hotmail.com>2016-02-02 23:33:21 -0500
committerAnatoli Arkhipenko <arkhipenko@hotmail.com>2016-02-03 23:40:50 -0500
commitc82cefce494ea962f46b3178dab240b7a8a3ae62 (patch)
tree0eddb6b04a4b09db0b8807b605f466f155003a15 /src/TaskScheduler.h
parent73e5c9a810cf1210e3c1a849b9162a71400f48c3 (diff)
v2.1.0 - microsecond resolution, startNow()v2.1.0
* support for microsecond resolution * added Scheduler baseline start time reset method: startNow() * added Example #13 for microseconds resolution test
Diffstat (limited to 'src/TaskScheduler.h')
-rw-r--r--src/TaskScheduler.h119
1 files changed, 68 insertions, 51 deletions
diff --git a/src/TaskScheduler.h b/src/TaskScheduler.h
index 8eea44b..e81212c 100644
--- a/src/TaskScheduler.h
+++ b/src/TaskScheduler.h
@@ -86,6 +86,7 @@
//
// v2.1.0:
// 2016-02-01 - support for microsecond resolution
+// 2016-02-02 - added Scheduler baseline start time reset method: startNow()
/* ============================================
Cooperative multitasking library code is placed under the MIT license
@@ -114,7 +115,6 @@ THE SOFTWARE.
#include <Arduino.h>
-
#ifndef _TASKSCHEDULER_H_
#define _TASKSCHEDULER_H_
@@ -141,22 +141,24 @@ THE SOFTWARE.
#define _TASK_TIME_FUNCTION() millis()
- #endif
+ #endif // _TASK_MICRO_RES
+
#ifdef _TASK_SLEEP_ON_IDLE_RUN
#ifdef ARDUINO_ARCH_AVR
#include <avr/sleep.h>
#include <avr/power.h>
-#endif
+#endif // ARDUINO_ARCH_AVR
#ifdef ARDUINO_ARCH_ESP8266
extern "C" {
#include "user_interface.h"
}
-#endif
+#define _TASK_ESP8266_DLY_THRESHOLD 200L
+#endif // ARDUINO_ARCH_ESP8266
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
#define TASK_IMMEDIATE 0
#define TASK_FOREVER (-1)
@@ -175,7 +177,7 @@ extern "C" {
#define TASK_MINUTE 60000000L
#define TASK_HOUR 3600000000L
-#endif
+#endif // _TASK_MICRO_RES
#ifdef _TASK_STATUS_REQUEST
@@ -197,7 +199,7 @@ class StatusRequest {
unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient
int iStatus; // status of the last completed request. negative = error; zero = OK; >positive = OK with a specific status
};
-#endif
+#endif // _TASK_STATUS_REQUEST
typedef struct {
@@ -213,7 +215,7 @@ class Scheduler;
#ifdef _TASK_WDT_IDS
static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically.
-#endif
+#endif // _TASK_WDT_IDS
class Task {
friend class Scheduler;
@@ -221,7 +223,7 @@ class Task {
Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
#ifdef _TASK_STATUS_REQUEST
Task(void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL);
-#endif
+#endif // _TASK_STATUS_REQUEST
void enable();
bool enableIfNot();
@@ -244,24 +246,24 @@ class Task {
#ifdef _TASK_TIMECRITICAL
inline long getOverrun() { return iOverrun; }
inline long getStartDelay() { return iStartDelay; }
-#endif
+#endif // _TASK_TIMECRITICAL
inline bool isFirstIteration() { return (iRunCounter <= 1); }
inline bool isLastIteration() { return (iIterations == 0); }
#ifdef _TASK_STATUS_REQUEST
void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1);
inline StatusRequest* getStatusRequest() {return iStatusRequest; }
-#endif
+#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_WDT_IDS
inline void setId(unsigned int aID) { iTaskID = aID; }
inline unsigned int getId() { return iTaskID; }
inline void setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
inline unsigned int getControlPoint() { return iControlPoint; }
-#endif
+#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
inline void setLtsPointer(void *aPtr) { iLTS = aPtr; }
inline void* getLtsPointer() { return iLTS; }
-#endif
+#endif // _TASK_LTS_POINTER
private:
void reset();
@@ -273,7 +275,7 @@ class Task {
#ifdef _TASK_TIMECRITICAL
volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past)
volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis
-#endif
+#endif // _TASK_TIMECRITICAL
volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations
long iSetIterations; // number of iterations originally requested (for restarts)
unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable.
@@ -284,20 +286,20 @@ class Task {
Scheduler *iScheduler; // pointer to the current scheduler
#ifdef _TASK_STATUS_REQUEST
StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on
-#endif
+#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_WDT_IDS
unsigned int iTaskID; // task ID (for debugging and watchdog identification)
unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass
-#endif
+#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct).
-#endif
+#endif // _TASK_LTS_POINTER
};
#ifdef _TASK_PRIORITY
static Scheduler* iCurrentScheduler;
-#endif
+#endif // _TASK_PRIORITY
class Scheduler {
friend class Task;
@@ -309,29 +311,30 @@ class Scheduler {
void disableAll(bool aRecursive = true);
void enableAll(bool aRecursive = true);
bool execute(); // Returns true if at none of the tasks' callback methods was invoked (true if idle run)
+ void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW.
inline Task& currentTask() {return *iCurrent; }
#ifdef _TASK_SLEEP_ON_IDLE_RUN
void allowSleep(bool aState = true);
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_LTS_POINTER
inline void* currentLts() {return iCurrent->iLTS; }
-#endif
+#endif // _TASK_LTS_POINTER
#ifdef _TASK_TIMECRITICAL
inline bool isOverrun() { return (iCurrent->iOverrun < 0); }
-#endif
+#endif // _TASK_TIMECRITICAL
#ifdef _TASK_PRIORITY
void setHighPriorityScheduler(Scheduler* aScheduler);
static Scheduler& currentScheduler() { return *(iCurrentScheduler); };
-#endif
+#endif // _TASK_PRIORITY
private:
Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain
#ifdef _TASK_SLEEP_ON_IDLE_RUN
bool iAllowSleep; // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time.
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_PRIORITY
Scheduler *iHighPriority; // Pointer to a higher priority scheduler
-#endif
+#endif // _TASK_PRIORITY
};
@@ -346,10 +349,10 @@ Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Sche
if (aScheduler) aScheduler->addTask(*this);
#ifdef _TASK_STATUS_REQUEST
iStatusRequest = NULL;
-#endif
+#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_WDT_IDS
iTaskID = ++__task_id_counter;
-#endif
+#endif // _TASK_WDT_IDS
if (aEnable) enable();
}
@@ -366,7 +369,7 @@ Task::Task( void (*aCallback)(), Scheduler* aScheduler, bool (*aOnEnable)(), voi
iStatusRequest = NULL;
#ifdef _TASK_WDT_IDS
iTaskID = ++__task_id_counter;
-#endif
+#endif // _TASK_WDT_IDS
}
/** Signals completion of the StatusRequest by one of the participating events
@@ -410,7 +413,7 @@ void Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval
enable();
}
}
-#endif
+#endif // _TASK_STATUS_REQUEST
/** Resets (initializes) the task/
* Task is not enabled and is taken out
@@ -428,16 +431,16 @@ void Task::reset() {
#ifdef _TASK_TIMECRITICAL
iOverrun = 0;
iStartDelay = 0;
-#endif
+#endif // _TASK_TIMECRITICAL
#ifdef _TASK_WDT_IDS
iControlPoint = 0;
-#endif
+#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
iLTS = NULL;
-#endif
+#endif // _TASK_LTS_POINTER
#ifdef _TASK_STATUS_REQUEST
iStatus.waiting = 0;
-#endif
+#endif // _TASK_STATUS_REQUEST
}
/** Explicitly set Task execution parameters
@@ -580,10 +583,10 @@ void Scheduler::init() {
iCurrent = NULL;
#ifdef _TASK_PRIORITY
iHighPriority = NULL;
-#endif
+#endif // _TASK_PRIORITY
#ifdef _TASK_SLEEP_ON_IDLE_RUN
allowSleep(true);
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
}
/** Appends task aTask to the tail of the execution chain.
@@ -651,8 +654,8 @@ void Scheduler::disableAll(bool aRecursive) {
current = current->iNext;
}
#ifdef _TASK_PRIORITY
- if (aRecursive && iHighPriority) iHighPriority->disableAll(aRecursive);
-#endif
+ if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
+#endif // _TASK_PRIORITY
}
@@ -666,8 +669,8 @@ void Scheduler::disableAll(bool aRecursive) {
current = current->iNext;
}
#ifdef _TASK_PRIORITY
- if (aRecursive && iHighPriority) iHighPriority->enableAll(aRecursive);
-#endif
+ if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
+#endif // _TASK_PRIORITY
}
/** Sets scheduler for the higher priority tasks (support for layered task priority)
@@ -680,9 +683,9 @@ void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) {
if (iHighPriority) {
iHighPriority->allowSleep(false); // Higher priority schedulers should not do power management
}
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
};
-#endif
+#endif // _TASK_PRIORITY
#ifdef _TASK_SLEEP_ON_IDLE_RUN
@@ -691,12 +694,26 @@ void Scheduler::allowSleep(bool aState) {
#ifdef ARDUINO_ARCH_ESP8266
wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T );
-#endif
+#endif // ARDUINO_ARCH_ESP8266
}
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
+void Scheduler::startNow( bool aRecursive ) {
+ unsigned long t = _TASK_TIME_FUNCTION();
+
+ iCurrent = iFirst;
+ while (iCurrent) {
+ if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
+ iCurrent = iCurrent->iNext;
+ }
+
+#ifdef _TASK_PRIORITY
+ if (aRecursive && iHighPriority) iHighPriority->startNow( true );
+#endif // _TASK_PRIORITY
+}
+
/** 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
@@ -710,7 +727,7 @@ bool Scheduler::execute() {
#ifdef ARDUINO_ARCH_ESP8266
unsigned long t1 = micros();
unsigned long t2 = 0;
-#endif
+#endif // ARDUINO_ARCH_ESP8266
iCurrent = iFirst;
@@ -720,7 +737,7 @@ bool Scheduler::execute() {
// If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler
if (iHighPriority) idleRun = iHighPriority->execute() && idleRun;
iCurrentScheduler = this;
-#endif
+#endif // _TASK_PRIORITY
do {
if ( iCurrent->iStatus.enabled ) {
@@ -728,7 +745,7 @@ bool Scheduler::execute() {
#ifdef _TASK_WDT_IDS
// For each task the control points are initialized to avoid confusion because of carry-over:
iCurrent->iControlPoint = 0;
-#endif
+#endif // _TASK_WDT_IDS
// Disable task on last iteration:
if (iCurrent->iIterations == 0) {
@@ -752,7 +769,7 @@ bool Scheduler::execute() {
}
iCurrent->iStatus.waiting = 0;
}
-#endif
+#endif // _TASK_STATUS_REQUEST
if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break;
@@ -766,7 +783,7 @@ bool Scheduler::execute() {
unsigned long p = iCurrent->iPreviousMillis;
iCurrent->iOverrun = (long) ( p + i - m );
iCurrent->iStartDelay = (long) ( m - p );
-#endif
+#endif // _TASK_TIMECRITICAL
iCurrent->iDelay = i;
if ( iCurrent->iCallback ) {
@@ -789,15 +806,15 @@ bool Scheduler::execute() {
/* The program will continue from here after the timer timeout ~1 ms */
sleep_disable(); /* First thing to do is disable sleep. */
-#endif
+#endif // ARDUINO_ARCH_AVR
#ifdef ARDUINO_ARCH_ESP8266
// to do: find suitable sleep function for esp8266
t2 = micros() - t1;
- if (t2 < 1000L) delay(1); // ESP8266 implementation of delay() uses timers and yield
-#endif
+ if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); // ESP8266 implementation of delay() uses timers and yield
+#endif // ARDUINO_ARCH_ESP8266
}
-#endif
+#endif // _TASK_SLEEP_ON_IDLE_RUN
return (idleRun);
}