Browse Source

* 3.0.3 (testing) Support for custom Sleep method

Anatoli Arkhipenko 6 năm trước cách đây
mục cha
commit
44240bfd0a

+ 157 - 0
examples/Scheduler_example23_IDLE_Callback/Scheduler_example23_IDLE_Callback.ino

@@ -0,0 +1,157 @@
+/*
+   An example of using scheduler's customer sleep callback method
+
+   An empty loop is executed for 10 seconds with a 10 ms. interval
+   The first time it is excuted with an empty sleep callback, and
+   the second time with a 1 ms delay in the custom callback
+
+  RESULTS:
+
+  Arduino Nano:
+=================================
+  Testing empty sleep callback...
+  cEmptyCallback=1001
+  cEmptyTotal=423866
+  Testing 1 ms delayed sleep callback...
+  cDelayCallback=1001
+  cDelayTotal=10669
+
+
+  ESP8266 at 80MHz
+=================================
+  Testing empty sleep callback...
+  cEmptyCallback=1001
+  cEmptyTotal=278101
+  Testing 1 ms delayed sleep callback...
+  cDelayCallback=1001
+  cDelayTotal=10493
+
+
+  ESP8266 at 160MHz
+=================================
+  Testing empty sleep callback...
+  cEmptyCallback=1001
+  cEmptyTotal=546041
+  Testing 1 ms delayed sleep callback...
+  cDelayCallback=1001
+  cDelayTotal=10746
+
+
+  Maple Mini STM32 board at 70MHz -O3 code optimization
+==================================
+  Testing empty sleep callback...
+  cEmptyCallback=1001
+  cEmptyTotal=2689973
+  Testing 1 ms delayed sleep callback...
+  cDelayCallback=1001
+  cDelayTotal=10958
+
+
+  esp32 at 240MHz
+==================================
+  Testing empty sleep callback...
+  cEmptyCallback=1001
+  cEmptyTotal=492851
+  Testing 1 ms delayed sleep callback...
+  cDelayCallback=1001
+  cDelayTotal=11002
+
+
+*/
+
+#define _TASK_SLEEP_ON_IDLE_RUN
+#include <TaskScheduler.h>
+
+Scheduler ts;
+
+// Callback methods prototypes
+void Count();
+bool tEmptyOn();
+void tEmptyOff();
+bool tDelayOn();
+void tDelayOff();
+
+// sleep methods prototypes
+void sEmpty(unsigned long aT);
+void sDelay(unsigned long aT);
+
+// Tasks
+Task tCount ( 10, TASK_FOREVER, &Count, &ts, false );
+Task tEmpty ( 10000, TASK_ONCE, NULL, &ts, false, &tEmptyOn, &tEmptyOff );
+Task tDelay ( 10000, TASK_ONCE, NULL, &ts, false, &tDelayOn, &tDelayOff );
+
+
+volatile unsigned long cEmptyCallback, cEmptyTotal, cDelayCallback, cDelayTotal;
+volatile unsigned long *cCB, *cTL;
+
+void setup() {
+  Serial.begin(115200);
+  delay(5000);
+  Serial.println("Start counting...");
+  ts.setSleepMethod( &sEmpty );
+  tEmpty.restartDelayed();
+}
+
+
+void sEmpty(unsigned long aT) {
+}
+
+void sDelay(unsigned long aT) {
+  delay(1);
+}
+
+bool tEmptyOn() {
+  Serial.println("Testing empty sleep callback...");
+  cCB = &cEmptyCallback;
+  cTL = &cEmptyTotal;
+
+  *cCB = 0;
+  *cTL = 0;
+
+  tCount.restart();
+
+  return true;
+}
+
+void tEmptyOff() {
+  tCount.disable();
+
+  Serial.print("cEmptyCallback="); Serial.println(*cCB);
+  Serial.print("cEmptyTotal="); Serial.println(*cTL);
+
+  ts.setSleepMethod( &sDelay );
+  tDelay.restartDelayed();
+}
+
+
+bool tDelayOn() {
+  Serial.println("Testing 1 ms delayed sleep callback...");
+  cCB = &cDelayCallback;
+  cTL = &cDelayTotal;
+
+  *cCB = 0;
+  *cTL = 0;
+
+  tCount.restart();
+
+  return true;
+}
+
+void tDelayOff() {
+  tCount.disable();
+
+  Serial.print("cDelayCallback="); Serial.println(*cCB);
+  Serial.print("cDelayTotal="); Serial.println(*cTL);
+}
+
+void Count() {
+  (*cCB)++;
+}
+
+
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  ts.execute();
+  (*cTL)++;
+}

