Ron Kreymborg
The previous example demonstrated various interrupts scheduling their associated tasks directly. This next example demonstrates a less immediate version where the interrupts simply set flags to indicate their task should run, and a single task, scheduled from a timer tick, is responsible for subsequently scheduling the flagged tasks. Thus tasks for which an interrupt occurred during the last clock tick period will be all scheduled at the same time and in the order the interrupts arrived.
If you downloaded the file set for Example 1 you will have noted how each task was in a separate module and how the static qualifier was used to ensure nothing is visible outside the module except for the initialization function. This is as close to C++ as C can get from the information hiding point of view. In this example I relax this somewhat as the flag byte needs to be publicly accessible to all interrupt functions, and the task managing the flags needs to know the task associated with each flag. Thus each module must make public both the initialization and task functions. The flag management function is typically put in the clock tick interrupt module.
Here I use a single flag byte with each interrupt setting a specific bit. This mechanism sacrifices code space for ram space, a not unusual tradeoff for small systems. It also needs careful definition as bits can easily be confused. If you have the room available in ram, use a byte per flag. The example uses a clock tick of 15.625 mSecs on an Atmel '103 using Timer0 from a 32768 Hertz crystal. This frequency provides a response time adequate for most situations and a handy source of one second real time ticks by dividing by 64. The Timer0 interrupt schedules a task which examines the flag bits. For each set flag the function clears the flag and then schedules the associated task. You can download the file set for this example. The supporting structures are defined as follows:
typedef struct TASKS {
void (*TaskName)();
BYTE Mask;
}
The masks would take the form:
enum {
TASK1_MASK = 0x01,
TASK2_MASK = 0x02,
TASK3_MASK = 0x04,
etc
}
The values specify the associated bit positions. An example of the initialization would be:
const TASKS Tasks[] = {
Task1, TASK1_MASK,
Task2, TASK2_MASK,
etc
};
#DEFINE TOTAL_TASKS sizeof(Tasks) / sizeof(Tasks[0])
Using a define saves ram. The following shows the typical flag management task.
//---------------------------------------------------------------------
// Examine the flag byte for any set bits. Schedule the associated
// task.
//
void Task_FlagManager(void)
{
BYTE i, sFlags;
ENTER_CRITICAL_REGION; // must be indivisible
sFlags = Flags; // make a local copy
Flags = 0; // clear for next
EXIT_CRITICAL_REGION;
if (sFlags == 0)
return; // nothing to do
// Scan the flag byte for set flags, scheduling the
// associated task in response.
//
for (i=0; i<TOTAL_TASKS; i++)
{
if (sFlags & Tasks[i].Mask)
{
QueTask(Tasks[i].TaskName);
}
}
}
This task is scheduled by the clock tick. When it starts it makes a local copy of the flag byte before setting it to zero. It does this within a critical region to ensure the copy and clearing are atomic. It then examines the flag byte as a whole for non-zero and if zero, simply exits with nothing to do. Otherwise it examines each bit in turn, scheduling the associated task for each bit set. When all bits have been examined the task exits and the task scheduler in TaskR will start running any queued tasks.
The interrupt function for an example task designated as task 2 and triggered by Timer2 is very simple:
//---------------------------------------------------------------------
SIGNAL(OVERFLOW2)
{
outp(CLK2_COUNT, TCNT2); // reset counter
Flag |= TASK2_MASK; // set flag bit
}
The Timer0 interrupt that defines the system clock tick would be:
//---------------------------------------------------------------------
SIGNAL(OVERFLOW0)
{
outp(CLK0_COUNT, TCNT0); // reset counter
QueTask(Task_FlagManager); // check for ready tasks
}
Of course this interrupt could also schedule some other task as well as the flag management task.