Delay timer is a simple and very useful way of controlling when a task runs.
To implement the timer we first need to declare one a delay counter for each task and initalize them to 0.
// Global timer variables. One for each task.
int taskOneDelayCount;
int taskTwoDelayCount;
void main(void)
{
// Initialize tasks
// Initialize timers to 0.
taskOneDelayCount = 0;
taskTwoDelayCount = 0;
Switch_To_Current();
}
Next we need to modify the scheduler so that it checks the delay counters. And we need to create a function that will set the delay for a task whenever its called.
void DelayTimer(int numOfTicks);
Also want to force a context switch whenever a task wants a delay. When the task calls for a delay it should be stopped immediately. That is the delay begins when the task calls the delay function. The only way to do that is to force a context switch right away.
void scheduler(int sp)
{
WRITEREG32(T0IR, 0xFF); // Reset timer
// Check counters
if (taskOneDelayCount > 0)
taskOneDelayCount--;
if (taskTwoDelayCount > 0)
taskTwoDelayCount--;
if (taskID == 1)
{
// Only switch to a task if it's delay count is at zero
if (taskTwoDelayCount == 0)
{
stackOneSP = sp; // Store stack pointer for task 1
taskID = 2; // Set the taskID to task 2
currentSP = stackTwoSP; // Set the current stack pointer to task 2's stack pointer
}
}
else
{
// Only switch to a task if it's delay count is at zero
if (taskOneDelayCount == 0)
{
stackTwoSP = sp; // Store stack pointer for task 2
taskID = 1; // Set the taskID to task 1
currentSP = stackOneSP; // Set the current stack pointer to task 1's stack pointer
}
}
}
int currentTaskId; // The ID of the current task
int currentSP; // The value of the current task's stack pointer
struct TaskBlock
{
int stackPointer;
int delayCount;
}
TaskBlock taskBlocks[NUMBER_OF_TASKS];
// Allocate space for stacks
int stackOne[STACKSIZE];
int stackTwo[STACKSIZE];
int stackThree[STACKSIZE];
void CreateTask(int id, int* stack, void* task_address)
{
taskBlocks[id].stackPointer = initialize_stack(stack, task_address);
taskBlocks[id].delayCount = 0;
}
void StartOS()
{
currentTaskId = 1;
currentSP = taskBlocks[currentTaskId].stackPointer;
// Run the current task
switch_to_current(); // This call never returns to this function.
}
void main(void)
{
// Create tasks
CreateTask(1, stackOne, (void*)taskOne);
CreateTask(2, stackTwo, (void*)taskTwo);
CreateTask(3, stackIdle, (void*)taskIdle);
StartOS();
}
Changes
Timer interrupt should call a timer handler that will acknowledge the VIC then call the context switching code. That will let us call the scheduler whenever we want independently of the timer.
void DelayTask(int numOfTicks)
{
taskBlocks[currentTaskId].delayCount = numOfTicks;
Scheduler();
}