+ 31 - 67
src/TaskScheduler.h

@@ -1,5 +1,5 @@
 // Cooperative multitasking library for Arduino
-// Copyright (c) 2015-2017 Anatoli Arkhipenko
+// Copyright (c) 2015-2019 Anatoli Arkhipenko
 //
 // Changelog:
 // v1.0.0:
@@ -157,7 +157,7 @@
 // and should be used in the main sketch depending on the functionality required
 //
 // #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
-// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
+// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
 // #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
@@ -181,28 +181,10 @@
 
 
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
+#include "TaskSchedulerSleepMethods.h"
 
-#ifdef ARDUINO_ARCH_AVR
-#include <avr/sleep.h>
-#include <avr/power.h>
-#endif  // ARDUINO_ARCH_AVR
-
-#ifdef ARDUINO_ARCH_ESP8266
-#define _TASK_ESP8266_DLY_THRESHOLD 200L
-extern "C" {
-#include "user_interface.h"
-}
-#endif //ARDUINO_ARCH_ESP8266
-
-#ifdef ARDUINO_ARCH_ESP32
-#define _TASK_ESP8266_DLY_THRESHOLD 200L
-#warning _TASK_SLEEP_ON_IDLE_RUN for ESP32 cannot use light sleep mode but a standard delay for 1 ms
-#endif  // ARDUINO_ARCH_ESP32
-
-#ifdef ARDUINO_ARCH_STM32F1
-#include <libmaple/pwr.h>
-#include <libmaple/scb.h>
-#endif  // ARDUINO_ARCH_STM32F1
+  Scheduler* iSleepScheduler;
+  SleepCallback iSleepMethod;
 
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 
@@ -665,6 +647,9 @@ void* Task::getLtsPointer() { return iLTS; }
  */
 Scheduler::Scheduler() {
     init();
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+	setSleepMethod(&SleepMethod);
+#endif // _TASK_SLEEP_ON_IDLE_RUN
 }
 
 /*
@@ -859,6 +844,15 @@ void* Scheduler::currentLts() { return iCurrent->iLTS; }
 bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); }
 #endif  // _TASK_TIMECRITICAL
 
+	
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+void  Scheduler::setSleepMethod( SleepCallback aCallback ) {
+	if ( aCallback != NULL ) {
+		iSleepScheduler = this;
+		iSleepMethod = aCallback;
+	}
+}
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 
 /** Makes one pass through the execution chain.
@@ -867,20 +861,17 @@ bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); }
  * Different pseudo "priority" could be achieved
  * by running task more frequently
  */
