From 07b0a9e49fd4c918821099aec940d0927c3f9ff5 Mon Sep 17 00:00:00 2001 From: Anatoli Arkhipenko Date: Tue, 13 Oct 2015 20:18:23 -0400 Subject: TaskScheduler v1.7.0 with the following changes: * introduced callback run counter - callback functions can branch on the iteration number * enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled * disable() returns previous enable state (true if was enabled, false if was already disabled) * introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled * new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left --- extras/TaskScheduler.doc | Bin 65024 -> 121856 bytes extras/TaskScheduler.html | 386 +++++++++++++++++++++++++++++++++--------- extras/TaskScheduler_html.png | Bin 0 -> 41281 bytes 3 files changed, 310 insertions(+), 76 deletions(-) create mode 100644 extras/TaskScheduler_html.png (limited to 'extras') diff --git a/extras/TaskScheduler.doc b/extras/TaskScheduler.doc index f6e88c7..a40aea7 100644 Binary files a/extras/TaskScheduler.doc and b/extras/TaskScheduler.doc differ diff --git a/extras/TaskScheduler.html b/extras/TaskScheduler.html index 7855c7f..96a605e 100644 --- a/extras/TaskScheduler.html +++ b/extras/TaskScheduler.html @@ -6,7 +6,7 @@ - + @@ -15,57 +15,70 @@ - +

Task Scheduler – cooperative multitasking for Arduino microcontrollers

-

Version 1.6.0: -2015-10-01

+

Version 1.7.0: +2015-10-12


-

REQUIREMENT:

+

OVERVIEW:

A lightweight -implementation of the task scheduling supporting:

+implementation of cooperative multitasking (task scheduling) +supporting:

    -
  1. execution period - (n times per second)

    -
  2. number of +

  3. Periodic task + execution (with dynamic execution period in milliseconds)

    +
  4. Number of iterations (n times)

    -
  5. execution of tasks +

  6. Execution of tasks in predefined sequence

    -
  7. dynamic change of - the execution parameters for both tasks and execution schedule

    -
  8. power saving via - entering IDLE sleep mode if no tasks are scheduled to run

    +
  9. Dynamic change of + task execution parameters (frequency, number of iterations, callback + function)

    +
  10. Power saving via + entering IDLE sleep mode between tasks are scheduled to run


-

IDEA:

+

TASK:

“Task” is a container concept that links together:

  1. Execution interval

  2. Number of execution iterations

    -
  3. A piece of code - performing the task activities (callback function)

    +
  4. Piece of code + performing task activities (callback functions)


-

Tasks are linked into -execution chains, which are processed by the “Scheduler” in the -order they are linked.

+

Tasks are linked +into execution chains, which are processed by the “Scheduler” +in the order they are linked.

+


+

+

Each task performs its +function via callback function. Scheduler calls Task’s callback +function periodically until task is disabled or task runs out of +iterations. In addition to “regular” callback, two methods could +be enabled for each task: a callback function invoked once when task +is enabled, and a callback function invoked once when the task is +disabled. Those two special methods allows task to properly initiate +themselves for execution and clean-up after execution is over. +


Tasks are responsible -for supporting cooperative multitasking by being “good neighbors”, -i.e., running their callback functions in a non-blocking way and -releasing control as soon as possible. +for supporting cooperative multitasking by being “good +neighbors”, i.e., running their callback functions quickly in a +non-blocking way and releasing control as soon as possible.


@@ -73,8 +86,8 @@ releasing control as soon as possible. executing Tasks' callback functions in the order the tasks were added to the chain, from first to last. Scheduler stops and exists after processing the chain once in order to allow other statements in the -main code of loop() function to run. This a “scheduling -pass”.

+main code of loop() function to run. This a “scheduling +pass”.


If compiled with @@ -82,11 +95,16 @@ pass”.

enabled, the scheduler will place processor into IDLE sleep mode (for approximately 1 ms, as the timer interrupt will wake it up), after what is determined to be an “idle” pass. An Idle Pass is a pass -through the chain when no Tasks were scheduled to run their callback -functions. This is done to avoid repetitive empty passes through the -chain when no tasks need to be executed. If any of the tasks in the -chain always requires immediate execution (aInterval = 0), then there -will be no end-of-pass delay.

