Timeslice shell for the Arduino

/*

TimeSlice code Revision 1.0b 7/7/09 ArduinoAndy

A programmable logic controller (PLC) controls machinery in a precise timed sequence or scan.
I re-created a small timeslice shell that runs on the Audrino that mimics a PLC.
(My user application for this timeslice code was to monitor and control a serial network)

Using the Arduino main function loop() { } is ok for most pratical purposes but if you need to control
devices and inputs/outputs in a more rigid “fixed time slice” then the following timeslice code might help.
The use of the Arduino function millis() makes it easy to create a simple timeslice shell for control applications
on the Arduino.

With the Arduino timeslice code you can do the following:

#1 Execute any number of tasks/functions at regular intervals.(e.g. every 20 ms./100 ms./500 ms.per scan/loop)
#2 You can choose the frequency of executing each task by changing the time between time slices.
#3 You can choose the order of execution by having the most important functions in the first task.

The timeslice code has 3 timeslice time classes. TimeClass1 (20 ms.), TimeClass2 (100 ms.) & TimeClass3 (500 ms.)
All three time classes are user adjustable. When the program starts, all “user code” at 20 ms. is executed first then
user code at 100 ms. and last the user code at 500 ms. is executed.
If the code within each time class overruns a fixed time slice then an overrun error occurs and the program aborts.
This prevents external input/outputs and functions from executing in a random fashion as opposed to a rigid timed
control sequence. Using time delays in the user code is not recommended because it blocks all code execution until
the delay is completed. It is up to the user to construct his/her code so it runs within the timeslice or timeclass
to which it is installed.

Hints/Tips in using the Timeslice code:

Put the “Fastest” code in TimeClass 1 (20 ms.) - code that will be executed @ 20 ms.
Put other “Medium” user code into TimeClass 2 (100 ms.)
Put other “Slower” user code into TimeClass 3 (500 ms.)
Do not put any delay() in the timeclass user code. This could create timeclass overflow and program halting.
(TimeClass overflow is too much “real time” taken for task execution. Do not put 30 ms. of code in the 20 ms.
timeclass - this will cause a timeclass overflow)
Please hook-up all diagnostic LEDs for easy troubleshooting. There are three LED to indicate each time class code execution
and overrun errors.
*/

Abbreviated code below:

//Timeslice global variables
unsigned long TimeSlice_TC1_Interval = 20;
unsigned long TimeSlice_TC2_Interval = 100;
unsigned long TimeSlice_TC3_Interval = 500;

unsigned long TimeSlice_TC1_Start_Time;
unsigned long TimeSlice_TC2_Start_Time;
unsigned long TimeSlice_TC3_Start_Time;



void loop()
{


//----------------------------------------------------------TimeSlice code-----------------------------------------------------------
while(1){
//-------------------
//Timeclass 1 20 ms
//-------------------
TimeSlice_TC1_Start_Time = millis();

// ---------------------------------------------------------User application code


// ---------------------------------------------------------End of user application code
// Toggle TC1 LED
toggle_LED(1);
// check timeout
if (((millis() - TimeSlice_TC1_Start_Time) > TimeSlice_TC1_Interval)) // 20 ms
{
while(1){
        over_run_Fault(1); // abort program and loop forever and flash Time class 1 LED
        }          
}
else
{
while (((millis() - TimeSlice_TC1_Start_Time) <= TimeSlice_TC1_Interval)){ // remove to save MIPS but it removes precise timeclass execution 
  }  
} 
  
//-------------------
//Timeclass 2 100 ms
//-------------------
TimeSlice_TC2_Start_Time = millis();
// ----------------------------------------------------------User application code

// ----------------------------------------------------------End of user application code
// Toggle TC2 LED
toggle_LED(2); 

if (((millis() - TimeSlice_TC2_Start_Time) > TimeSlice_TC2_Interval)) // 100 ms
{
while(1){
        over_run_Fault(2); // abort program and loop forever flash Time class 2 LED
        }        
}
else
{
 while (((millis() - TimeSlice_TC2_Start_Time) <= TimeSlice_TC2_Interval)){  // remove to save MIPS but it removes precise timeclass execution 
   }  
}

//------------------
//Timeclass 3 500 ms
//------------------
TimeSlice_TC3_Start_Time = millis();
//------------------------------------------------------------------User application code


// -----------------------------------------------------------------End of user application code
// Toggle TC3 LED
toggle_LED(3);
if (((millis() - TimeSlice_TC3_Start_Time) > TimeSlice_TC3_Interval)) // 500 ms
{
 while(1){
         over_run_Fault(3); // abort program loop forever flash Time class 3 LED
         }     
}
else
{  
while (((millis() - TimeSlice_TC3_Start_Time) <= TimeSlice_TC3_Interval)){  // remove to save MIPS but it removes precise timeclass execution 
  }    
}  
} // End of main while loop



}// end of main loop