+
 bool Scheduler::execute() {
     bool     idleRun = true;
     register unsigned long m, i;  // millis, interval;
-
-#ifdef _TASK_SLEEP_ON_IDLE_RUN
-#if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32)
-      unsigned long t1 = micros();
-      unsigned long t2 = 0;
-#endif  // ARDUINO_ARCH_ESP8266
-#endif // _TASK_SLEEP_ON_IDLE_RUN
+	unsigned long tStart, tFinish;
 
 	Task *nextTask;  // support for deleting the task in the onDisable method
     iCurrent = iFirst;
 
+	tStart = micros();  // Scheduling pass start time in microseconds. 
+
 #ifdef _TASK_PRIORITY
     // If lower priority scheduler does not have a single task in the chain
     // the higher priority scheduler still has to have a chance to run
@@ -888,7 +879,6 @@ bool Scheduler::execute() {
         iCurrentScheduler = this;
 #endif  // _TASK_PRIORITY
 
-
     while (iCurrent) {
 
 #ifdef _TASK_PRIORITY
@@ -966,48 +956,22 @@ bool Scheduler::execute() {
             }
         } while (0);    //guaranteed single run - allows use of "break" to exit
         iCurrent = nextTask;
+		
 #if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32)
         yield();
-#endif  // ARDUINO_ARCH_ESP8266
+#endif  // ARDUINO_ARCH_ESPxx
+		
     }
 
+    tFinish = micros(); // Scheduling pass end time in microseconds.
+	
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
     if (idleRun && iAllowSleep) {
-
-#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards.
-      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 // ARDUINO_ARCH_AVR
-
-#ifdef CORE_TEENSY
-    asm("wfi");
-#endif //CORE_TEENSY
-
-#ifdef ARDUINO_ARCH_ESP8266
-// to do: find suitable sleep function for esp8266
-      t2 = micros() - t1;
-      if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1);   // ESP8266 implementation of delay() uses timers and yield
-#endif  // ARDUINO_ARCH_ESP8266
-
-#ifdef ARDUINO_ARCH_ESP32
-//TODO: find a correct light sleep implementation for ESP32
-    // esp_sleep_enable_timer_wakeup(1000); //1 ms
-    // int ret= esp_light_sleep_start();
-      t2 = micros() - t1;
-      if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1);
-#endif  // ARDUINO_ARCH_ESP32
-
-#ifdef ARDUINO_ARCH_STM32F1
-	  // Now go into stop mode, wake up on interrupt.
-	  // Systick interrupt will run every 1 milliseconds.
-	  asm("    wfi");
-#endif  // ARDUINO_ARCH_STM32
-
+		if ( iSleepScheduler == this ) { // only one scheduler should make the MC go to sleep. 
+			if ( iSleepMethod != NULL ) {
+				(*iSleepMethod)( tFinish-tStart );
+			}
+		}
     }
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 

+ 17 - 9
src/TaskSchedulerDeclarations.h

@@ -1,5 +1,5 @@
 // Cooperative multitasking library for Arduino
-// Copyright (c) 2015-2017 Anatoli Arkhipenko
+// Copyright (c) 2015-2019 Anatoli Arkhipenko
 
 #include <stddef.h>
 #include <stdint.h>
@@ -12,7 +12,7 @@
 // and should be used in the main sketch depending on the functionality required
 //
 // #define _TASK_TIMECRITICAL      // Enable monitoring scheduling overruns
-// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
+// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
 // #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
@@ -24,6 +24,8 @@
 // #define _TASK_TIMEOUT           // Support for overall task timeout
 // #define _TASK_OO_CALLBACKS      // Support for dynamic callback method binding
 
+class Scheduler;
+
 #ifdef _TASK_DEBUG
     #define _TASK_SCOPE  public
 #else
@@ -39,14 +41,11 @@
 #endif
 
 #ifdef _TASK_PRIORITY
-    class Scheduler;
     extern Scheduler* iCurrentScheduler;
 #endif // _TASK_PRIORITY
 
-#if !defined(ARDUINO)
-	extern unsigned long micros(void);
-	extern unsigned long millis(void);
-#endif
+extern unsigned long micros(void);
+extern unsigned long millis(void);
 
 
 #ifdef _TASK_INLINE
@@ -104,7 +103,15 @@ typedef std::function<bool()> TaskOnEnable;
 typedef void (*TaskCallback)();
 typedef void (*TaskOnDisable)();
 typedef bool (*TaskOnEnable)();
-#endif
+#endif  // _TASK_STD_FUNCTION
+
+
+#ifdef _TASK_SLEEP_ON_IDLE_RUN
+  typedef void (*SleepCallback)( unsigned long aDuration );
+
+  extern Scheduler* iSleepScheduler;
+  extern SleepCallback iSleepMethod;
+#endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 typedef struct  {
     bool  enabled    : 1;           // indicates that task is enabled or not.