+through the task chain when no Tasks were scheduled to run their +callback functions. This is done to avoid repetitive empty passes +through the chain when no tasks need to be executed. If any of the +tasks in the chain always requires immediate execution (aInterval = +0), then there will be no IDLE sleep between task callback execution.

+


+

+

+Below is the flowchart of a Task lifecycle:

+


Note: Task @@ -178,7 +196,8 @@ tasks are created disabled by default.

Task(unsigned long aInterval, long aIterations, void -(*aCallback)(), Scheduler* aScheduler, bool aEnable);

+(*aCallback)(), Scheduler* aScheduler, bool aEnable, bool +(*aOnEnable)(), void (*aOnDisable)(),);


Constructor @@ -191,24 +210,33 @@ every pass.

  1. aInterval is in - milliseconds

    + milliseconds (default = 0)

  2. aIteration in - number of times, -1 for indefinite execution
    Note: Tasks - do not remember the number of iteration set initially. After the - iterations are done, internal iteration counter is 0. If you need to - perform another set of iterations, you need to set the number of - iterations again.
    Note: Tasks which performed all their - iterations remain active. + number of times, -1 for indefinite execution (default = -1)
    Note: + Tasks do not remember the number of iteration set initially. + After the iterations are done, internal iteration counter is 0. If + you need to perform another set of iterations, you need to set the + number of iterations again.
    Note: Tasks which performed + all their iterations remain active.

  3. aCallback is a - pointer to a void function without parameters

    + pointer to a void callback function without parameters (default = + NULL)

  4. aScheduler – optional reference to existing scheduler. If supplied (not NULL) this task will be appended to the task chain of the current - scheduler). Default=NULL

    + scheduler). (default = NULL)

  5. aEnable – optional. Value of true will create task enabled. - Default = false

    + (default = false)

    +
  6. aOnEnable is a + pointer to a bool callback function without parameters, invoked when + task is enabled. If OnEnable function returns true, task is + enabled. If OnEnable function return false, task + remains disabled (default = NULL)

    +
  7. aOnDisable is a + pointer to a void callback function without parameters, invoked when + task is disabled (default = NULL)


@@ -267,13 +295,35 @@ execution purposes.


+

unsigned long +getRunCounter()

+

Returns +the number of the current run. “Current run” is the number of +times a callback function has been invoked since the last time a task +was enabled.

+

+

NOTE: +The runCounter value is incremented before callback +function is invoked. If a task is checking the runCounter +value within its callback function, then the first run value is 1. +

+

If +task T1 is checking the runCounter value of another task (T2) +, then value = 0 indicates that T2 has not been invoked yet, and +value = 1 indicates that T2 has run once. +

+


+

bool isFirstIteration()

+

Indicates +whether current pass is a first iteration of the task. +

bool isLastIteration()

For -tasks with a defined number of iterations, indicates whether current -pass is a first or a last iteration of the task (respectively). +tasks with a limited number of iterations only, indicates whether +current pass is the last iteration.


@@ -292,6 +342,25 @@ is a task which was enabled and requires execution.


+

Note: +enable() invokes task’s OnEnable method (if not NULL), +which can prepare task for execution. OnEnable must return a +value of true for task to be enabled. If OnEnable +returns false, task remains disabled. OnEnable is +invoked every time enable is called, regardless if task is +already enabled or not. +

+


+

+

bool enableIfNot();

+


+

+

Enables +the task only if it was previously disabled. Returns previous enable +state: true if task was already enabled, and false if +task was disabled.

+


+

void delay();


@@ -301,6 +370,28 @@ the enabled/disabled status of the task.


+

Note: +a delay of 0 (zero) will delay task for current execution +interval. Use forceNextIteration() method to force execution +of the task’s callback during immediate next scheduling pass. +

+


+

+

void +forceNextIteration();

+


+

+

Schedules +the task for execution during immediate next scheduling pass.

+


+

+

