TaskScheduler.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Cooperative multitasking library for Arduino version 1.51
  2. // Copyright (c) 2015 Anatoli Arkhipenko
  3. //
  4. /* ============================================
  5. Cooperative multitasking library code is placed under the MIT license
  6. Copyright (c) 2015 Anatoli Arkhipenko
  7. Permission is hereby granted, free of charge, to any person obtaining a copy
  8. of this software and associated documentation files (the "Software"), to deal
  9. in the Software without restriction, including without limitation the rights
  10. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. copies of the Software, and to permit persons to whom the Software is
  12. furnished to do so, subject to the following conditions:
  13. The above copyright notice and this permission notice shall be included in
  14. all copies or substantial portions of the Software.
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. THE SOFTWARE.
  22. ===============================================
  23. */
  24. #include "TaskScheduler.h"
  25. // ------------------ Task implementation --------------------
  26. /** Constructor, uses default default values for the parameters
  27. * so could be called with no parameters.
  28. */
  29. Task::Task(unsigned long aInterval, long aIterations, void (*aCallback)(), Scheduler* aScheduler, boolean aEnable) {
  30. reset();
  31. set(aInterval, aIterations, aCallback);
  32. if (aScheduler) aScheduler->addTask(*this);
  33. if (aEnable) enable();
  34. }
  35. /** Resets (initializes) the task/
  36. * Task is not enabled and is taken out
  37. * out of the execution chain as a result
  38. */
  39. void Task::reset() {
  40. iEnabled = false;
  41. iPreviousMillis = 0;
  42. iPrev = NULL;
  43. iNext = NULL;
  44. iScheduler = NULL;
  45. iDisableOnLastIteration = false;
  46. #ifdef _TASK_TIMECRITICAL
  47. iOverrun = 0;
  48. #endif
  49. }
  50. /** Explicitly set Task execution parameters
  51. * @param aInterval - execution interval in ms
  52. * @param aIterations - number of iterations, use -1 for no limit
  53. * @param aCallback - pointer to the callback function which executes the task actions
  54. */
  55. void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)()) {
  56. iInterval = aInterval;
  57. if (iEnabled && iIterations == 0) enable();
  58. iSetIterations = iIterations = aIterations;
  59. iCallback = aCallback;
  60. }
  61. /** Sets number of iterations for the task
  62. * if task is enabled, schedule for immediate execution
  63. * @param aIterations - number of iterations, use -1 for no limit
  64. */
  65. void Task::setIterations(long aIterations) {
  66. if (iEnabled && iIterations == 0) enable();
  67. iSetIterations = iIterations = aIterations;
  68. }
  69. /** Enables the task
  70. * and schedules it for execution as soon as possible
  71. */
  72. void Task::enable() {
  73. iEnabled = true;
  74. iPreviousMillis = millis() - iInterval;
  75. }
  76. /** Enables the task
  77. * and schedules it for execution after a delay = aInterval
  78. */
  79. void Task::enableDelayed(unsigned long aDelay) {
  80. iEnabled = true;
  81. delay(aDelay);
  82. }
  83. /** Delays Task for execution after a delay = aInterval (if task is enabled).
  84. * leaves task enabled or disabled
  85. * if aDelay is zero, delays for the original scheduling interval from now
  86. */
  87. void Task::delay(unsigned long aDelay) {
  88. if (!aDelay) aDelay = iInterval;
  89. iPreviousMillis = millis() - iInterval + aDelay;
  90. }
  91. /** Sets the execution interval.
  92. * Task execution is delayed for aInterval
  93. * Use enable() to schedule execution ASAP
  94. * @param aInterval - new execution interval
  95. */
  96. void Task::setInterval (unsigned long aInterval) {
  97. iInterval = aInterval;
  98. delay();
  99. }
  100. /** Disables task
  101. * Task will no loner be executed by the scheduler
  102. */
  103. void Task::disable() {
  104. iEnabled = false;
  105. }
  106. /** Restarts task
  107. * Task will run number of iterations again
  108. */
  109. void Task::restart() {
  110. iIterations = iSetIterations;
  111. enable();
  112. }
  113. /** Restarts task delayed
  114. * Task will run number of iterations again
  115. */
  116. void Task::restartDelayed(unsigned long aDelay) {
  117. iIterations = iSetIterations;
  118. enableDelayed(aDelay);
  119. }
  120. // ------------------ Scheduler implementation --------------------
  121. /** Default constructor.
  122. * Creates a scheduler with an empty execution chain.
  123. */
  124. Scheduler::Scheduler() {
  125. init();
  126. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  127. iAllowSleep = true;
  128. #endif
  129. }
  130. /** Appends task aTask to the tail of the execution chain.
  131. * @param &aTask - reference to the Task to be appended.
  132. * @note Task can only be part of the chain once.
  133. */
  134. void Scheduler::addTask(Task& aTask) {
  135. aTask.iScheduler = this;
  136. // First task situation:
  137. if (iFirst == NULL) {
  138. iFirst = &aTask;
  139. aTask.iPrev = NULL;
  140. }
  141. else {
  142. // This task gets linked back to the previous last one
  143. aTask.iPrev = iLast;
  144. iLast->iNext = &aTask;
  145. }
  146. // "Previous" last task gets linked to this one - as this one becomes the last one
  147. aTask.iNext = NULL;
  148. iLast = &aTask;
  149. }
  150. /** Deletes specific Task from the execution chain
  151. * @param &aTask - reference to the task to be deleted from the chain
  152. */
  153. void Scheduler::deleteTask(Task& aTask) {
  154. if (aTask.iPrev == NULL) {
  155. if (aTask.iNext == NULL) {
  156. iFirst = NULL;
  157. iLast = NULL;
  158. return;
  159. }
  160. else {
  161. aTask.iNext->iPrev = NULL;
  162. iFirst = aTask.iNext;
  163. aTask.iNext = NULL;
  164. return;
  165. }
  166. }
  167. if (aTask.iNext == NULL) {
  168. aTask.iPrev->iNext = NULL;
  169. iLast = aTask.iPrev;
  170. aTask.iPrev = NULL;
  171. return;
  172. }
  173. aTask.iPrev->iNext = aTask.iNext;
  174. aTask.iNext->iPrev = aTask.iPrev;
  175. aTask.iPrev = NULL;
  176. aTask.iNext = NULL;
  177. }
  178. /** Disables all tasks in the execution chain
  179. * Convenient for error situations, when the only
  180. * task remaining active is an error processing task
  181. */
  182. void Scheduler::disableAll() {
  183. Task *current = iFirst;
  184. while (current) {
  185. current->disable();
  186. current = current->iNext;
  187. }
  188. }
  189. /** Enables all the tasks in the execution chain
  190. */
  191. void Scheduler::enableAll() {
  192. Task *current = iFirst;
  193. while (current) {
  194. current->enable();
  195. current = current->iNext;
  196. }
  197. }
  198. /** Makes one pass through the execution chain.
  199. * Tasks are executed in the order they were added to the chain
  200. * There is no concept of priority
  201. * Different pseudo "priority" could be achieved
  202. * by running task more frequently
  203. */
  204. void Scheduler::execute() {
  205. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  206. bool idleRun = true;
  207. #endif
  208. iCurrent = iFirst;
  209. while (iCurrent) {
  210. do {
  211. if (iCurrent->iEnabled) {
  212. if (iCurrent->iIterations == 0) {
  213. if (iCurrent->iDisableOnLastIteration) iCurrent->disable();
  214. break;
  215. }
  216. if (iCurrent->iInterval > 0) {
  217. unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
  218. if (targetMillis <= millis()) {
  219. if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
  220. iCurrent->iPreviousMillis += iCurrent->iInterval;
  221. #ifdef _TASK_TIMECRITICAL
  222. // Updated_previous+current should put us into the future, so iOverrun should be positive or zero.
  223. // If negative - the task is behind (next execution time is already in the past)
  224. iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
  225. #endif
  226. if (iCurrent->iCallback) {
  227. (*(iCurrent->iCallback))();
  228. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  229. idleRun = false;
  230. #endif
  231. }
  232. break;
  233. }
  234. }
  235. else {
  236. if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
  237. if (iCurrent->iCallback) {
  238. (*(iCurrent->iCallback))();
  239. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  240. idleRun = false;
  241. #endif
  242. }
  243. }
  244. }
  245. } while (0); //guaranteed single run - allows use of "break" to exit
  246. iCurrent = iCurrent->iNext;
  247. }
  248. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  249. if (idleRun && iAllowSleep) {
  250. set_sleep_mode(SLEEP_MODE_IDLE);
  251. sleep_enable();
  252. /* Now enter sleep mode. */
  253. sleep_mode();
  254. /* The program will continue from here after the timer timeout ~1 ms */
  255. sleep_disable(); /* First thing to do is disable sleep. */
  256. }
  257. #endif
  258. }