Skip to content

Commit

Permalink
* 3.0.3 (testing) Support for custom Sleep method
Browse files Browse the repository at this point in the history
  • Loading branch information
arkhipenko committed Jun 12, 2019
1 parent b64c65f commit 44240bf
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -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)++;
}
98 changes: 31 additions & 67 deletions src/TaskScheduler.h
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
}

/*
Expand Down Expand Up @@ -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.
Expand All @@ -867,28 +861,24 @@ 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
if (!iCurrent && iHighPriority) iHighPriority->execute();
iCurrentScheduler = this;
#endif // _TASK_PRIORITY


while (iCurrent) {

#ifdef _TASK_PRIORITY
Expand Down Expand Up @@ -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

Expand Down
26 changes: 17 additions & 9 deletions src/TaskSchedulerDeclarations.h
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 44240bf

Please sign in to comment.