فهرست منبع

v2.1.0 - microsecond resolution, startNow()

 * support for microsecond resolution
 * added Scheduler baseline start time reset method: startNow()
 * added Example #13 for microseconds resolution test
Anatoli Arkhipenko 10 سال پیش
والد
کامیت
c82cefce49

+ 29 - 1
README

@@ -1,9 +1,12 @@
 Task Scheduler – cooperative multitasking for Arduino microcontrollers
 Version 2.1.0: 2016-02-01
+
+If you find TaskScheduler useful for your Arduino project, please drop me an email: arkhipenko@hotmail.com
+----------------------------------------------------------------------------------------------------------
   
 OVERVIEW:
  A lightweight implementation of cooperative multitasking (task scheduling) supporting:
- 1. Periodic task execution (with dynamic execution period in milliseconds)
+ 1. Periodic task execution (with dynamic execution period in milliseconds or microseconds)
  2. Number of iterations (n times)
  3. Execution of tasks in predefined sequence
  4. Dynamic change of task execution parameters (frequency, number of iterations, callback function)
@@ -18,9 +21,34 @@ Scheduling overhead: between 15 and 18 microseconds per scheduling pass (check t
 For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder.
 =======================================================================================================
 
+Check out what TaskScheduler can do:
+====================================
+  Houston midi clock project - TaskScheduler with microseconds resolution
+    (by chaffneue: My first arduino project. It's a multi-master midi controller with a shared clock and 
+     auto count in behaviour. Project files on github: https://github.com/chaffneue/houston
+	 youtube: https://www.youtube.com/watch?v=QRof550TtXo)
+	  
+  Aurora Chaser Cubesat project, University of Sydney (Yay! TaskScheduler goes to space)
+    (by Jordan Jolly: http://52.35.220.33/blogs/jordan-jolly-cubesat--aero3670--blog)
+	
+  Arduino Nano based Hexbug Scarab Robotic Spider 
+    (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/)
+	
+  Wave your hand to control OWI Robotic Arm... no strings attached
+    (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/)
+	
+  APIS - Automated Plant Irrigation System
+    (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/)
+	
+  Interactive Halloween Pumpkin
+    (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/)
+
+
 Changelog:
+=========
 v2.1.0:
     2016-02-01 - support for microsecond resolution
+    2016-02-02 - added Scheduler baseline start time reset method: startNow()
 
 v2.0.1:
     2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation

+ 2 - 0
examples/Scheduler_example02/Scheduler_example02.ino

@@ -65,6 +65,8 @@ void setup () {
   Serial.println("Scheduler TEST");
   
    delay(5000);
+   
+   runner.startNow();  // set point-in-time for scheduling start
 }
 
 

+ 17 - 3
examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino

@@ -10,7 +10,20 @@
  * The test board is Arduino UNO 16MHz processor.
  *
  
- 
+TaskScheduler 2.1.0:
+No modifiers
+Duration=19869
+
+with SLEEP
+Duration=20058
+
+with status request:
+Duration=20058
+
+with time critical:
+Duration=27289
+
+
 TaskScheduler 1.9.0:
 No modifiers
 Duration=15656
@@ -41,10 +54,11 @@ Duration=18423
 
 
 //#define _TASK_TIMECRITICAL     // Enable monitoring scheduling overruns
-#define _TASK_STATUS_REQUEST     // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
+//#define _TASK_STATUS_REQUEST     // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
 //#define _TASK_WDT_IDS          // Compile with support for wdt control points and task ids
 //#define _TASK_LTS_POINTER      // Compile with support for local task storage pointer
-#define _TASK_SLEEP_ON_IDLE_RUN
+//#define _TASK_SLEEP_ON_IDLE_RUN
+//#define _TASK_MICRO_RES
 #include <TaskScheduler.h>
 
 Scheduler ts;

+ 71 - 0
examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino

@@ -0,0 +1,71 @@
+/**
+ * TaskScheduler Test of microsecond scheduling resolution
+ *
+ * Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration 
+ * until it wraps when interval reaches about 72 minutes mark
+ * 
+ * Task 2 provides heartbeat at a steady 5 seconds intervals
+ *
+ */
+ 
+#define _TASK_MICRO_RES
+#include <TaskScheduler.h>
+
+#define T1_INIT (211L)
+
+Scheduler runner;
+// Callback methods prototypes
+void t1Callback();
+void t1OnDisable();
+void t2Callback();
+
+
+unsigned long t1_interval = T1_INIT;
+
+// Tasks
+Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable);      //adding task to the chain on creation
+Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true);  //adding task to the chain on creation
+
+
+
+void t1Callback() {
+  unsigned long t = micros();
+  Serial.print("t1: ");
+  Serial.println(t);
+}
+
+void t1OnDisable() {
+  t1_interval += t1_interval; 
+  if (t1_interval < T1_INIT) t1_interval = T1_INIT;
+  t1.setInterval(t1_interval);
+  t1.restartDelayed();
+}
+
+void t2Callback() {
+  unsigned long t = micros();
+  Serial.print("t2: ");
+  Serial.print(t);
+  Serial.println(" heartbeat");
+}
+
+
+void setup () {
+  Serial.begin(115200);
+  Serial.println("Scheduler TEST Microsecond Resolution");
+  
+  Serial.println("5 seconds delay");
+  delay(5000);
+   
+  runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks. 
+                      // PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME
+  t1.delay();        // Tasks which need to start delayed, need to be delayed again after startNow();
+
+// Alternatively, tasks should be just enabled at the bottom of setup() method
+//  runner.enableAll();    
+//  t1.delay();
+}
+
+
+void loop () {
+  runner.execute();
+}

BIN
extras/TaskScheduler.doc


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 107 - 113
extras/TaskScheduler.html


+ 1 - 0
keywords.txt

@@ -22,6 +22,7 @@ enableAll	KEYWORD2
 currentTask	KEYWORD2
 currentLts	KEYWORD2
 execute	KEYWORD2
+startNow	KEYWORD2
 allowSleep	KEYWORD2
 enable	KEYWORD2
 enableIfNot	KEYWORD2

+ 2 - 2
library.properties

@@ -1,9 +1,9 @@
 name=TaskScheduler
-version=2.0.1
+version=2.1.0
 author=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com>
 sentence=A light-weight cooperative multitasking library for arduino microcontrollers.
-paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization.
+paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization.
 category=Timing
 url=https://github.com/arkhipenko/TaskScheduler.git
 architectures=*

+ 68 - 51
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);
 }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است