Note: +Task’s schedule is adjusted to run from this moment in time. +For instance: if a task was running every 10 seconds: 10, 20, 30, .., +calling forceNextIteration at 44th second of task execution +will make subsequent schedule look like: 44, 54, 64, 74, ..

+


+

void enableDelayed();


@@ -340,26 +431,37 @@ Task is scheduled to run first iteration after a delay = aDelay milliseconds.


-

void disable();

+

bool disable();


Disables the task. Scheduler will not execute this task any longer, even if it remains in the chain. Task can be later re-enabled for execution.

+

Return +previous enabled state: true if task was enabled prior to +calling disable, and false otherwise.

+

If +not NULL, task’s OnDisable method is invoked. OnDisable +is invoked only if task was enabled. Calling disable 3 times +for instance will invoke OnDisable only once.


void set(unsigned -long aInterval, long aIterations, void (*aCallback)());

+long aInterval, long aIterations, void (*aCallback)() , bool +(*aOnEnable)() , void (*aOnDisable)());


Allows -dynamic control of all task execution parameters in one function -call. +dynamic control of task execution parameters in one function call. +

+

Note: +OnEnable and OnDisable parameters can be omitted. In that case +they will be assigned to NULL and not called.


-

Next three “setter” +

Next five “setter” functions allow changes of individual task execution control parameters.

@@ -372,6 +474,12 @@ setIterations (long aIterations)

void setCallback (void (*aCallback)())

+

void +setOnEnable (bool (*aCallback)()) +

+

void +setOnDisable (void (*aCallback)()) +


Note: Next @@ -381,11 +489,12 @@ scheduler. For the situations when one task is changing the interval parameter for the other, setInterval function calls delay explicitly to guarantee schedule change, however it does not enable the task if task is disabled.

-

Note: Tasks -that ran through all their allocated iterations are disabled. -SetIterations() -method DOES NOT -enable the task. Either enable explicitly, or use restart methods. +

Note: Tasks that +ran through all their allocated iterations are disabled. +SetIterations() method DOES NOT enable the task. Either +enable explicitly, or use restart methods. +

+



@@ -427,16 +536,17 @@ addTask(Task& aTask)

Adds task aTask to the execution queue (or chain) of tasks by appending it to the end of the chain. If two tasks are scheduled for execution, -the sequence will match the order tasks are appended to the chain. -However, in reality, due to different timing of task execution, the -actual order will be different. +the sequence will match the order in which tasks were appended to the +chain. However, in reality, due to different timing of task +execution, the actual order may be different.

Note: -Currently, changing the execution dynamically is not supported. +Currently, changing the execution sequence in a chain dynamically +is not supported.

If -you need to reorder the queue – initialize the scheduler and re-add -the tasks in a different order. +you need to reorder the chain sequence – initialize the scheduler +and re-add the tasks in a different order.


void deleteTask(Task& aTask)

@@ -489,8 +599,8 @@ currentTask()

Returns reference to the task, currently executing via execute() loop. -Could be used by callback functions to identify which of the -

+Could be used by callback functions to identify which of the Tasks +invoked callback function.


void execute()


@@ -774,16 +884,16 @@ determine the length of ultrasonic pulse.

<PinChangeInt.h>


#define TRIGGERPIN 5
#define ECHOPIN 6

Output<TRIGGERPIN> pTrigger;
Input<ECHOPIN> pEcho;

Scheduler -r;

Task tMeasure(1000, -1, &measureCallback);
Task -tDisplay(1000, -1, &displayCallback);
Task tPing(0, 1, -&pingCalcCallback);


volatile bool pulseBusy = -false;
volatile bool pulseTimeout = false;
volatile unsigned -long pulseStart = 0;
volatile unsigned long pulseStop = -0;
volatile unsigned long pingDistance = 0;


