Get Adobe Flash player
FacebookTwitterGoogle+
English Arabic French German Italian Portuguese Russian Spanish

Did you know?

Einstein received the Nobel Prize for Physics in 1921 for his explanation of the photoelectric effect, the phenomenon by which electrons are knocked out of matter by electromagnetic radiation such as light.
 

Help us stay online:

small donate

 

1State Machine or Time sliced multi-tasking system

 

A state machine is recommended for most solutions where there is non time-critical tasks to perform. However, the time sliced multi-tasking is useful when:

   1. There is multiple tasks.
   2. Some of the tasks are low-priority, and can execute in the background, being interrupted by higher priority tasks. For example, low-frequency PWM with 300ms of mathematical calculations happening every 1 second.

 

Using a State Machine

The topic of this tutorial is not to teach state machines - they are very common.

Using a Time Sliced Multi-tasking system 

The multitasking system below was used to implement, on an not so powerfull microcontroller.

This technique that allows one to harness the power of a PIC micro.  It is definitely worth learning, and probably the single most important technique in microcontroller programing.   

   1. Set up timer 1 to overflow and trigger and generate an interrupt every 500us.
   2. The interrupt service routine will get executed every 500us.
   3. For each individual task, have three things:
       i.      A counter that increments every time the interrupt happens every 500us
      ii.      A task counter maximum, called TASKX_COUNTER_MAX.  When the counter gets to this maximum, it executes the task.  Thus, one can set the frequency that each task gets performed by changing this maximum.  One task may have a maximum set to 1, which means it executes every 500us, and another set to 2000 which means it only gets executed every 1000ms.
      iii.      A task enable boolean flag, TASKX_ENABLE.
   4. Thus, we can set the frequency and priority of how often each task gets performed.
   5. We can extend this further – what happens if we have to regularly do a task that takes ages, say 100ms of mathematical calculations, but it only happens every second?  It we execute it right in the interrupt, it will stop all the high frequency, important tasks from executing every 500us.  We couldn’t have the PWM stopping for 100ms.
         - So, all we do is trigger the slow, complex task from the interrupt, by setting taskX_go=true.  Then, the interrupt immediately finishes ready for the next task.
        - In the meantime, a loop in main() is polling taskX_go.  If it is true, it sets it to false ready for next time, then executes the task at its leisure.  While this is going on, the high priority tasks still happen at a normal rate.
   6. In summary:
         - We can execute any number of tasks at regular intervals.
         - We can choose the frequency of executing each task by altering TASKX_COUNTER_MAX.
         - Choose the order of execution by having the most important tasks first in the interrupt routine.  This alters the priority.
         - Have slow, infrequent background  tasks executing in main(), triggered by the interrupt setting ‘taskX_go=true’ at regular intervals.
   7. There is one more tip to avoid problems.  The time inbetween the 500us ‘ticks’ must be enough to execute all the tasks, in the worst case.  Either that, or only one task is executed per 500us interrupt ‘tick’, and the others have to wait until a free slot comes along.  To tell whether the timing is too tight and tasks are executed again before the previous one had a chance to finish, add a line to check whether the timer interrupt flag has aleady been set before the interrupt exits. 

Sample Code Outline

The sample code below briefly outlines the code described above:

//multitasking system – handle multiple tasks with one microprocessor
//task counters used to tell when a task is ready to be executed
//all these counters are incremented every time a 500us interrupt happens
//every task has its own counter that is updated every time a 500us interrupt happens
//first we declare variables we will be using
unsigned int task0_counter=0;
unsigned int task1_counter=0;
unsigned int task2_counter=0;
//this tells when a task is going to happen again
//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
#define TASK0_COUNTER_MAX 1         //high frequency task – every 500us, maybe PWM
#define TASK1_COUNTER_MAX 2000      //low frequency task – every 1000ms
#define TASK2_COUNTER_MAX 5000      //low frequency and low priority task, every 2500ms
//Note: every variable referenced in both interrupt and main() must be declared volatile. You have been warned!
//this enables/disables a task
volatile unsigned char task0_enable=TRUE;
volatile unsigned char task1_enable=TRUE;
volatile unsigned char task2_enable=TRUE;
//this allows tasks triggered by interrupt to run in the background in main()
volatile unsigned char task2_go=FALSE;
 
//this is basicly just seting up timer interupt interval and enabling timer
//you should not pay much attention to details it is
//principle of multitasking that is important here
void setup_multitasking(void)
{
   //set up tmr1  to interrupt every 500us
   TMR1CS=0;
   T1CKPS0=0;
   T1CKPS1=0;
/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock).  Timer 1 interrupts when it gets to 0xFFFF or 65535.  Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535.  To test, use simulator to find that its exactly correct*/
   #define  TICKS_BETWEEN_INTERRUPTS      2000
   #define  INTERRUPT_OVERHEAD            19
   #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
   #define TMR1RESET_HIGH TMR1RESET >> 8
   #define TMR1RESET_LOW TMR1RESET & 0xFF
   TMR1ON=0;
   TMR1H=TMR1RESET_HIGH;
   TMR1L=TMR1RESET_LOW;
   TMR1ON=1;     
   TMR1IF=0;
   TMR1IE=1;
   PEIE=1;
   GIE=1;  
}
 
void interrupt isr(void)
{
   //one tick every 500us at 16Mhz
   if (TMR1IF)
   {
      //set up timer 1 again to interrupt 500us in future
      TMR1IF=0;
      TMR1ON=0;
      TMR1H=TMR1RESET_HIGH;
      TMR1L=TMR1RESET_LOW;
      TMR1ON=1;
 
//this is important part to understand below
      task0_counter++;
      if (task0_counter>=TASK0_COUNTER_MAX)     //high frequency task – every 1 tick
      {
            task0_counter=0;
            if (task0_enable==TRUE)
            {
                  //do high frequency important task 0, for example PWM
            }
      }
      task1_counter++;
      if (task1_counter>=TASK1_COUNTER_MAX)     //low priority task - every 2000 ticks
      {
            task1_counter=0;
            if (task1_enable==TRUE)
            {
                  //do low frequency yet important task 1
            }
      }
/*this task takes a long time, 100ms for example, lots of maths.  Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it.  In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/
      task2_counter++;
      if (task2_counter>=TASK2_COUNTER_MAX)     //every 250ms
      {
          task2_counter=0;
          if (task2_enable==TRUE)
          {
               //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
               task2_go=TRUE;
          }
      }
   }  //if (TMR1IF)
} //interrupt routine
main()
{
   setup_multitasking();
   while(1)
   {
      if (task2_go==TRUE)
      {
            task2_go=FALSE;
            //take our time, doing heaps of complex maths at our leisure in the background
      }
   }
}

This principle will help you to make process high speed processes and still run slow complicated calculations in baskround as well as organise your code so it has desired number of tasks that run simultaniously. 

this text is partially taken from www.microchipc.com