Task scheduler library  1.0
Arduino library for simply executing tasks in parallel
Tasks.cpp
Go to the documentation of this file.
1 
7 #include "Tasks.h"
8 
9 
10 // check Arduino controller only once
11 #if !defined(__AVR__) && !defined(__SAM3X8E__)
12  #error board not supported, error
13 #endif
14 
15 
16 /**************************************/
17 /******* start skip in doxygen ********/
18 /**************************************/
20 
21 // measure speed via pin D9(=PB1). Is configured as OUTPUT in Scheduler_Start()
22 #define TASKS_MEASURE_PIN 0
23 #if (TASKS_MEASURE_PIN)
24  #define SET_PIN (PORTB |= B00000010)
25  #define CLEAR_PIN (PORTB &= ~B00000010)
26  #define TOGGLE_PIN (PORTB ^= B00000010)
27 #endif
28 
29 
30 // macro to pause / resume interrupt (interrupts are only reactivated in case they have been active in the beginning)
31 uint8_t oldISR = 0;
32 #if defined(__AVR__)
33  #define PAUSE_INTERRUPTS { oldISR = SREG; noInterrupts(); }
34  #define RESUME_INTERRUPTS { SREG = oldISR; interrupts(); }
35 #elif defined(__SAM3X8E__)
36  #define PAUSE_INTERRUPTS { oldISR = ((__get_PRIMASK() & 0x1) == 0 && (__get_FAULTMASK() & 0x1) == 0); noInterrupts(); }
37  #define RESUME_INTERRUPTS { if (oldISR != 0) { interrupts(); } }
38 #endif
39 
40 
41 // task container
42 typedef struct SchedulingStruct
43 {
44  Task func; // function to call
45  bool active; // task is active
46  bool running; // task is currently being executed
47  int16_t period; // period of task (0 = call only once)
48  int16_t time; // time of next call
49 };
50 
51 
52 // global variables for scheduler
53 SchedulingStruct SchedulingTable[MAX_TASK_CNT]; // array containing all tasks
54 bool SchedulingActive; // false = Scheduling stopped, true = Scheduling active (no configuration allowed)
55 int16_t _timebase; // 1ms counter (on ATMega 1.024ms, is compensated)
56 int16_t _nexttime; // time of next task call
57 uint8_t _lasttask; // last task in the tasks array (cauting! This variable starts is not counting from 0 to x but from 1 to x meaning that a single tasks will be at SchedulingTable[0] but _lasttask will have the value '1')
58 
59 
60 #if defined(__SAM3X8E__)
61  /*
62  Code from https://forum.arduino.cc/index.php?topic=130423.0
63  ISR/IRQ TC Channel Due pins
64  TC0 TC0 0 2, 13
65  TC1 TC0 1 60, 61
66  TC2 TC0 2 58
67  TC3 TC1 0 none
68  TC4 TC1 1 none
69  TC5 TC1 2 none
70  TC6 TC2 0 4, 5
71  TC7 TC2 1 3, 10
72  TC8 TC2 2 11, 12
73  */
74  void startTasksTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency)
75  {
76  pmc_set_writeprotect(false);
77  pmc_enable_periph_clk((uint32_t)irq);
78  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);
79  uint32_t rc = (SystemCoreClock >> 1)/frequency; //2 because we selected TIMER_CLOCK4 above
80  //TC_SetRA(tc, channel, (rc >> 1)); //50% high, 50% low
81  TC_SetRC(tc, channel, rc);
82  TC_Start(tc, channel);
83  tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
84  tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
85  NVIC_SetPriority(SysTick_IRQn, 8);
86  NVIC_SetPriority(irq, 15);
87  NVIC_EnableIRQ(irq);
88  }
89 #endif // __SAM3X8E__
90 
91 
92 void Scheduler_update_nexttime(void)
93 {
94  // stop interrupts, store old setting
95  PAUSE_INTERRUPTS;
96 
97  // find time of next task execution
98  _nexttime = _timebase + INT16_MAX; // Max. possible delay of the next time
99  for (uint8_t i = 0; i < _lasttask; i++) {
100  if ((SchedulingTable[i].active == true) && (SchedulingTable[i].func != NULL)) {
101 
102  /*
103  Serial.print(i);
104  Serial.print(" ");
105  Serial.println(SchedulingTable[i].time);
106  */
107 
108  if ((int16_t)(SchedulingTable[i].time - _nexttime) < 0) {
109  _nexttime = SchedulingTable[i].time;
110  }
111  }
112  }
113 
114  /*
115  Serial.print("timebase ");
116  Serial.println(_timebase);
117  Serial.print("nexttime ");
118  Serial.println(_nexttime);
119  Serial.println();
120  */
121  /*
122  Serial.print(_timebase);
123  Serial.print(" ");
124  Serial.println(_nexttime - _timebase);
125  */
126 
127  // resume stored interrupt setting
128  RESUME_INTERRUPTS;
129 
130 } // Scheduler_update_nexttime()
131 
132 
134 /************************************/
135 /******* end skip in doxygen ********/
136 /************************************/
137 
138 
139 
140 void Tasks_Init(void)
141 {
142  uint8_t i;
143 
144  // stop interrupts, store old setting
145  PAUSE_INTERRUPTS;
146 
147  // init scheduler
148  SchedulingActive = false;
149  _timebase = 0;
150  _nexttime = 0;
151  _lasttask = 0;
152  for(i = 0; i < MAX_TASK_CNT; i++)
153  {
154  //Reset scheduling table
155  SchedulingTable[i].func = NULL;
156  SchedulingTable[i].active = false;
157  SchedulingTable[i].running = false;
158  SchedulingTable[i].period = 0;
159  SchedulingTable[i].time = 0;
160  } // loop over scheduler slots
161 
162  // resume stored interrupt setting
163  RESUME_INTERRUPTS;
164 
165 } // Tasks_Init()
166 
167 
168 
169 bool Tasks_Add(Task func, int16_t period, int16_t delay)
170 {
171  // stop interrupts, store old setting
172  PAUSE_INTERRUPTS;
173 
174  // Check range of period and delay
175  if ((period < 0) || (delay < 0))
176  return false;
177 
178  // workaround for 1.024ms timer period of Arduino ATMega
179  #if defined(__AVR__)
180  delay = (uint16_t)(((((int32_t)delay) * 250) + 128) >> 8); // delay = delay / 1.024 <-- with up/down rounding
181  period = (uint16_t)(((((int32_t)period) * 250) + 128) >> 8); // period = period / 1.024 <-- with up/down rounding
182  #endif
183 
184  // Check if task already exists and update it in this case
185  for(uint8_t i = 0; i < _lasttask; i++)
186  {
187  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
188  PAUSE_INTERRUPTS;
189 
190  // same function found
191  if (SchedulingTable[i].func == func)
192  {
193  SchedulingTable[i].active = true;
194  SchedulingTable[i].running = false;
195  SchedulingTable[i].period = period;
196  SchedulingTable[i].time = _timebase + delay;
197 
198  // resume stored interrupt setting
199  RESUME_INTERRUPTS;
200 
201  // find time for next task execution
202  Scheduler_update_nexttime();
203 
204  // return success
205  return true;
206  }
207 
208  // resume stored interrupt setting
209  RESUME_INTERRUPTS;
210 
211  } // loop over scheduler slots
212 
213  // find free scheduler slot
214  for (uint8_t i = 0; i < MAX_TASK_CNT; i++)
215  {
216  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
217  PAUSE_INTERRUPTS;
218 
219  // free slot found
220  if (SchedulingTable[i].func == NULL)
221  {
222  // add task to scheduler table
223  SchedulingTable[i].func = func;
224  SchedulingTable[i].active = true;
225  SchedulingTable[i].running = false;
226  SchedulingTable[i].period = period;
227  SchedulingTable[i].time = _timebase + delay;
228 
229  // update _lasttask
230  if (i >= _lasttask)
231  _lasttask = i + 1;
232 
233  // resume stored interrupt setting
234  RESUME_INTERRUPTS;
235 
236  // find time for next task execution
237  Scheduler_update_nexttime();
238 
239  // return success
240  return true;
241 
242  } // if free slot found
243 
244  // resume stored interrupt setting
245  RESUME_INTERRUPTS;
246 
247  } // loop over scheduler slots
248 
249  // did not change anything, thus no scheduler_update_nexttime neccessary
250  // no free slot found -> error
251  return false;
252 
253 } // Tasks_Add()
254 
255 
256 
257 bool Tasks_Remove(Task func)
258 {
259  // stop interrupts, store old setting
260  PAUSE_INTERRUPTS;
261 
262  // find function in scheduler table
263  for (uint8_t i = 0; i < _lasttask; i++)
264  {
265  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
266  PAUSE_INTERRUPTS;
267 
268  // function pointer found in list
269  if (SchedulingTable[i].func == func)
270  {
271  // remove task from scheduler table
272  SchedulingTable[i].func = NULL;
273  SchedulingTable[i].active = false;
274  SchedulingTable[i].running = false;
275  SchedulingTable[i].period = 0;
276  SchedulingTable[i].time = 0;
277 
278  // update _lasttask
279  if (i == (_lasttask - 1))
280  {
281  _lasttask--;
282  while(_lasttask != 0)
283  {
284  if(SchedulingTable[_lasttask - 1].func != NULL)
285  {
286  break;
287  }
288  _lasttask--;
289  }
290  }
291 
292  // resume stored interrupt setting
293  RESUME_INTERRUPTS;
294 
295  // find time for next task execution
296  Scheduler_update_nexttime();
297 
298  // return success
299  return true;
300 
301  } // if function found
302 
303  // resume stored interrupt setting
304  RESUME_INTERRUPTS;
305 
306  } // loop over scheduler slots
307 
308  // did not change anything, thus no scheduler_update_nexttime neccessary
309  // function not in scheduler -> error
310  return false;
311 
312 } // Tasks_Remove()
313 
314 
315 
316 bool Tasks_Delay(Task func, int16_t delay)
317 {
318  // stop interrupts, store old setting
319  PAUSE_INTERRUPTS;
320 
321  // Check range of delay
322  if (delay < 0)
323  return false;
324 
325  // Workaround for 1.024ms timer period of Arduino MEGA
326  #if defined(__AVR__)
327  delay = (uint16_t)(((((int32_t)delay) * 250) + 128) >> 8); // delay = delay / 1.024 <-- with up/down rounding
328  #endif
329 
330  // find function in scheduler table
331  for (uint8_t i = 0; i < _lasttask; i++)
332  {
333  // stop interrupts, store old setting
334  PAUSE_INTERRUPTS;
335 
336  // function pointer found in list
337  if (SchedulingTable[i].func == func)
338  {
339  // if task is currently running, delay next call
340  if (SchedulingTable[i].running == true)
341  SchedulingTable[i].time = SchedulingTable[i].time - SchedulingTable[i].period;
342 
343  // set time to next execution
344  SchedulingTable[i].time = _timebase + delay;
345 
346  // resume stored interrupt setting
347  RESUME_INTERRUPTS;
348 
349  // find time for next task execution
350  Scheduler_update_nexttime();
351 
352  // return success
353  return true;
354 
355  } // if function found
356 
357  // resume stored interrupt setting
358  RESUME_INTERRUPTS;
359 
360  } // loop over scheduler slots
361 
362  // did not change anything, thus no scheduler_update_nexttime neccessary
363  // function not in scheduler -> error
364  return false;
365 
366 } // Tasks_Delay()
367 
368 
369 
370 bool Tasks_SetState(Task func, bool state)
371 {
372  // stop interrupts, store old setting
373  PAUSE_INTERRUPTS;
374 
375  // find function in scheduler table
376  for (uint8_t i = 0; i < _lasttask; i++)
377  {
378  // stop interrupts when accessing any element within the scheduler (also neccessary for if checks!), store old setting
379  PAUSE_INTERRUPTS;
380 
381  // function pointer found in list
382  if(SchedulingTable[i].func == func)
383  {
384  // set new function state
385  SchedulingTable[i].active = state;
386  SchedulingTable[i].time = _timebase + SchedulingTable[i].period;
387 
388  // resume stored interrupt setting
389  RESUME_INTERRUPTS;
390 
391  // find time for next task execution
392  Scheduler_update_nexttime();
393 
394  // return success
395  return true;
396 
397  } // if function found
398 
399  // resume stored interrupt setting
400  RESUME_INTERRUPTS;
401 
402  } // loop over scheduler slots
403 
404  // did not change anything, thus no scheduler_update_nexttime neccessary
405  // function not in scheduler -> error
406  return false;
407 
408 } // Tasks_SetState()
409 
410 
411 
412 void Tasks_Start(void)
413 {
414  #if (TASKS_MEASURE_PIN)
415  pinMode(9, OUTPUT);
416  #endif
417 
418  // enable scheduler
419  SchedulingActive = true;
420  //_timebase = 0; // unwanted delay after resume, see time-print() output! -> likely delete
421 
422  _nexttime = _timebase; // Scheduler should perform a full check of all tasks after the next start
423 
424  // enable timer interrupt
425  #if defined(__AVR__)
426  TIMSK0 |= (1<<OCIE0A); // Enable OC0A Interrupt
427  #elif defined(__SAM3X8E__)
428  startTasksTimer(TC1, 0, TC3_IRQn, 1000); // TC1 channel 0, the IRQ for that channel and the desired frequency
429  #endif
430 
431  // find time for next task execution
432  Scheduler_update_nexttime();
433 
434 } // Tasks_Start()
435 
436 
437 
438 void Tasks_Pause(void)
439 {
440  // pause scheduler
441  SchedulingActive = false;
442  //_timebase = 0; // unwanted delay after resume, see time-print() output! -> likely delete
443 
444  // disable timer interrupt
445  #if defined(__AVR__)
446  TIMSK0 &= ~(1<<OCIE0A); //Disable OC0A Interrupt
447  #elif defined(__SAM3X8E__)
448  NVIC_DisableIRQ(TC3_IRQn);
449  #endif
450 
451 } // Tasks_Pause()
452 
453 
454 
455 /**************************************/
456 /******* start skip in doxygen ********/
457 /**************************************/
459 
460 #if defined(__AVR__)
461  ISR(TIMER0_COMPA_vect) // Timer0 interrupt is called each 1.024ms before the OVL interrupt used for millis()
462 #elif defined(__SAM3X8E__)
463  void TC3_Handler(void)
464 #else
465  void Scheduler_dummy_handler(void) // avoid compiler error for unsupported boards
466 #endif
467 {
468  uint8_t i;
469 
470  // measure speed via GPIO
471  #if (TASKS_MEASURE_PIN)
472  SET_PIN;
473  #endif
474 
475  #if defined(__SAM3X8E__)
476  TC_GetStatus(TC1, 0);
477  #endif
478 
479  // Skip if scheduling was stopped or is in the process of being stopped
480  if (SchedulingActive == false) {
481  #if (TASKS_MEASURE_PIN) // measure speed via GPIO
482  CLEAR_PIN;
483  #endif
484  return;
485  }
486 
487  // increase 1ms counter
488  _timebase++;
489 
490  // no task is pending -> return immediately
491  if ((int16_t)(_nexttime - _timebase) > 0) {
492  #if (TASKS_MEASURE_PIN) // measure speed via GPIO
493  CLEAR_PIN;
494  #endif
495  return;
496  }
497 
498  // loop over scheduler slots
499  for(i = 0; i < _lasttask; i++)
500  {
501  // disable interrupts
502  noInterrupts();
503 
504  // function pointer found in list, function is active and not running (arguments ordered to provide maximum speed
505  if ((SchedulingTable[i].active == true) && (SchedulingTable[i].running == false) && (SchedulingTable[i].func != NULL))
506  {
507  // function period has passed
508  if((int16_t)(SchedulingTable[i].time - _timebase) <= 0)
509  {
510  // execute task
511  SchedulingTable[i].running = true; // avoid dual function call
512  SchedulingTable[i].time = _timebase + SchedulingTable[i].period; // set time of next call
513 
514  // re-enable interrupts
515  interrupts();
516 
517  // execute function
518  SchedulingTable[i].func();
519 
520  // disable interrupts
521  noInterrupts();
522 
523  // re-allow function call by scheduler
524  SchedulingTable[i].running = false;
525 
526  // if function period is 0, remove it from scheduler after execution
527  if(SchedulingTable[i].period == 0)
528  {
529  SchedulingTable[i].func = NULL;
530  }
531 
532  // re-enable interrupts
533  interrupts();
534  } // if function period has passed
535  } // if function found
536 
537  // re-enable interrupts
538  interrupts();
539 
540  } // loop over scheduler slots
541 
542  // find time for next task execution
543  Scheduler_update_nexttime();
544 
545  // measure speed viaGPIO
546  #if (TASKS_MEASURE_PIN)
547  CLEAR_PIN;
548  #endif
549 
550 } // ISR()
551 
552 
554 /************************************/
555 /******* end skip in doxygen ********/
556 /************************************/
void(* Task)(void)
Example prototype for a function than can be executed as a task.
Definition: Tasks.h:44
void Tasks_Pause(void)
Pause the task scheduler.
Definition: Tasks.cpp:438
void Tasks_Start(void)
Start the task scheduler.
Definition: Tasks.cpp:412
bool Tasks_SetState(Task func, bool state)
Enable or disable the execution of a task.
Definition: Tasks.cpp:370
bool Tasks_Add(Task func, int16_t period, int16_t delay)
Add a task to the task scheduler.
Definition: Tasks.cpp:169
#define MAX_TASK_CNT
Maximum number of parallel tasks.
Definition: Tasks.h:42
void Tasks_Init(void)
Initialize and reset the tasks library.
Definition: Tasks.cpp:140
bool Tasks_Delay(Task func, int16_t delay)
Delay execution of a task.
Definition: Tasks.cpp:316
Library providing a simple task scheduler for multitasking.
bool Tasks_Remove(Task func)
Remove a task from the task scheduler.
Definition: Tasks.cpp:257