void -pingTrigger(unsigned long aTimeout) {
if (pulseBusy) return; // -do not trigger if in the middle of a pulse
if (pEcho == HIGH) -return; // do not trigger if ECHO pin is high

pulseBusy = -true;
pulseTimeout = false;

+r;

Task tMeasure(1000, -1, &measureCallback, &r, +true);
Task tDisplay(1000, -1, &displayCallback, &r, +true);
Task tPing(0, 1, &pingCalcCallback, &r, +false);


volatile bool pulseBusy = false;
volatile bool +pulseTimeout = false;
volatile unsigned long pulseStart = +0;
volatile unsigned long pulseStop = 0;
volatile unsigned long +pingDistance = 0;


void pingTrigger(unsigned long aTimeout) +{
if (pulseBusy) return; // do not trigger if in the middle of +a pulse
if (pEcho == HIGH) return; // do not trigger if ECHO pin +is high

pulseBusy = true;
pulseTimeout = false;


pTrigger = LOW;
delayMicroseconds(4);
pTrigger = HIGH;

tPing.setInterval (aTimeout);

delayMicroseconds(10);
@@ -832,10 +942,134 @@ Wait for the measurement to
void measureCallbackWait() {
if cm);
Serial.println(d);

}

void setup() {
// put your setup code here, to run once:

Serial.begin(115200);


pTrigger = LOW;
pEcho = -LOW;

r.init();
r.addTask(tDisplay);
-r.addTask(tMeasure);
r.addTask(tPing);

-r.enableAll();
tPing.disable();
}

void loop() {
-// put your main code here, to run repeatedly:
r.execute();
}

+LOW;

}

void loop() {
// put your main code here, +to run repeatedly:
r.execute();
}

+

+
    +
  1. USING + ONENABLE AND ONDISBALE METHODS +

    +
+


+

+

Consider +a task to flash onboard LED for 5 seconds with random frequency. Task +should be repeated every 30 seconds indefinitely. Since frequency is +random, there are two challenges:

+
    +
      +
    1. We + need to make sure LED is turned OFF at the last iteration

      +
    2. We + need to calculate random frequency every time

      +
    +
+


+

+

Below +is the implementation using TaskScheduler +

+


+

+


#include +<TaskScheduler.h>

#define LEDPIN 13


Scheduler +ts;

+

+

Task +tWrapper(30000L, -1, &WrapperCallback, &ts, true);

+

Task +tBlink(5000, 1, NULL, &ts, false, &BlinkOnEnable, +&BlinkOnDisable);

+

Task +tLED(0, -1, NULL, &ts, false, NULL, &LEDOff);

+


+

+

void +WrapperCallback() {

+

tBlink.restartDelayed(); + // LED blinking is initiated +

+

+ //every 30 +seconds for 5 seconds

+

}

+


+

+


+

+

// +Upon being enabled, tBlink will define the parameters

+

// +and enable LED blinking task, which actually controls

+

// +the hardware (LED in this example)

+

bool +BlinkOnEnable() {

+

tLED.setInterval( +500 + random(501) );

+

tLED.setCallback( +&LEDOn);

+

tLED.enable();

+


+

+

return +true; // Task should be enabled

+

}

+


+

+

// +tBlink does not really need a callback function

+

// +since it just waits for 5 seconds for the first +

+

// +and only iteration to occur. Once the iteration

+

// +takes place, tBlink is disabled by the Scheduler, +

+

// +thus executing its OnDisable method below.

+


+

+

void +BlinkOnDisable() {

+

tLED.disable();

+

}

+


+

+

void +LEDOn () {

+

digitalWrite(LEDPIN, +HIGH);

+

tLED.setCallback( +&LEDOff);

+

}

+


+

+

void +LEDOff () {

+

digitalWrite(LEDPIN, +LOW);

+

tLED.setCallback( +&LEDOn);

+

}

+


+

+

// +Note that LEDOff method serves as OnDisable method

+

// +to make sure the LED is turned off when the tBlink

+

// +task finishes (or disabled ahead of time)

+


+

+

void +setup() {
// put your setup code here, to run once:
}

void +loop() {
// put your main code here, to run repeatedly:
+ts.execute();
}

+


+

+


\ No newline at end of file diff --git a/extras/TaskScheduler_html.png b/extras/TaskScheduler_html.png new file mode 100644 index 0000000..febb8c7 Binary files /dev/null and b/extras/TaskScheduler_html.png differ -- cgit v1.2.3