Task scheduler library  1.0
Arduino library for simply executing tasks in parallel
Task_scheduler.cpp
Go to the documentation of this file.
1 
7 #include "Task_scheduler.h"
8 
9 // check Arduino controller only once
10 #if !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega328P__) && !defined(__SAM3X8E__)
11  #error board not supported, error
12 #endif
13 
14 /**************************************/
15 /******* start skip in doxygen ********/
16 /**************************************/
18 
19 // measure speed via pin D9(=PB1). Is configured as OUTPUT in Scheduler_Start()
20 #define MEASURE_PIN 0
21 #if (MEASURE_PIN)
22  #define SET_PIN (PORTB |= B00000010)
23  #define CLEAR_PIN (PORTB &= ~B00000010)
24  #define TOGGLE_PIN (PORTB ^= B00000010)
25 #endif
26 
27 
28 // task container
29 typedef struct SchedulingStruct
30 {
31  Task func; // function to vall
32  bool active; // task is active
33  bool running; // task is currently being executed
34  int16_t period; // period of task (0 = call only once)
35  int16_t time; // time of next call
36 };
37 
38 
39 // global variables for scheduler
40 SchedulingStruct SchedulingTable[MAX_TASK_CNT]; // array containing tasks
41 bool SchedulingActive; // false = Scheduling stopped, true = Scheduling active (no configuration allowed)
42 int16_t _timebase; // 1ms counter (on ATMega 1.024ms, is compensated)
43 int16_t _nexttime; // time of next task call
44 
45 
46 #if defined(__SAM3X8E__)
47  /*
48  Code from https://forum.arduino.cc/index.php?topic=130423.0
49  ISR/IRQ TC Channel Due pins
50  TC0 TC0 0 2, 13
51  TC1 TC0 1 60, 61
52  TC2 TC0 2 58
53  TC3 TC1 0 none
54  TC4 TC1 1 none
55  TC5 TC1 2 none
56  TC6 TC2 0 4, 5
57  TC7 TC2 1 3, 10
58  TC8 TC2 2 11, 12
59  */
60  void startTasksTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency)
61  {
62  pmc_set_writeprotect(false);
63  pmc_enable_periph_clk((uint32_t)irq);
64  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);
65  uint32_t rc = (SystemCoreClock >> 1)/frequency; //2 because we selected TIMER_CLOCK4 above
66  //TC_SetRA(tc, channel, (rc >> 1)); //50% high, 50% low
67  TC_SetRC(tc, channel, rc);
68  TC_Start(tc, channel);
69  tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
70  tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
71  NVIC_SetPriority(SysTick_IRQn, 8);
72  NVIC_SetPriority(irq, 15);
73  NVIC_EnableIRQ(irq);
74  }
75 #endif // __SAM3X8E__
76 
77 
78 void Scheduler_update_nexttime(void)
79 {
80  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
81  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
82  uint8_t oldSREG = SREG;
83  #elif defined(__SAM3X8E__)
84  uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
85  #endif
86  noInterrupts();
87 
88  // find time of next task execution
89  int16_t _min = INT16_MAX;
90  for (uint8_t i = 0; i < MAX_TASK_CNT; i++) {
91  if ((SchedulingTable[i].func != NULL) && (SchedulingTable[i].active == true)) {
92  int16_t tmp = (int16_t)(SchedulingTable[i].time - _timebase);
93  if ((tmp >= 0) && (tmp < _min)) {
94  _min = tmp;
95  _nexttime = SchedulingTable[i].time;
96  }
97  }
98  }
99 
100  // resume stored interrupt setting
101  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
102  SREG = oldSREG;
103  #elif defined(__SAM3X8E__)
104  if (enableInterrupts)
105  interrupts(); // re-enable the interrupts
106  #endif
107 
108 } // Scheduler_update_nexttime()
109 
110 
112 /************************************/
113 /******* end skip in doxygen ********/
114 /************************************/
115 
116 
117 void Scheduler_Init(void)
118 {
119  uint8_t i;
120 
121  // stop interrupts, store old setting
122  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
123  uint8_t oldSREG = SREG;
124  #elif defined(__SAM3X8E__)
125  uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
126  #endif
127  noInterrupts();
128 
129  // init scheduler
130  SchedulingActive = false;
131  _timebase = 0;
132  _nexttime = INT16_MAX;
133  for (i = 0; i < MAX_TASK_CNT; i++)
134  {
135  //Reset scheduling table
136  SchedulingTable[i].func = NULL;
137  SchedulingTable[i].active = false;
138  SchedulingTable[i].running = false;
139  SchedulingTable[i].period = 0;
140  SchedulingTable[i].time = 0;
141  } // loop over scheduler slots
142 
143  // resume stored interrupt setting
144  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
145  SREG = oldSREG;
146  #elif defined(__SAM3X8E__)
147  if (enableInterrupts)
148  interrupts(); // re-enable the interrupts
149  #endif
150 
151 } // Scheduler_Init()
152 
153 
154 
155 bool Scheduler_Task_Add(Task func, int16_t period, int16_t delay)
156 {
157  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
158  uint8_t oldSREG = 0;
159  #elif defined(__SAM3X8E__)
160  uint8_t enableInterrupts = 0;
161  #endif
162 
163  // Check range of period and delay
164  if ((period < 0) || (delay < 0))
165  return false;
166 
167  // Workaround for 1.024ms timer period of Arduino MEGA
168  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
169  delay = (uint16_t)(((((int32_t)delay) * 250) + 128) >> 8); // delay = delay / 1.024 <-- with up/down rounding
170  period = (uint16_t)(((((int32_t)period) * 250) + 128) >> 8); // period = period / 1.024 <-- with up/down rounding
171  #endif
172 
173  // Check if task already exists and update it in this case
174  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
175  {
176  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
177  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
178  oldSREG = SREG;
179  #elif defined(__SAM3X8E__)
180  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
181  #endif
182  noInterrupts();
183 
184  // same function found
185  if (SchedulingTable[i].func == func)
186  {
187  SchedulingTable[i].active = true;
188  SchedulingTable[i].running = false;
189  SchedulingTable[i].period = period;
190  SchedulingTable[i].time = _timebase + delay;
191 
192  // resume stored interrupt setting
193  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
194  SREG = oldSREG;
195  #elif defined(__SAM3X8E__)
196  if (enableInterrupts)
197  interrupts(); // re-enable the interrupts
198  #endif
199 
200  // find time for next task execution
201  Scheduler_update_nexttime();
202 
203  // existing task updated -> return success
204  return true;
205  }
206 
207  // resume stored interrupt setting
208  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
209  SREG = oldSREG;
210  #elif defined(__SAM3X8E__)
211  if (enableInterrupts)
212  interrupts(); // re-enable the interrupts
213  #endif
214 
215  } // loop over scheduler slots
216 
217  // new task -> find free scheduler slot
218  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
219  {
220  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
221  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
222  oldSREG = SREG;
223  #elif defined(__SAM3X8E__)
224  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
225  #endif
226  noInterrupts();
227 
228  // free slot found
229  if (SchedulingTable[i].func == NULL)
230  {
231  // add task to scheduler table
232  SchedulingTable[i].func = func;
233  SchedulingTable[i].active = true;
234  SchedulingTable[i].running = false;
235  SchedulingTable[i].period = period;
236  SchedulingTable[i].time = _timebase + delay;
237 
238  // resume stored interrupt setting
239  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
240  SREG = oldSREG;
241  #elif defined(__SAM3X8E__)
242  if (enableInterrupts)
243  interrupts(); // re-enable the interrupts
244  #endif
245 
246  // find time for next task execution
247  Scheduler_update_nexttime();
248 
249  // new task added -> return success
250  return true;
251 
252  } // if free slot found
253 
254  // resume stored interrupt setting
255  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
256  SREG = oldSREG;
257  #elif defined(__SAM3X8E__)
258  if (enableInterrupts)
259  interrupts(); // re-enable the interrupts
260  #endif
261 
262  } // loop over scheduler slots
263 
264  // find time for next task execution
265  Scheduler_update_nexttime();
266 
267  // no free slot found -> error
268  return false;
269 
270 } // Scheduler_Task_Add()
271 
272 
273 
275 {
276  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
277  uint8_t oldSREG = 0;
278  #elif defined(__SAM3X8E__)
279  uint8_t enableInterrupts = 0;
280  #endif
281 
282  // find function in scheduler table
283  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
284  {
285  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
286  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
287  oldSREG = SREG;
288  #elif defined(__SAM3X8E__)
289  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
290  #endif
291  noInterrupts();
292 
293  // function pointer found in list
294  if (SchedulingTable[i].func == func)
295  {
296  // remove task from scheduler table
297  SchedulingTable[i].func = NULL;
298  SchedulingTable[i].active = false;
299  SchedulingTable[i].running = false;
300  SchedulingTable[i].period = 0;
301  SchedulingTable[i].time = 0;
302 
303  // resume stored interrupt setting
304  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
305  SREG = oldSREG;
306  #elif defined(__SAM3X8E__)
307  if (enableInterrupts)
308  interrupts(); // re-enable the interrupts
309  #endif
310 
311  // find time for next task execution
312  Scheduler_update_nexttime();
313 
314  // task deleted -> return success
315  return true;
316 
317  } // if function found
318 
319  // resume stored interrupt setting
320  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
321  SREG = oldSREG;
322  #elif defined(__SAM3X8E__)
323  if (enableInterrupts)
324  interrupts(); // re-enable the interrupts
325  #endif
326 
327  } // loop over scheduler slots
328 
329  // find time for next task execution
330  Scheduler_update_nexttime();
331 
332  // function not in scheduler -> error
333  return false;
334 
335 } // Scheduler_Task_Remove()
336 
337 
338 
339 bool Scheduler_Task_Delay(Task func, int16_t delay)
340 {
341  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
342  uint8_t oldSREG = 0;
343  #elif defined(__SAM3X8E__)
344  uint8_t enableInterrupts = 0;
345  #endif
346 
347  // Check range of delay
348  if (delay < 0)
349  return false;
350 
351  // Workaround for 1.024ms timer period of Arduino MEGA
352  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
353  delay = (uint16_t)(((((int32_t)delay) * 250) + 128) >> 8); // delay = delay / 1.024 <-- with up/down rounding
354  #endif
355 
356  // find function in scheduler table
357  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
358  {
359  // stop interrupts, store old setting
360  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
361  oldSREG = SREG;
362  #elif defined(__SAM3X8E__)
363  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
364  #endif
365  noInterrupts();
366 
367  // function pointer found in list
368  if (SchedulingTable[i].func == func)
369  {
370  // if task is currently running, delay next call
371  if (SchedulingTable[i].running == true)
372  SchedulingTable[i].time = SchedulingTable[i].time - SchedulingTable[i].period;
373 
374  // set time to next execution
375  SchedulingTable[i].time = _timebase + delay;
376 
377  // resume stored interrupt setting
378  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
379  SREG = oldSREG;
380  #elif defined(__SAM3X8E__)
381  if (enableInterrupts)
382  interrupts(); // re-enable the interrupts
383  #endif
384 
385  // find time for next task execution
386  Scheduler_update_nexttime();
387 
388  // task updated -> return success
389  return true;
390 
391  } // if function found
392 
393  // resume stored interrupt setting
394  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
395  SREG = oldSREG;
396  #elif defined(__SAM3X8E__)
397  if (enableInterrupts)
398  interrupts(); // re-enable the interrupts
399  #endif
400 
401  } // loop over scheduler slots
402 
403  // find time for next task execution
404  Scheduler_update_nexttime();
405 
406  // function not in scheduler -> error
407  return false;
408 
409 } // Scheduler_Task_Delay()
410 
411 
412 
414 {
415  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
416  uint8_t oldSREG = 0;
417  #elif defined(__SAM3X8E__)
418  uint8_t enableInterrupts = 0;
419  #endif
420 
421  // find function in scheduler table
422  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
423  {
424  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
425  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
426  oldSREG = SREG;
427  #elif defined(__SAM3X8E__)
428  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
429  #endif
430  noInterrupts();
431 
432  // function pointer found in list
433  if (SchedulingTable[i].func == func)
434  {
435  // set new function state
436  if (SchedulingTable[i].active ==false)
437  {
438  SchedulingTable[i].active = true;
439  SchedulingTable[i].time = _timebase + SchedulingTable[i].period;
440  }
441 
442  // resume stored interrupt setting
443  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
444  SREG = oldSREG;
445  #elif defined(__SAM3X8E__)
446  if (enableInterrupts)
447  interrupts(); // re-enable the interrupts
448  #endif
449 
450  // find time for next task execution
451  Scheduler_update_nexttime();
452 
453  // task updated -> return success
454  return true;
455 
456  } // if function found
457 
458  // resume stored interrupt setting
459  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
460  SREG = oldSREG;
461  #elif defined(__SAM3X8E__)
462  if (enableInterrupts)
463  interrupts(); // re-enable the interrupts
464  #endif
465  } // loop over scheduler slots
466 
467  // find time for next task execution
468  Scheduler_update_nexttime();
469 
470  // function not in scheduler -> error
471  return false;
472 
473 } // Scheduler_Task_Start()
474 
475 
476 
478 {
479  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
480  uint8_t oldSREG = 0;
481  #elif defined(__SAM3X8E__)
482  uint8_t enableInterrupts = 0;
483  #endif
484 
485  // find function in scheduler table
486  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
487  {
488  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
489  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
490  oldSREG = SREG;
491  #elif defined(__SAM3X8E__)
492  enableInterrupts = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0);
493  #endif
494  noInterrupts();
495 
496  // function pointer found in list
497  if (SchedulingTable[i].func == func)
498  {
499  // set new function state
500  SchedulingTable[i].active = false;
501  SchedulingTable[i].time = _timebase + SchedulingTable[i].period;
502 
503  // resume stored interrupt setting
504  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
505  SREG = oldSREG;
506  #elif defined(__SAM3X8E__)
507  if (enableInterrupts)
508  interrupts(); // re-enable the interrupts
509  #endif
510 
511  // find time for next task execution
512  Scheduler_update_nexttime();
513 
514  // task updated -> return success
515  return true;
516 
517  } // if function found
518 
519  // resume stored interrupt setting
520  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
521  SREG = oldSREG;
522  #elif defined(__SAM3X8E__)
523  if (enableInterrupts)
524  interrupts(); // re-enable the interrupts
525  #endif
526  } // loop over scheduler slots
527 
528  // find time for next task execution
529  Scheduler_update_nexttime();
530 
531  // function not in scheduler -> error
532  return false;
533 
534 } // Scheduler_Task_Pause()
535 
536 
537 
538 void Scheduler_Start(void)
539 {
540  #if (MEASURE_PIN)
541  pinMode(9, OUTPUT);
542  #endif
543 
544  // enable scheduler
545  SchedulingActive = true;
546 
547  // enable timer interrupt
548  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
549  TIMSK0 |= (1<<OCIE0A); // Enable OC0A Interrupt
550  #elif defined(__SAM3X8E__)
551  startTasksTimer(TC1, 0, TC3_IRQn, 1000); // TC1 channel 0, the IRQ for that channel and the desired frequency
552  #endif
553 
554  // find time for next task execution
555  Scheduler_update_nexttime();
556 
557 } // Scheduler_Start()
558 
559 
560 
561 void Scheduler_Pause(void)
562 {
563  // pause scheduler
564  SchedulingActive = false;
565 
566  // disable timer interrupt
567  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
568  TIMSK0 &= ~(1<<OCIE0A); //Disable OC0A Interrupt
569  #elif defined(__SAM3X8E__)
570  NVIC_DisableIRQ(TC3_IRQn);
571  #endif
572 
573 } // Scheduler_Pause()
574 
575 
576 
577 /**************************************/
578 /******* start skip in doxygen ********/
579 /**************************************/
581 
582 #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
583  ISR(TIMER0_COMPA_vect) // Timer0 interrupt is called each 1.024ms before the OVL interrupt used for millis()
584 #elif defined(__SAM3X8E__)
585  void TC3_Handler(void)
586 #else
587  void Scheduler_dummy_handler(void) // avoid compiler error for unsupported boards
588 #endif
589 {
590  uint8_t i, u;
591 
592  // measure speed via GPIO
593  #if (MEASURE_PIN)
594  SET_PIN;
595  #endif
596 
597  #if defined(__SAM3X8E__)
598  TC_GetStatus(TC1, 0);
599  #endif
600 
601  // Skip if scheduling was stopped or is in the process of being stopped
602  if (SchedulingActive == false) {
603  #if (MEASURE_PIN) // measure speed via GPIO
604  CLEAR_PIN;
605  #endif
606  return;
607  }
608 
609  // increase 1ms counter
610  _timebase++;
611 
612  // no task is pending -> return immediately
613  if ((int16_t)(_nexttime - _timebase) > 0) {
614  #if (MEASURE_PIN) // measure speed via GPIO
615  CLEAR_PIN;
616  #endif
617  return;
618  }
619 
620  // loop over scheduler slots
621  for (i = 0; i < MAX_TASK_CNT; i++)
622  {
623  // disable interrupts
624  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
625  noInterrupts();
626  #elif defined(__SAM3X8E__)
627  NVIC_DisableIRQ(TC3_IRQn);
628  #endif
629 
630  // function pointer found in list and function is active
631  if ((SchedulingTable[i].func != NULL) && (SchedulingTable[i].active == true))
632  {
633  // function is not already being executed
634  if (SchedulingTable[i].running == false)
635  {
636  // function period has passed
637  if ((int16_t)(SchedulingTable[i].time - _timebase) <= 0)
638  {
639  // execute task
640  SchedulingTable[i].running = true; // avoid dual function call
641  SchedulingTable[i].time = _timebase + SchedulingTable[i].period; // set time of next call
642 
643  // re-enable interrupts
644  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
645  interrupts();
646  #elif defined(__SAM3X8E__)
647  NVIC_EnableIRQ(TC3_IRQn);
648  #endif
649 
650  // execute function
651  SchedulingTable[i].func();
652 
653  // disable interrupts
654  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
655  noInterrupts();
656  #elif defined(__SAM3X8E__)
657  NVIC_DisableIRQ(TC3_IRQn);
658  #endif
659 
660  // re-allow function call by scheduler
661  SchedulingTable[i].running = false;
662 
663  // if function period is 0, remove it from scheduler after execution
664  if (SchedulingTable[i].period == 0)
665  SchedulingTable[i].func = NULL;
666 
667  // re-enable interrupts
668  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
669  interrupts();
670  #elif defined(__SAM3X8E__)
671  NVIC_EnableIRQ(TC3_IRQn);
672  #endif
673 
674  } // if function period has passed
675 
676  } // if function not already running
677 
678  } // if function found
679 
680  // re-enable interrupts
681  #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
682  interrupts();
683  #elif defined(__SAM3X8E__)
684  NVIC_EnableIRQ(TC3_IRQn);
685  #endif
686 
687  } // loop over scheduler slots
688 
689  // find time for next task execution
690  Scheduler_update_nexttime();
691 
692  // measure speed viaGPIO
693  #if (MEASURE_PIN)
694  CLEAR_PIN;
695  #endif
696 
697 } // ISR()
698 
699 
701 /************************************/
702 /******* end skip in doxygen ********/
703 /************************************/
void Scheduler_Start(void)
Start the task scheduler.
Library providing a simple task scheduler for multitasking.
#define MAX_TASK_CNT
Maximum number of parallel tasks.
bool Scheduler_Task_Add(Task func, int16_t period, int16_t delay)
Add a task to the task scheduler.
bool Scheduler_Task_Remove(Task func)
Remove a task from the task scheduler.
bool Scheduler_Task_Pause(Task func)
Deactivate a task in the scheduler.
void(* Task)(void)
Example prototype for a function than can be executed as a task.
bool Scheduler_Task_Start(Task func)
Activate a task in the scheduler.
bool Scheduler_Task_Delay(Task func, int16_t delay)
Delay execution of a task.
void Scheduler_Init(void)
Initialize and reset the tasks library.
void Scheduler_Pause(void)
Pause the task scheduler.