Ron Kreymborg
The first example shows the two tasks plugged into the task scheduler. In the following discussions I will call the computing task Task1 and the state change task Task2. While the timers run at different frequencies, I have arranged the periods so the tasks move in and out of synchronization. You can cut and past directly from here along with the matching makefile. Or you can download the complete set of files where the tasks are defined in modules.
#include <stdlib.h>
#include <interrupt.h>
#include <signal.h>
#include "Taskr.h"
int main(void);
void InitComputeTask(void);
void InitStateChangeTask(void);
void Task_Computation(void);
void Task_ManageEvent(void);
#define CLK0_DIVIDER 0x04 // PCK0 / 64
#define CLK0_COUNT 256-8 // 15.625 mSec tick
#define CLK2_DIVIDER 0x05
#define CLK2_COUNT 256-101
volatile BYTE ComputationTicks;
BYTE LED_State;
int main(void)
{
// PORTA
outp(0x00, PORTA); // all low
outp(0xff, DDRA); // all output
// PORTB
outp(0xff, PORTB); // all low
outp(0xff, DDRB); // all output
sbi(ACSR, ACD); // disable comparator
InitTaskR(); // init TaskR
InitComputeTask(); // init the "compute" task
InitStateChangeTask(); // init the state change task
sei(); // hello world
while (1)
Dispatch(); // loop forever
return 0;
}
//---------------------------------------------------------------------
//
void InitComputeTask(void)
{
outp(CLK2_DIVIDER, TCCR2);
outp(CLK2_COUNT, TCNT2);
sbi(TIMSK, TOIE2); // enable Timer2 interrupts
}
//---------------------------------------------------------------------
void InitStateChangeTask(void)
{
sbi(ASSR, AS0); // clock from external crystal
outp(CLK0_DIVIDER, TCCR0); // 32768 / 64 = 512 ticks/sec
outp(CLK0_COUNT, TCNT0);
sbi(TIMSK, TOIE0); // enable Timer0 interrupts
}
//---------------------------------------------------------------------
void Task_Computation(void)
{
int i, j;
sbi(PORTA, 3); // for scoping
cbi(PORTB, 7); // the event start
for (i=0; i<5000; i++)
j = 1;
sbi(PORTB, 7); // the event stop
cbi(PORTA, 3); // for scoping
}
//---------------------------------------------------------------------
void Task_ManageEvent(void)
{
if (LED_State)
{
cbi(PORTA, 5); // for scoping
sbi(PORTB, 6); // the state change
}
else
{
sbi(PORTA, 5); // for scoping
cbi(PORTB, 6); // the state change
}
LED_State = ~LED_State; // flip state
}
//---------------------------------------------------------------------
// The simulated event that causes the state change.
//
SIGNAL(SIG_OVERFLOW0)
{
outp(CLK0_COUNT, TCNT0); // reset counter
sbi(PORTA, 1); // for scoping
QueTask(Task_ManageEvent); // schedule the task
cbi(PORTA, 1); // for scoping
}
//---------------------------------------------------------------------
// The simulated event that initiates the compute task.
//
SIGNAL(SIG_OVERFLOW2)
{
outp(CLK2_COUNT, TCNT2); // reset the timer
if (++ComputationTicks %gt 5)
{
ComputationTicks = 0;
QueTask(Task_Computation); // schedule the task
}
}
You can see the tasks running by connecting a scope to the port A pins 3 and 5. Trigger on pin 5 and set a timebase of 5mSec/div. Notice how the Task2 on time (pin 5) varies as its execution period coincides with that of Task1. Even though the interrupt for Task2 may occur during Task1's on time and the task put on the ready queue at that time, it must wait until the other task completes before it can run. The queued Task2 runs immediately Task1 exits, but the timer controlling this task has been ticking all this time, and so when next it interrupts and changes the state, the preceding on or off time for Task2 will consequently be something less than the required 50% duty cycle.
While this will not be a problem in most cases, it would be if the 50% duty cycle was a specification of the output. Where a task must run immediately, simply execute the task from the interrupt. In our case Task2 is very fast, so the only change is to the interrupt function:
//---------------------------------------------------------------------
// The simulated event that causes the state change.
//
SIGNAL(SIG_OVERFLOW0)
{
outp(CLK0_COUNT, TCNT0); // reset counter
sbi(PORTA, 1); // for scoping
Task_ManageEvent(); // execute the task directly
cbi(PORTA, 1); // for scoping
}