hey, very nice, thanks for posting it. i'll try it for sure.

It was brought to my attention that another post on this forum was made similar to my post. Check this link out, which was made over three years ago on the zbasic.net forum. http://www.zbasic.net/forum/about346.html?pc_tzo=-14400&pc_d=20090802&pc_t=64244 I modified it, with permission of my next door neighbor, to benefit the Arduino community. 8-) 8-) 8-) Cheers

That's very interesting. Let's see the code you wrote to implement the preemptive multitasking system that Z-Basic has but is so sorely lacking with the Arduino.

If you read carefully the above link the first statement says

QUOTE "#1 Execute any number of tasks at regular locked intervals.(e.g. every 20 ms./100 ms./500 ms.)"

Which means the preemptive multi-tasking system is "lockout" and each timeclass is run in sequence order every 20 ms then 100 ms then 500 ms --> WITHOUT THE PREEMPTIVE MULTITASKING SYSTEM INTERFERRING! This is the same code I posted above but I used the Arduino millis() function to implement it.

Please Note: The only difference is when the third time class runs out it unlocks, then the periodic time class takes over and those instructions not timed are executed. The Arduino has no function for this periodic time class execution.

My timeslice shell is very simplistic in which, instead of running the main void loop(), three time classes are executed in sequence in a while(1) loop within the main void loop(). This bring a timed sequece to each time class! 8-) 8-) 8-)

preemptive multitasking system that Z-Basic has but is so sorely lacking with the Arduino

It would be a very big mistake for the Arduino Design Team to implement a multi-tasking system on the Arduino. There were so many "quirks" and do's and don'ts with the multi-tasking sytem with Zbasic which the Arduino does not have. 8-) 8-) 8-)

QUOTE "#1 Execute any number of tasks at regular locked intervals.(e.g. every 20 ms./100 ms./500 ms.)"

But your code as posted does not execute at e.g. every 20 ms./100 ms./500 ms, it executes three groups of code every 620 ms. I don't see the usefulness of what you have posted.

It would be a very big mistake for the Arduino Design Team to implement a multi-tasking system on the Arduino

Surely you don't presume to judge what the designers can or cannot do successfully?

it executes three groups of code every 620 ms

You are correct. I said in sequence.

I don't see the usefulness of what you have posted.

Three examples: (1) You have a fixed packet LIN bus polling several slave nodes. Since the packets are fixed (not variable) you know exactly how long it takes to poll and the timed response from each slave. So if you allocate the network LIN functions and software to a time class and it overruns that time class then there is a problem with your network communications. (2) The Sensiron SHT7x poll rate cannot be less than, 3-5 seconds or else the device "self heats" and draws excessive current. Putting the code into a fixed time class with counters will make sure this never happens. (3) PID needs a fixed time slice for proper tuning and variable calculations. By putting the PID algorithm into a fixed time class this problem is solved.

All I am doing with the Timeslice shell is timing instructions just like a PLC would be controlling a piece of machinery. 8-) 8-) 8-)

Surely you don't presume to judge what the designers can or cannot do successfully?

This is a disaster waiting to happen if they go to a RTOS on the Arduino.

You see the Arduino philosophy is to fix the problems and move on. This brings innovation. It does not lock them to the past and makes them adhere to functions created years ago.

Zbasic, on the other hand, had their hands tied and had to adhere to their ZBasic format and make every instruction work with any revision upgrade. This does not bring innovation because they are locked to their ZBasic standard set of instructions. Case in point, ZBasic is locked in a 14.7456 MHZ clock reference whereas the Arduino runs at 16 MHZ and could be increased to 20 MHz. A loss of over 5 MHZ, to be compatible, is not being innovative if using Zbasic hardware and software. Case in point, ZBasic has a simulator that runs all their functions/instructions and any upgrade changes would have to checked and fixed to make them compatible. If the person that does this, who created Zbasic, decides to "buy the farm" and is gone then all the ZBasic users are on the hook for "old" Zbasic hardware and software.

