Test, test, test

or don't use it

I leave extensive testing to you. Have a look at the example with the three tasks on the second page (table). Time to see it happening. We will use our common debugging technic, we will send bytes to the serial port. Have the full code!


/// 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;
}task;

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

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

/// Prototypes ////////
void InitUART (u16 baud);
void TransmitByte (u8 data);
void InitScheduler (void);
void UpdateScheduler(void);
void DeleteTask (u8 index);
void AddTask (void (*taskfunc)(void), u16 taskdelay, u16 taskperiod);
void DispatchTask (void);

void TestA (void);
void TestB (void);
void TestC (void);

/// Main //////////////
int main(void)
{
   InitUART (23);
   InitScheduler();
       
   // populate task array       
   AddTask (TestA, 0, 3);
   AddTask (TestB, 1, 4);
   AddTask (TestC, 4, 0);
       
   // enable interrupts
   sei();
       
   while (1)
   {
      DispatchTask();
   }           
}

void InitUART (u16 baud)
{
   UBRRH = (u8)(baud>>8);                                                        
   UBRRL = (u8)baud;
   UCSRB = (1<<RXEN)|(1<<TXEN);              
   UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}

void TransmitByte (u8 data)
{
   while ( !( UCSRA & (1<<UDRE)) );
   UDR = data;
}

void InitScheduler (void)
{
   u8 i;
   
   // timer prescaler clock/1024
   TCCR0 |= (1<<CS02)|(1<<CS00);
   // clear pending interrupts
   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;
}

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;   
   }
}

SIGNAL(SIG_OVERFLOW0)
{
   u8 m;

   // testing
   TransmitByte(45); // (-)
   
   // load timer
   TCNT0 = StartFrom;
   
   for (m=0; m<MAXnTASKS; m++)
   {
      if (TaskArray[m].pfunc)
      {   
         if (TaskArray[m].delay == 0)
         {
            TaskArray[m].run = 1;
            TaskArray[m].delay = TaskArray[m].period;
         }
         else TaskArray[m].delay--;
      }
   }
}

void DispatchTask (void)
{
   u8 k;
   
   for (k=0; k<MAXnTASKS; k++)
   {
      if (TaskArray[k].run == 1)
      {
         // run task
         (*TaskArray[k].pfunc)();
         // clear run flag
         TaskArray[k].run = 0;
      }
   }
}

void TestA (void)
{
   TransmitByte(49); //(1)
}

void TestB (void)
{
   TransmitByte(50); //(2)
}

void TestC (void)
{
   TransmitByte(51); //(3)
}
 

Connect your favorite terminal program and get the following sequence ... -1 -2 - - -13 -3 -23 -3 -13 -3 -3 -23 -13 -3 -3 -3 -123 -3 -3 -3 -13 -23 -3 -3 -13 -3 -23 -3 -13 -3 -3 -23 -13 -3 -3 -3 -123 ... it works!

Final word, you cannot use interrupts with this scheme. The only interrupt that should exist is the timer overflow interrupt. If you do activate other interrupts, your code will be doomed!

Thanks to [davef] for his interest and corrections!

 
 
victor@avrtutor.com