What is a task

drudgery

OK then, let's start from the beginning, the task declaration. Which are those things that comprise a task? Have a look at the following definitions:


/// Includes //////////
#include <avr/io.h>
#include <avr/interrupt.h>

/// Data Types /////////
typedef unsigned char u8;
typedef unsigned int u16;
typedef struct task
{
   // pointer to a function
   void (*pfunc) (void);
   // delay before the first call
   u16 delay;
   // interval between subsequent runs
   u16 period;
   // flag indicating time to run
   u8 run;
};

/// Defines ///////////
// 25msec period 256-180=76
// 7.3728MHz clock and /1024 prescaler
#define StartFrom       76
// maximum number of tasks
#define MAXnTASKS       20

/// Globals ///////////
struct task TaskArray[MAXnTASKS];
 

Each task executes a routine and the thing is, we have to generalize that, this is why we use the beast at the first line, a function pointer. This is a pointer to a function that takes no arguments and returns nothing. Thus, you have to keep task functions that way. If there is a need tasks to communicate with each other, you have to do it through global variables. We also define the maximum number of tasks and we declare an array for holding these tasks.

As mentioned, we need to define the delay before the task is initially executed and the interval between subsequent runs. The unit of measurement is always the timer timeout period. And, of course there should be a flag denoting if the time has come for a task to run or not.

What else interesting do you see? The timer interval of course. The important thing here is, even the longest task should be able to finish within a single period. Say for example that we don't care having things run fast and we are happy with a 25msec scheduler period. By having a crystal of 73272800Hz and setting the timer prescaler to 1/1024 we get timer ticks every 1024/7372800=0.13889msec. If the timer is set to count to 180, we get 180*0.13889=25msec. Thus, we have to start the timer from number 256-180=76 instead of 0. Good, let's see the initialization routine.


void InitScheduler (void)
{
   u8 i;
       
   // timer prescaler clock/1024
   TCCR0 = (1<<CS02)|(1<<CS00);
   // clear interrupt flag
   TIFR = 1<<TOV0;
   // enable timer0 overflow interrupt
   TIMSK = 1<<TOIE0;
   // load timer0
   TCNT0 = StartFrom;

   // clear task array
   for (i=0; i<MAXnTASKS; i++) DeleteTask(i);
}

void DeleteTask (u8 j)
{
   TaskArray[j].pfunc = 0x0000;
   TaskArray[j].delay = 0;
   TaskArray[j].period = 0;
   TaskArray[j].run = 0;
}
 

Nothing special with this one, just configure the timer and clear the task array. Not much for the AddTask routine either, just locate the next available position in the task array and place the new task there.


void AddTask (void (*taskfunc)(void), u16 taskdelay, u16 taskperiod)
{
   u8 n=0;

   // find next available position
   while ((TaskArray[n].pfunc != 0) && (n < MAXnTASKS)) n++;

   // place task
   if (n < MAXnTASKS)
   {
      TaskArray[n].pfunc = taskfunc;
      TaskArray[n].delay = taskdelay;
      TaskArray[n].period = taskperiod;
      TaskArray[n].run = 0;   
   }
}
 

To summarize, if you add the following tasks to the task array, AddTask (TestA, 0, 3), AddTask (TestB, 1, 4), AddTask (TestC, 4, 0), you would get the depicted behavior. TestA is executed every 100msec, right?

 
 
Visit my other site!