TaskScheduler.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // Cooperative multitasking library for Arduino version 1.6
  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. #ifdef _TASK_TIMECRITICAL
  46. iOverrun = 0;
  47. #endif
  48. }
  49. /** Explicitly set Task execution parameters
  50. * @param aInterval - execution interval in ms
  51. * @param aIterations - number of iterations, use -1 for no limit
  52. * @param aCallback - pointer to the callback function which executes the task actions
  53. */
  54. void Task::set(unsigned long aInterval, long aIterations, void (*aCallback)()) {
  55. iInterval = aInterval;
  56. iSetIterations = iIterations = aIterations;
  57. iCallback = aCallback;
  58. }
  59. /** Sets number of iterations for the task
  60. * if task is enabled, schedule for immediate execution
  61. * @param aIterations - number of iterations, use -1 for no limit
  62. */
  63. void Task::setIterations(long aIterations) {
  64. iSetIterations = iIterations = aIterations;
  65. }
  66. /** Enables the task
  67. * and schedules it for execution as soon as possible
  68. */
  69. void Task::enable() {
  70. iEnabled = true;
  71. iPreviousMillis = millis() - iInterval;
  72. }
  73. /** Enables the task
  74. * and schedules it for execution after a delay = aInterval
  75. */
  76. void Task::enableDelayed(unsigned long aDelay) {
  77. iEnabled = true;
  78. delay(aDelay);
  79. }
  80. /** Delays Task for execution after a delay = aInterval (if task is enabled).
  81. * leaves task enabled or disabled
  82. * if aDelay is zero, delays for the original scheduling interval from now
  83. */
  84. void Task::delay(unsigned long aDelay) {
  85. if (!aDelay) aDelay = iInterval;
  86. iPreviousMillis = millis() - iInterval + aDelay;
  87. }
  88. /** Sets the execution interval.
  89. * Task execution is delayed for aInterval
  90. * Use enable() to schedule execution ASAP
  91. * @param aInterval - new execution interval
  92. */
  93. void Task::setInterval (unsigned long aInterval) {
  94. iInterval = aInterval;
  95. delay();
  96. }
  97. /** Disables task
  98. * Task will no loner be executed by the scheduler
  99. */
  100. void Task::disable() {
  101. iEnabled = false;
  102. }
  103. /** Restarts task
  104. * Task will run number of iterations again
  105. */
  106. void Task::restart() {
  107. iIterations = iSetIterations;
  108. enable();
  109. }
  110. /** Restarts task delayed
  111. * Task will run number of iterations again
  112. */
  113. void Task::restartDelayed(unsigned long aDelay) {
  114. iIterations = iSetIterations;
  115. enableDelayed(aDelay);
  116. }
  117. // ------------------ Scheduler implementation --------------------
  118. /** Default constructor.
  119. * Creates a scheduler with an empty execution chain.
  120. */
  121. Scheduler::Scheduler() {
  122. init();
  123. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  124. iAllowSleep = true;
  125. #endif
  126. }
  127. /** Appends task aTask to the tail of the execution chain.
  128. * @param &aTask - reference to the Task to be appended.
  129. * @note Task can only be part of the chain once.
  130. */
  131. void Scheduler::addTask(Task& aTask) {
  132. aTask.iScheduler = this;
  133. // First task situation:
  134. if (iFirst == NULL) {
  135. iFirst = &aTask;
  136. aTask.iPrev = NULL;
  137. }
  138. else {
  139. // This task gets linked back to the previous last one
  140. aTask.iPrev = iLast;
  141. iLast->iNext = &aTask;
  142. }
  143. // "Previous" last task gets linked to this one - as this one becomes the last one
  144. aTask.iNext = NULL;
  145. iLast = &aTask;
  146. }
  147. /** Deletes specific Task from the execution chain
  148. * @param &aTask - reference to the task to be deleted from the chain
  149. */
  150. void Scheduler::deleteTask(Task& aTask) {
  151. if (aTask.iPrev == NULL) {
  152. if (aTask.iNext == NULL) {
  153. iFirst = NULL;
  154. iLast = NULL;
  155. return;
  156. }
  157. else {
  158. aTask.iNext->iPrev = NULL;
  159. iFirst = aTask.iNext;
  160. aTask.iNext = NULL;
  161. return;
  162. }
  163. }
  164. if (aTask.iNext == NULL) {
  165. aTask.iPrev->iNext = NULL;
  166. iLast = aTask.iPrev;
  167. aTask.iPrev = NULL;
  168. return;
  169. }
  170. aTask.iPrev->iNext = aTask.iNext;
  171. aTask.iNext->iPrev = aTask.iPrev;
  172. aTask.iPrev = NULL;
  173. aTask.iNext = NULL;
  174. }
  175. /** Disables all tasks in the execution chain
  176. * Convenient for error situations, when the only
  177. * task remaining active is an error processing task
  178. */
  179. void Scheduler::disableAll() {
  180. Task *current = iFirst;
  181. while (current) {
  182. current->disable();
  183. current = current->iNext;
  184. }
  185. }
  186. /** Enables all the tasks in the execution chain
  187. */
  188. void Scheduler::enableAll() {
  189. Task *current = iFirst;
  190. while (current) {
  191. current->enable();
  192. current = current->iNext;
  193. }
  194. }
  195. /** Makes one pass through the execution chain.
  196. * Tasks are executed in the order they were added to the chain
  197. * There is no concept of priority
  198. * Different pseudo "priority" could be achieved
  199. * by running task more frequently
  200. */
  201. void Scheduler::execute() {
  202. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  203. bool idleRun = true;
  204. #endif
  205. iCurrent = iFirst;
  206. while (iCurrent) {
  207. do {
  208. if (iCurrent->iEnabled) {
  209. if (iCurrent->iIterations == 0) {
  210. iCurrent->disable();
  211. break;
  212. }
  213. if (iCurrent->iInterval > 0) {
  214. unsigned long targetMillis = iCurrent->iPreviousMillis + iCurrent->iInterval;
  215. if (targetMillis <= millis()) {
  216. if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
  217. iCurrent->iPreviousMillis += iCurrent->iInterval;
  218. #ifdef _TASK_TIMECRITICAL
  219. // Updated_previous+current should put us into the future, so iOverrun should be positive or zero.
  220. // If negative - the task is behind (next execution time is already in the past)
  221. iCurrent->iOverrun = (long) (iCurrent->iPreviousMillis + iCurrent->iInterval - millis());
  222. #endif
  223. if (iCurrent->iCallback) {
  224. (*(iCurrent->iCallback))();
  225. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  226. idleRun = false;
  227. #endif
  228. }
  229. break;
  230. }
  231. }
  232. else {
  233. if (iCurrent->iIterations > 0) iCurrent->iIterations--; // do not decrement (-1) being a signal of eternal task
  234. if (iCurrent->iCallback) {
  235. (*(iCurrent->iCallback))();
  236. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  237. idleRun = false;
  238. #endif
  239. }
  240. }
  241. }
  242. } while (0); //guaranteed single run - allows use of "break" to exit
  243. iCurrent = iCurrent->iNext;
  244. }
  245. #ifdef _TASK_SLEEP_ON_IDLE_RUN
  246. if (idleRun && iAllowSleep) {
  247. set_sleep_mode(SLEEP_MODE_IDLE);
  248. sleep_enable();
  249. /* Now enter sleep mode. */
  250. sleep_mode();
  251. /* The program will continue from here after the timer timeout ~1 ms */
  252. sleep_disable(); /* First thing to do is disable sleep. */
  253. }
  254. #endif
  255. }