@@ -278,6 +285,7 @@ class Scheduler {
 
 #ifdef _TASK_SLEEP_ON_IDLE_RUN
     INLINE void allowSleep(bool aState = true);
+    INLINE void setSleepMethod( SleepCallback aCallback );
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 #ifdef _TASK_LTS_POINTER
@@ -297,7 +305,7 @@ class Scheduler {
     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.
+    bool        iAllowSleep;                      // indication if putting MC to IDLE_SLEEP mode is allowed by the program at this time.
 #endif  // _TASK_SLEEP_ON_IDLE_RUN
 
 #ifdef _TASK_PRIORITY

+ 97 - 0
src/TaskSchedulerSleepMethods.h

@@ -0,0 +1,97 @@
+// Cooperative multitasking library for Arduino
+// Copyright (c) 2015-2019 Anatoli Arkhipenko
+
+#ifndef _TASKSCHEDULERSLEEPMETHODS_H_
+#define _TASKSCHEDULERSLEEPMETHODS_H_
+
+		
+#if defined( ARDUINO_ARCH_AVR ) // Could be used only for AVR-based boards.
+
+#include <avr/sleep.h>
+#include <avr/power.h>
+
+void SleepMethod( unsigned long aDuration ) {
+  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. */
+}
+// ARDUINO_ARCH_AVR
+
+
+#elif defined( CORE_TEENSY )
+void SleepMethod( unsigned long aDuration ) {
+  asm("wfi");
+}
+//CORE_TEENSY
+
+
+#elif defined( ARDUINO_ARCH_ESP8266 )
+
+#define _TASK_ESP8266_DLY_THRESHOLD 200L
+extern "C" {
+#include "user_interface.h"
+}
+
+void SleepMethod( unsigned long aDuration ) {
+// to do: find suitable sleep function for esp8266
+      if ( aDuration < _TASK_ESP8266_DLY_THRESHOLD) delay(1);   // ESP8266 implementation of delay() uses timers and yield
+}
+// ARDUINO_ARCH_ESP8266
+
+
+#elif defined( ARDUINO_ARCH_ESP32 )
+
+#define _TASK_ESP32_DLY_THRESHOLD 200L
+#warning _TASK_SLEEP_ON_IDLE_RUN for ESP32 cannot use light sleep mode but a standard delay for 1 ms
+extern unsigned long tStart, tFinish;
+
+void SleepMethod( unsigned long aDuration ) {
+//TODO: find a correct light sleep implementation for ESP32
+    // esp_sleep_enable_timer_wakeup(1000); //1 ms
+    // int ret= esp_light_sleep_start();
+      if ( aDuration < _TASK_ESP32_DLY_THRESHOLD) delay(1);   // ESP8266 implementation of delay() uses timers and yield
+
+}
+// ARDUINO_ARCH_ESP32
+
+
+#elif defined( ARDUINO_ARCH_STM32F1 )
+
+#include <libmaple/pwr.h>
+#include <libmaple/scb.h>
+
+void SleepMethod( unsigned long aDuration ) {
+	  // Now go into stop mode, wake up on interrupt.
+	  // Systick interrupt will run every 1 milliseconds.
+	  asm("    wfi");
+}
+// ARDUINO_ARCH_STM32
+
+
+#elif defined( ENERGIA_ARCH_MSP432 )
+
+void SleepMethod( unsigned long aDuration ) {
+    delay(1);
+}
+// ENERGIA_ARCH_MSP432
+
+
+#elif defined( ENERGIA_ARCH_MSP430 )
+
+void SleepMethod( unsigned long aDuration ) {
+    sleep(1);
+}
+// ENERGIA_ARCH_MSP430
+
+
+#else
+void SleepMethod( unsigned long aDuration ) {
+}
+
+#endif //  SLEEP METHODS
+
+#endif // _TASKSCHEDULERSLEEPMETHODS_H_