diff options
| author | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-10-13 20:18:23 -0400 |
|---|---|---|
| committer | Anatoli Arkhipenko <arkhipenko@hotmail.com> | 2015-10-13 20:18:23 -0400 |
| commit | 07b0a9e49fd4c918821099aec940d0927c3f9ff5 (patch) | |
| tree | 03da52d7d6b20c71c6afbbbe1b61da1ab2874d71 | |
| parent | 332d0cf661ff7d86640773168113ffe5e47de505 (diff) | |
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
| -rw-r--r-- | README | 39 | ||||
| -rw-r--r-- | examples/Scheduler_example3/Scheduler_example3.ino | 60 | ||||
| -rw-r--r-- | extras/TaskScheduler.doc | bin | 65024 -> 121856 bytes | |||
| -rw-r--r-- | extras/TaskScheduler.html | 386 | ||||
| -rw-r--r-- | extras/TaskScheduler_html.png | bin | 0 -> 41281 bytes | |||
| -rw-r--r-- | keywords.txt | 5 | ||||
| -rw-r--r-- | library.properties | 2 | ||||
| -rw-r--r-- | src/TaskScheduler.cpp | 48 | ||||
| -rw-r--r-- | src/TaskScheduler.h | 28 |
9 files changed, 450 insertions, 118 deletions
@@ -1,33 +1,16 @@ Task Scheduler – cooperative multitasking for Arduino microcontrollers -Version 1.6.0: 2015-09-22 +Version 1.7.0: 2015-10-12 -REQUIREMENT: -A lightweight implementation of the task scheduling supporting: -execution period (n times per second) -number of iterations (n times) -execution of tasks in predefined sequence -dynamic change of the execution parameters for both tasks and execution schedule -power saving via entering IDLE sleep mode if no tasks are scheduled to run +OVERVIEW: +A lightweight implementation of cooperative multitasking (task scheduling) supporting: +1. Periodic task execution (with dynamic execution period in milliseconds) +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) +5. Power saving via entering IDLE sleep mode between tasks are scheduled to run -IDEA: -“Task” is a container concept that links together: -Execution interval -Number of execution iterations -A piece of code performing the task activities (callback function) - -Tasks are linked into execution chains, which are processed by the “Scheduler” in the order they are linked. - -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. - -“Scheduler” is 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”. - -If compiled with _TASK_SLEEP_ON_IDLE_RUN 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. - -Note: Task Scheduler uses millis() to determine if tasks are ready to be invoked. Therefore, if you put your device to any “deep” sleep mode disabling timer interrupts, the millis() count will be suspended, leading to effective suspension of scheduling. Upon wake up, active tasks need to be re-enabled, which will effectively reset their internal time scheduling variables to the new value of millis(). Time spent in deep sleep mode should be considered “frozen”, i.e., if a task was scheduled to run in 1 second from now, and device was put to sleep for 5 minutes, upon wake up, the task will still be scheduled 1 second from the time of wake up. Executing enable() function on this tasks will make it run as soon as possible. This is a concern only for tasks which are required to run in a truly periodical manner (in absolute time terms). - - Changelog: 2015-02-24 - Initial release 2015-02-28 - added delay() and disableOnLastIteration() functions @@ -52,3 +35,9 @@ v1.6.0: 2015-09-22 - deprecated disableOnLastIteration method as a result 2015-10-01 - made version numbers semver compliant (documentation only) +v1.7.0: + 2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number. + 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled. + 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) + 2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled + 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left diff --git a/examples/Scheduler_example3/Scheduler_example3.ino b/examples/Scheduler_example3/Scheduler_example3.ino new file mode 100644 index 0000000..d683855 --- /dev/null +++ b/examples/Scheduler_example3/Scheduler_example3.ino @@ -0,0 +1,60 @@ +#define _TASK_SLEEP_ON_IDLE_RUN +#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.doc b/extras/TaskScheduler.doc Binary files differindex f6e88c7..a40aea7 100644 --- a/extras/TaskScheduler.doc +++ b/extras/TaskScheduler.doc 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 @@ <META NAME="GENERATOR" CONTENT="LibreOffice 3.5 (Linux)"> <META NAME="CREATED" CONTENT="20150206;16300000"> <META NAME="CHANGEDBY" CONTENT="Anatoli Arkhipenko"> - <META NAME="CHANGED" CONTENT="20150921;21210000"> + <META NAME="CHANGED" CONTENT="20151012;23280000"> <META NAME="Info 1" CONTENT=""> <META NAME="Info 2" CONTENT=""> <META NAME="Info 3" CONTENT=""> @@ -15,57 +15,70 @@ <!-- @page { margin: 0.79in } P { margin-bottom: 0.08in; direction: ltr; color: #000000; widows: 0; orphans: 0 } - P.western { font-family: "Liberation Serif", "Times New Roman", serif; font-size: 12pt; so-language: en-US } + P.western { font-family: "Liberation Serif", "MS PMincho", serif; font-size: 12pt; so-language: en-US } P.cjk { font-family: "WenQuanYi Micro Hei", "MS Mincho"; font-size: 12pt; so-language: zh-CN } P.ctl { font-family: "Lohit Hindi", "MS Mincho"; font-size: 12pt; so-language: hi-IN } --> </STYLE> </HEAD> -<BODY LANG="en-US" TEXT="#000000" DIR="LTR"> +<BODY LANG="en-US" TEXT="#000000" BGCOLOR="#ffffff" DIR="LTR"> <P CLASS="western" STYLE="margin-bottom: 0in"><B>Task Scheduler – cooperative multitasking for Arduino microcontrollers</B></P> -<P CLASS="western" STYLE="margin-bottom: 0in"><B>Version 1.6.0: -2015-10-01</B></P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>Version 1.7.0: +2015-10-12</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> -<P CLASS="western" STYLE="margin-bottom: 0in"><B>REQUIREMENT</B>:</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>OVERVIEW</B>:</P> <P CLASS="western" STYLE="margin-bottom: 0in">A lightweight -implementation of the task scheduling supporting:</P> +implementation of cooperative multitasking (task scheduling) +supporting:</P> <OL> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">execution period - (n times per second)</P> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">number of + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Periodic task + execution (with dynamic execution period in milliseconds)</P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Number of iterations (n times)</P> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">execution of tasks + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Execution of tasks in predefined sequence</P> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">dynamic change of - the execution parameters for both tasks and execution schedule</P> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">power saving via - entering IDLE sleep mode if no tasks are scheduled to run</P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Dynamic change of + task execution parameters (frequency, number of iterations, callback + function)</P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Power saving via + entering IDLE sleep mode between tasks are scheduled to run</P> </OL> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> -<P CLASS="western" STYLE="margin-bottom: 0in"><B>IDEA</B>:</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>TASK</B>:</P> <P CLASS="western" STYLE="margin-bottom: 0in">“Task” is a container concept that links together:</P> <OL> <LI><P CLASS="western" STYLE="margin-bottom: 0in">Execution interval</P> <LI><P CLASS="western" STYLE="margin-bottom: 0in">Number of execution iterations</P> - <LI><P CLASS="western" STYLE="margin-bottom: 0in">A piece of code - performing the task activities (callback function)</P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">Piece of code + performing task activities (callback functions)</P> </OL> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> -<P CLASS="western" STYLE="margin-bottom: 0in">Tasks are linked into -execution chains, which are processed by the “Scheduler” in the -order they are linked.</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>Tasks</B> are linked +into execution <B>chains</B>, which are processed by the “Scheduler” +in the order they are linked.</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-bottom: 0in">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. +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in">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 <B>cooperative</B> <B>multitasking</B> by being “good +neighbors”, i.e., running their callback functions quickly in a +non-blocking way and releasing control as soon as possible. </P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> @@ -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 <B>loop()</B> function to run. This a “scheduling -pass”.</P> +main code of <B>loop()</B> function to run. This <B>a “scheduling +pass”.</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in">If compiled with @@ -82,11 +95,16 @@ pass”.</P> 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.</P> +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.</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid"> +<B>Below is the flowchart of a Task lifecycle:</B></P> +<P CLASS="western" STYLE="margin-bottom: 0in"><IMG SRC="TaskScheduler_html.png" NAME="graphics1" ALIGN=BOTTOM WIDTH=664 HEIGHT=769 BORDER=0></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>Task @@ -178,7 +196,8 @@ tasks are created <B>disabled</B> by default.</P> </P> <P CLASS="western" STYLE="margin-bottom: 0in; page-break-after: avoid"> <B>Task(unsigned long aInterval, long aIterations, void -(*aCallback)(), Scheduler* aScheduler, bool aEnable);</B></P> +(*aCallback)(), Scheduler* aScheduler, bool aEnable, bool +(*aOnEnable)(), void (*aOnDisable)(),);</B></P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Constructor @@ -191,24 +210,33 @@ every pass. </P> <OL> <LI><P CLASS="western" STYLE="margin-bottom: 0in">aInterval is in - milliseconds</P> + milliseconds (<B>default = 0)</B></P> <LI><P CLASS="western" STYLE="margin-bottom: 0in">aIteration in - number of times, -1 for indefinite execution<BR><B>Note: </B>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. <BR><B>Note: </B>Tasks which performed all their - iterations remain active. + number of times, -1 for indefinite execution (<B>default = -1)</B><BR><B>Note: + </B>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. <BR><B>Note: </B>Tasks which performed + all their iterations remain active. </P> <LI><P CLASS="western" STYLE="margin-bottom: 0in">aCallback is a - pointer to a void function without parameters</P> + pointer to a void callback function without parameters (<B>default = + NULL)</B></P> <LI><P CLASS="western" STYLE="margin-bottom: 0in">aScheduler – <B>optional</B> reference to existing scheduler. If supplied (not NULL) this task will be appended to the task chain of the current - scheduler). <B>Default=NULL</B></P> + scheduler). (<B>default = NULL)</B></P> <LI><P CLASS="western" STYLE="margin-bottom: 0in">aEnable – <B>optional</B>. Value of <B>true </B>will create task enabled. - <B>Default = false</B></P> + (<B>default = false)</B></P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">aOnEnable is a + pointer to a bool callback function without parameters, invoked when + task is enabled. If OnEnable function returns <B>true</B>, task is + enabled. If <B>OnEnable</B> function return <B>false</B>, task + remains disabled (<B>default = NULL)</B></P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in">aOnDisable is a + pointer to a void callback function without parameters, invoked when + task is disabled (<B>default = NULL)</B></P> </OL> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> @@ -267,13 +295,35 @@ execution purposes. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>unsigned long +getRunCounter()</B></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">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. <BR><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>NOTE: +</B>The <B>runCounter</B> value is incremented <I>before</I> callback +function is invoked. If a task is checking the <B>runCounter</B> +value within its callback function, then the first run value is 1. +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If +task T1 is checking the <B>runCounter</B> 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. +</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>bool isFirstIteration()</B></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Indicates +whether current pass is a first iteration of the task. +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>bool isLastIteration()</B></P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">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. </P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> @@ -292,6 +342,25 @@ is a task which was enabled and requires execution. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note: +</B>enable() invokes task’s <B>OnEnable</B> method (if not NULL), +which can prepare task for execution. <B>OnEnable</B> must return a +value of <B>true</B> for task to be enabled. If <B>OnEnable</B> +returns <B>false</B>, task remains disabled. <B>OnEnable</B> is +invoked every time <B>enable</B> is called, regardless if task is +already enabled or not. +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool enableIfNot();</B></P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Enables +the task only if it was previously disabled. Returns previous enable +state: <B>true</B> if task was already enabled, and <B>false</B> if +task was disabled.</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>void delay();</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> @@ -301,6 +370,28 @@ the enabled/disabled status of the task. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note: +</B>a delay of 0 (zero) will delay task for current execution +interval. Use <B>forceNextIteration() </B>method to force execution +of the task’s callback during immediate next scheduling pass. +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>void +forceNextIteration();</B></P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Schedules +the task for execution during immediate next scheduling pass.</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note: +</B>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 <B>forceNextIteration </B>at 44th second of task execution +will make subsequent schedule look like: 44, 54, 64, 74, ..</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>void enableDelayed();</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> @@ -340,26 +431,37 @@ Task is scheduled to run first iteration after a delay = <B>aDelay</B> milliseconds.</P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> -<P CLASS="western" STYLE="margin-bottom: 0in"><B>void disable();</B></P> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>bool disable();</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">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. </P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Return +previous enabled state: <B>true</B> if task was enabled prior to +calling disable, and <B>false</B> otherwise.</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">If +not NULL, task’s <B>OnDisable</B> method is invoked. <B>OnDisable</B> +is invoked only if task was enabled. Calling <B>disable</B> 3 times +for instance will invoke <B>OnDisable</B> only once.</P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>void set(unsigned -long aInterval, long aIterations, void (*aCallback)());</B></P> +long aInterval, long aIterations, void (*aCallback)() , bool +(*aOnEnable)() , void (*aOnDisable)());</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Allows -dynamic control of all task execution parameters in one function -call. +dynamic control of task execution parameters in one function call. +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note</B><B>: +</B>OnEnable and OnDisable parameters can be omitted. In that case +they will be assigned to NULL and not called. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> -<P CLASS="western" STYLE="margin-bottom: 0in">Next three “setter” +<P CLASS="western" STYLE="margin-bottom: 0in">Next five “setter” functions allow changes of individual task execution control parameters. </P> @@ -372,6 +474,12 @@ setIterations (long aIterations) </B> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>void setCallback (void (*aCallback)()) </B> </P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>void +setOnEnable (bool (*aCallback)()) </B> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>void +setOnDisable (void (*aCallback)()) </B> +</P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>Next @@ -381,11 +489,12 @@ scheduler. For the situations when one task is changing the interval parameter for the other, <B>setInterval</B> function calls <B>delay </B>explicitly to guarantee schedule change, however it <B>does not </B>enable the task if task is disabled.</P> -<P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B><SPAN STYLE="font-weight: normal">Tasks -that ran through all their allocated iterations are disabled. -</SPAN><B>SetIterations()</B><SPAN STYLE="font-weight: normal"> -method </SPAN><B>DOES NOT</B><SPAN STYLE="font-weight: normal"> -enable the task. Either enable explicitly, or use restart methods. </SPAN> +<P CLASS="western" STYLE="margin-bottom: 0in"><B>Note: </B>Tasks that +ran through all their allocated iterations are disabled. +<B>SetIterations()</B> method <B>DOES NOT</B> enable the task. Either +<B>enable</B> explicitly, or use <B>restart</B> methods. +</P> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> @@ -427,16 +536,17 @@ addTask(Task& aTask)</B></P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">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. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><B>Note: -</B>Currently, changing the execution dynamically is not supported. +</B>Currently, changing the execution sequence in a chain dynamically +is not supported. </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">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. </P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR><B>void deleteTask(Task& aTask)</B></P> @@ -489,8 +599,8 @@ currentTask()<BR></B><BR> </P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in">Returns reference to the task, currently executing via <B>execute()</B> loop. -Could be used by callback functions to identify which of the -</P> +Could be used by callback functions to identify which of the Tasks +invoked callback function.</P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR><B>void execute()</B></P> <P CLASS="western" STYLE="margin-bottom: 0in"><BR> </P> @@ -774,16 +884,16 @@ determine the length of ultrasonic pulse.</FONT></P> <PinChangeInt.h><BR><BR><BR>#define TRIGGERPIN 5<BR>#define ECHOPIN 6<BR><BR>Output<TRIGGERPIN> pTrigger;<BR>Input<ECHOPIN> pEcho;<BR><BR>Scheduler -r;<BR><BR>Task tMeasure(1000, -1, &measureCallback);<BR>Task -tDisplay(1000, -1, &displayCallback);<BR>Task tPing(0, 1, -&pingCalcCallback);<BR><BR><BR>volatile bool pulseBusy = -false;<BR>volatile bool pulseTimeout = false;<BR>volatile unsigned -long pulseStart = 0;<BR>volatile unsigned long pulseStop = -0;<BR>volatile unsigned long pingDistance = 0;<BR><BR><BR>void -pingTrigger(unsigned long aTimeout) {<BR> if (pulseBusy) return; // -do not trigger if in the middle of a pulse<BR> if (pEcho == HIGH) -return; // do not trigger if ECHO pin is high<BR> <BR> pulseBusy = -true;<BR> pulseTimeout = false;</FONT></FONT></P> +r;<BR><BR>Task tMeasure(1000, -1, &measureCallback, &r, +true);<BR>Task tDisplay(1000, -1, &displayCallback, &r, +true);<BR>Task tPing(0, 1, &pingCalcCallback, &r, +false);<BR><BR><BR>volatile bool pulseBusy = false;<BR>volatile bool +pulseTimeout = false;<BR>volatile unsigned long pulseStart = +0;<BR>volatile unsigned long pulseStop = 0;<BR>volatile unsigned long +pingDistance = 0;<BR><BR><BR>void pingTrigger(unsigned long aTimeout) +{<BR> if (pulseBusy) return; // do not trigger if in the middle of +a pulse<BR> if (pEcho == HIGH) return; // do not trigger if ECHO pin +is high<BR> <BR> pulseBusy = true;<BR> pulseTimeout = false;</FONT></FONT></P> <P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><BR> pTrigger = LOW;<BR> delayMicroseconds(4);<BR> pTrigger = HIGH;<BR><BR> tPing.setInterval (aTimeout);<BR><BR> delayMicroseconds(10);<BR> @@ -832,10 +942,134 @@ Wait for the measurement to <BR>void measureCallbackWait() {<BR> if cm);<BR> Serial.println(d);<BR> <BR>}<BR><BR>void setup() {<BR> // put your setup code here, to run once:<BR> <BR> Serial.begin(115200);<BR> <BR><BR> pTrigger = LOW;<BR> pEcho = -LOW;<BR> <BR> r.init();<BR> r.addTask(tDisplay);<BR> -r.addTask(tMeasure);<BR> r.addTask(tPing);<BR> <BR> -r.enableAll();<BR> tPing.disable();<BR>}<BR><BR>void loop() {<BR> -// put your main code here, to run repeatedly:<BR> r.execute();<BR>}<BR></FONT></FONT><BR> +LOW;<BR> <BR>}<BR><BR>void loop() {<BR> // put your main code here, +to run repeatedly:<BR> r.execute();<BR>}<BR></FONT></FONT><BR> +</P> +<OL START=3> + <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><B>USING + ONENABLE AND ONDISBALE METHODS </B></FONT> + </P> +</OL> +<P CLASS="western" STYLE="margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Times New Roman, serif">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:</FONT></P> +<OL START=3> + <OL> + <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif">We + need to make sure LED is turned OFF at the last iteration</FONT></P> + <LI><P CLASS="western" STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif">We + need to calculate random frequency every time</FONT></P> + </OL> +</OL> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Times New Roman, serif">Below +is the implementation using TaskScheduler </FONT> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2><BR>#include +<TaskScheduler.h><BR><BR>#define LEDPIN 13<BR><BR><BR>Scheduler +ts;<BR></FONT></FONT><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task +tWrapper(30000L, -1, &WrapperCallback, &ts, true);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task +tBlink(5000, 1, NULL, &ts, false, &BlinkOnEnable, +&BlinkOnDisable);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>Task +tLED(0, -1, NULL, &ts, false, NULL, &LEDOff);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void +WrapperCallback() {</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tBlink.restartDelayed(); + // LED blinking is initiated </FONT></FONT> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"> +<FONT FACE="Courier New, monospace"><FONT SIZE=2> //every 30 +seconds for 5 seconds</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +Upon being enabled, tBlink will define the parameters</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +and enable LED blinking task, which actually controls</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +the hardware (LED in this example)</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>bool +BlinkOnEnable() {</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.setInterval( +500 + random(501) );</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.setCallback( +&LEDOn);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.enable();</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> return +true; // Task should be enabled</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +tBlink does not really need a callback function</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +since it just waits for 5 seconds for the first </FONT></FONT> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +and only iteration to occur. Once the iteration</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +takes place, tBlink is disabled by the Scheduler, </FONT></FONT> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +thus executing its OnDisable method below.</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void +BlinkOnDisable() {</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.disable();</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void +LEDOn () {</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> digitalWrite(LEDPIN, +HIGH);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.setCallback( +&LEDOff);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void +LEDOff () {</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> digitalWrite(LEDPIN, +LOW);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2> tLED.setCallback( +&LEDOn);</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +Note that LEDOff method serves as OnDisable method</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +to make sure the LED is turned off when the tBlink</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>// +task finishes (or disabled ahead of time)</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=2>void +setup() {<BR> // put your setup code here, to run once:<BR>}<BR><BR>void +loop() {<BR> // put your main code here, to run repeatedly:<BR> +ts.execute();<BR>}</FONT></FONT></P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> +</P> +<P CLASS="western" STYLE="margin-left: 0.49in; margin-bottom: 0in"><BR> </P> </BODY> </HTML>
\ No newline at end of file diff --git a/extras/TaskScheduler_html.png b/extras/TaskScheduler_html.png Binary files differnew file mode 100644 index 0000000..febb8c7 --- /dev/null +++ b/extras/TaskScheduler_html.png diff --git a/keywords.txt b/keywords.txt index bd52529..0e5d354 100644 --- a/keywords.txt +++ b/keywords.txt @@ -22,8 +22,10 @@ currentTask KEYWORD2 execute KEYWORD2 allowSleep KEYWORD2 enable KEYWORD2 +enableIfNot KEYWORD2 enableDelayed KEYWORD2 delay KEYWORD2 +forceNextIteration KEYWORD2 restart KEYWORD2 restartDelayed KEYWORD2 disable KEYWORD2 @@ -33,7 +35,10 @@ setInterval KEYWORD2 getInterval KEYWORD2 setIterations KEYWORD2 getIterations KEYWORD2 +getRunCounter KEYWORD2 setCallback KEYWORD2 +setOnEnable KEYWORD2 +setOnDisable KEYWORD2 disableOnLastIteration KEYWORD2 getOverrun KEYWORD2 isFirstIteration KEYWORD2 diff --git a/library.properties b/library.properties index 685cf14..7623ac5 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TaskScheduler -version=1.6.0 +version=1.7.0 author=Anatoli Arkhipenko <arkhipenko@hotmail.com> maintainer=Anatoli Arkhipenko <arkhipenko@hotmail.com> sentence=A light-weight cooperative multitasking library for arduino microcontrollers. diff --git a/src/TaskScheduler.cpp b/src/TaskScheduler.cpp index 43cb976..5cdf2f4 100644 --- a/src/TaskScheduler.cpp +++ b/src/TaskScheduler.cpp @@ -1,4 +1,4 @@ -// Cooperative multitasking library for Arduino version 1.6.0 +// Cooperative multitasking library for Arduino version 1.7.0 // Copyright (c) 2015 Anatoli Arkhipenko // @@ -33,9 +33,11 @@ THE SOFTWARE. /** Constructor, uses default default values for the parameters * so could be called with no parameters. */ -Task::Task(unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, boolean aEnable) { +Task::Task( unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, bool aEnable, bool (*aOnEnable)(), void (*aOnDisable)() ) { reset(); set(aInterval, aIterations, aCallback); + setOnEnable(aOnEnable); + setOnDisable(aOnDisable); if (aScheduler) aScheduler->addTask(*this); if (aEnable) enable(); } @@ -50,6 +52,7 @@ void Task::reset() { iPrev = NULL; iNext = NULL; iScheduler = NULL; + iRunCounter = 0; #ifdef _TASK_TIMECRITICAL iOverrun = 0; #endif @@ -59,11 +62,15 @@ void Task::reset() { * @param aInterval - execution interval in ms * @param aIterations - number of iterations, use -1 for no limit * @param aCallback - pointer to the callback function which executes the task actions + * @param aOnEnable - pointer to the callback function which is called on enable() + * @param aOnDisable - pointer to the callback function which is called on disable() */ -void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)()) { +void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)(), void (*aOnDisable)()) { iInterval = aInterval; iSetIterations = iIterations = aIterations; iCallback = aCallback; + iOnEnable = aOnEnable; + iOnDisable = aOnDisable; } /** Sets number of iterations for the task @@ -75,18 +82,29 @@ void Task::setIterations(long aIterations) { } /** Enables the task - * and schedules it for execution as soon as possible + * schedules it for execution as soon as possible, + * and resets the RunCounter back to zero */ void Task::enable() { - iEnabled = true; + iEnabled = iOnEnable ? (*iOnEnable)() : true; + iRunCounter = 0; iPreviousMillis = millis() - iInterval; } +/** Enables the task only if it was not enabled already + * Returns previous state (true if was already enabled, false if was not) + */ +bool Task::enableIfNot() { + bool previousEnabled = iEnabled; + if (!iEnabled) enable(); + return (previousEnabled); +} + /** Enables the task * and schedules it for execution after a delay = aInterval */ void Task::enableDelayed(unsigned long aDelay) { - iEnabled = true; + enable(); delay(aDelay); } @@ -99,6 +117,14 @@ void Task::delay(unsigned long aDelay) { iPreviousMillis = millis() - iInterval + aDelay; } +/** Schedules next iteration of Task for execution immediately (if enabled) + * leaves task enabled or disabled + * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time + */ +void Task::forceNextIteration() { + iPreviousMillis = millis() - iInterval; +} + /** Sets the execution interval. * Task execution is delayed for aInterval * Use enable() to schedule execution ASAP @@ -110,10 +136,14 @@ void Task::setInterval (unsigned long aInterval) { } /** Disables task - * Task will no loner be executed by the scheduler + * Task will no longer be executed by the scheduler + * Returns status of the task before disable was called (i.e., if the task was already disabled) */ -void Task::disable() { +bool Task::disable() { + bool previousEnabled = iEnabled; + if (iEnabled && iOnDisable) (*iOnDisable)(); iEnabled = false; + return (previousEnabled); } /** Restarts task @@ -244,6 +274,7 @@ void Scheduler::execute() { unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval; if (targetMillis <= millis()) { if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task + iCurrent->iRunCounter++; iCurrent->iPreviousMillis += iCurrent->iInterval; #ifdef _TASK_TIMECRITICAL @@ -263,6 +294,7 @@ void Scheduler::execute() { } else { if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task + iCurrent->iRunCounter++; if (iCurrent->iCallback) { (*(iCurrent->iCallback))(); #ifdef _TASK_SLEEP_ON_IDLE_RUN diff --git a/src/TaskScheduler.h b/src/TaskScheduler.h index 90f8132..c09be2f 100644 --- a/src/TaskScheduler.h +++ b/src/TaskScheduler.h @@ -1,4 +1,4 @@ -// Cooperative multitasking library for Arduino version 1.6.0 +// Cooperative multitasking library for Arduino version 1.7.0 // Copyright (c) 2015 Anatoli Arkhipenko // // Changelog: @@ -25,8 +25,13 @@ // 2015-09-22 - deprecated disableOnLastIteration method as a result // 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this // 2015-10-01 - made version numbers semver compliant (documentation only) - - +// +// v1.7.0: +// 2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number. +// 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled. +// 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) +// 2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled +// 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left /* ============================================ Cooperative multitasking library code is placed under the MIT license @@ -63,7 +68,6 @@ THE SOFTWARE. //#define _TASK_TIMECRITICAL //#define _TASK_SLEEP_ON_IDLE_RUN - #ifdef _TASK_SLEEP_ON_IDLE_RUN #include <avr/sleep.h> #include <avr/power.h> @@ -96,25 +100,30 @@ class Scheduler { class Task { friend class Scheduler; public: - Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false); + Task(unsigned long aInterval=0, long aIterations=0, void (*aCallback)()=NULL, Scheduler* aScheduler=NULL, boolean aEnable=false, bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL); void enable(); + bool enableIfNot(); void enableDelayed(unsigned long aDelay=0); void delay(unsigned long aDelay=0); + void forceNextIteration(); void restart(); void restartDelayed(unsigned long aDelay=0); - void disable(); + bool disable(); inline bool isEnabled() { return iEnabled; } - void set(unsigned long aInterval, long aIterations, void (*aCallback)()); + void set(unsigned long aInterval, long aIterations, void (*aCallback)(),bool (*aOnEnable)()=NULL, void (*aOnDisable)()=NULL); void setInterval(unsigned long aInterval); inline unsigned long getInterval() { return iInterval; } void setIterations(long aIterations); inline long getIterations() { return iIterations; } + inline unsigned long getRunCounter() { return iRunCounter; } inline void setCallback(void (*aCallback)()) { iCallback = aCallback; } + inline void setOnEnable(bool (*aCallback)()) { iOnEnable = aCallback; } + inline void setOnDisable(void (*aCallback)()) { iOnDisable = aCallback; } #ifdef _TASK_TIMECRITICAL inline long getOverrun() { return iOverrun; } #endif - inline bool isFirstIteration() { return (iIterations >= iSetIterations-1); } + inline bool isFirstIteration() { return (iRunCounter <= 1); } inline bool isLastIteration() { return (iIterations == 0); } private: @@ -128,7 +137,10 @@ class Task { #endif volatile long iIterations; long iSetIterations; + unsigned long iRunCounter; void (*iCallback)(); + bool (*iOnEnable)(); + void (*iOnDisable)(); Task *iPrev, *iNext; Scheduler *iScheduler; }; |
