|
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?

|