No offense "Arduino Design Team" "Keep it simple and always innovate" 8-) 8-) 8-)

Reading a timer at the end of a group of instructions and calculating whether or not the elapsed time is less than some predetermined value is not an innovation. It looks like you have paraphrased the other work to the point that it no longer has a similar use.

It looks like you have paraphrased the other work to the point that it no longer has a similar use.

It works in many of my applications. Keep in mind that the "ArduinoAndy design team" came up the this algorithm 3 years ago and have been using it ever since. I just wanted to share this algorithm with other Arduino forum members. Thanks for your "unbias" comment. :P 8-) 8-) 8-)

Imagine what you could accomplish with more hands. :wink:

My design team designed and created from scratch to finish, in less than one week, the LIN bus network node board. Imagine that! EJ "Topper" :P 1/2 day design review, 1/2 pcb design & 5 days waiting for parts from vendors- ExpressPCB, Microchip direct and Digikey. So actually it took 1 day plus board construction. 8-) 8-) 8-)

No, there is certainly no way anyone could top that! Good job, boys and girls!

You two don't play well together do you. ;)

Lefty

I’ve recently developed something similar (but untested, lol). The task library itself is not terribly readable because I was playing with the macros. The basic idea is to allow any task a shot at running at given intervals while still allowing other processing to happen (cooperative multitasking, of a sort).

Task.h:

#ifndef TASK_H
#define TASK_H

#define PROPERTY_DECL( name, type ) type Get##name(); void Set##name( type value );

class Task
{
  public:
    PROPERTY_DECL( Interval, long )
    PROPERTY_DECL( LastEvent, long )
    int IsActive;
    void (*Execute)();

  private:
    long _interval;
    long _lastEvent;
};

#endif

Task.cpp is quite simple:

#include "Task.h"
#define PROPERTY_IMPL( cls, name, type, local ) type cls::Get##name(){return local;}void cls::Set##name( type value ){local = value;}

PROPERTY_IMPL( Task, Interval, long, _interval )
PROPERTY_IMPL( Task, LastEvent, long, _lastEvent )

I also threw together some sample usage code:

#include "Task.h"

#define MAX_TASK_COUNT 2

Task _tasks[MAX_TASK_COUNT];

void sampleTemperatures()
{
      // do some work
}

void sendValues()
{
      // do some other work
}

void setup()
{
  _tasks[0].SetInterval( 1000 );
  _tasks[0].SetLastEvent( 0 );
  _tasks[0].IsActive = true;
  _tasks[0].Execute = &sampleTemperatures;

  _tasks[1].SetInterval( 30000 );
  _tasks[1].SetLastEvent( 0 );
  _tasks[1].IsActive = true;
  _tasks[1].Execute = &sendValues;
}

void loop()
{
  for( int idx = 0; idx < MAX_TASK_COUNT; idx++ )
  {
    if( _tasks[idx].IsActive )
    {
      if( ( current - _tasks[idx].GetLastEvent() ) >= _tasks[idx].GetInterval() )
      {
        _tasks[idx].SetLastEvent( current );
        (*(_tasks[idx].Execute))();
      }
    }
  }

  // other work can happen here, as long as it doesn't hold things up too long
}

You two don't play well together do you.

Ha, apparently not. I'm sorry, I just wanted to get one straight answer!

Well at any rate the thread seems to have turned toward a scheme that might actually work on the Arduino.

The Foozinator lost me pretty quickly with all the macro stuff but I think I get the basic Idea. Fooz, do you have some sort of scheduler in mind? It seems to me that there needs to be a mechanism to asynchronously stop and start tasks to take advantage of changes in the program's "world".

Or maybe I've missed the point all together. :)

Fooz, do you have some sort of scheduler in mind?

Something like that. In the example, the sampleTemperatures function gets called approximately each second, while the sendValues function gets called about every 30 seconds. In the meantime, other overhead can happen within the loop function, as long as it doesn't hog cycles.

It's basically taking the blink without delay example and allowing you to duplicate that for any (reasonable) number of events/tasks.

Edit:

...lost me pretty quickly with all the macro stuff

Sorry about that. I was playing with an easy way to have Get/Set accessor properties in C++, but it really makes it hard to read. Just think of that part as Interval and LastEvent fields wrapped in fancy get/set code.

Ah, I get it. Do you plan to expand on your idea?