SCoop - multitask and Simple COOPerative scheduler AVR & ARM

I had a look at the SCoopME sources, but without diving into it: it looks like a cleaned up version of SCoop, better written and probably smaller. I suggest you use that instead of SCoop, at least until fabriceo comes up and explains what are the differences.

In fact, I had tried SCoop and then witched to SchedulerARMAVR, which is much simpler, and which I am using right now, because I do not need all the goodies provided by SCoop, it is much smaller and has the same interface as the standard Arduino Scheduler library. The only thing is that I needed to measure the stack occupancy of tasks and I had to write those functions by myself (I posted them above).

fpoto:
In fact, I had tried SCoop and then witched to SchedulerARMAVR, which is much simpler, and which I am using right now, because I do not need all the goodies provided by SCoop, it is much smaller and has the same interface as the standard Arduino Scheduler library.

Interesting - I'll have a look at the SchedulerARMAVR library. Like you, my project uses very little of the facilities that SCoop/SCoopME offer and SchedulerARMAVR may suit.

There's precious little documentation for SchedulerARMAVR and there appears to only the Scheduler.startloop and yield() functions. Do you happen to know of anything else that will enable me to get a 'handle' on it, please?

I notice in the 'Multiple Blinks' example:

 // IMPORTANT:
  // When multiple tasks are running 'delay' passes control to
  // other tasks while waiting and guarantees they get executed.
  Scheduler.delay(1000);

I'm a bit confused as I thought it was yield() that passes control to the other tasks!

Jim

First look at the Scheduler library page, then at the page where Fabrice Oudert announces SchedulerARMAVR, you'll find many indications.

Anyway, if you use the 1.5.x library, the delay() function calls yield(). If you use the 1.0.x library, the delay() function does not call yield(), so you should use Scheduler.delay() rather than delay() to obtain the same effect.

Hello;

I m so sorry that I didnt pay attention to this thread in the past weeks... I was spending time on another fancy project more around electronic and DSP, but ok :slight_smile:

for some of you having tried Scoop V1.2, for one year ago I havent tried it myself on the early release of Arduino and I ll try to do so in the comming days. I ve just received also a pair of teensy 3.1 and a pair of sparkcore and will check that the lib is working with arduino 1.05 and teense 1.18 and may be on spark later

it is fair to use the SchedulerARMAVR instead of SCoop as it is smaller. I ve just put the Assembly code in it to make it compatible with AVR, credit goes to others and the android team;

still Scoop can offer a bit more features, especially the ring token handling and some time allocation to task, which at some point in your devlopement might be needed to optimize the CPU time given to a task

SCoopeME is a concept which is a rework of SCoop 1.2 but using object templates instead of macros.
also it is ported for TexasIntrusment MSP430 (at least I got it to work)
I ve missed time to finish it as I wanted, and to write proper documentation, but it s not bad and can be considered if you are an early adopter :slight_smile:

I still strongly beleive in cooperative scheduler concept and whish all the best to the 230+ people having downloaded it and the other to come

cheers and Happy new 2014

I'm relieved to see that you haven't abandoned SCoop, Fabrice!

Firstly, thank you for SCoopME - it's transformed my project and integrates nicely with the phi_prompt menu library I'm using.

As I mentioned in an earlier post, I couldn't get the SCoop examples to compile on Arduino 1.5.4, and switched to SCoopME, which works well.

One feature I ideally need is for the SCoopTimer to handle a time period of 1 Hour, so it needs a milliseconds input of a long unsigned int, rather than int as it is now. Any ideas how I can work round this problem, please?

Jim

Hello

that the beauty of modern programing with templates,
in fact when you declare a SCoopTimer you can specify as a third paramter another object in charge of counting the time.
by default the object used is "SCtimerMs"
and this is visible in the source code line 240. here is the extract

// class for creating "timers"
template< class CHILD, 
          unsigned TIME,
		  class SCTIME = SCtimerMs >

struct SCoopTimer : SCoop< SCoopTimer< CHILD, TIME, SCTIME > > {

  static SCTIME timer;
  static SCTIME thresold;

if you create you own object called SCtimerSec then you can pass it when declaring your timer, as a third parameter, like this

struct myTimer1 : SCoopTimer< myTimer1, 100, SCtimerSec > { // every 100 second
static void run() { 
  // user code go here.
} } myTimer1;

and for creating you own SCtimerSec, just copy paste and rework the SCtimerMs :slight_smile:

I have tested some month ago and rember it worked. Hope you ll get successfull :slight_smile:
this template stuff is really amaizing

or you could alsways enter in the timer.run() every second and manage to continue only after 3600 ticks :slight_smile: thats the old fashion way but without risk!

Thanks for the quick reply Fabrice.

I'm going to have to digest your suggestions - I have enough problems with 'regular' programming, let alone 'modern'!

Many thanks again for the support - have a good New Year!

Jim

Is there a page somewhere with the list of cooperative schedulers available for arduino ? I've seen scoop, schedulerarmavr , leOS2 AVRQueue mentioned here etc.

Ideally is there a simple combination of a roundrobin function scheduler that can be driven from both the superloop and a timer plus a basic task state machine ? It'd be really nice to have a desktop c++ code path for basic testing. ( and i mean explicitly a run-to-completion coop scheduler, not something that needs yield() )

Hello Kert!

well indeed the magic solution is not yet known :slight_smile:
the problem is tradeoff. I mean, if you go with something like FreeRTOS or ChibiOS, you ll get this kind of features all together in a modular form trough a config file, e.g. I remeber that ChibiOS has a cooperative mode.

also there is some sort of incompatibility between RTOS and coop scheduler : RTOS slice the CPU time on regular basis and change the task context and if you want to care about side effect you have to deal with semaphore and make sure the library is ready for re-entrance or atomic code for example.

for coop scheduler, it is more up to the user to decide the context switch, by calling the yield(), therefore accomodating this constrains inside the core program.
sometime the yield() is hidden in a waiting or pooling function like Serial.read() or keypressed() or delay() and this looks easier, but not different in fact. e.g. usleep() in some linux distro.

as soon as you want a task to be launched periodically, like timer based, you have a decision to make if the code of this task takes long CPU time or not. if not, then it looks more like an interrupt and this is the model of ieos2 if I remeber well
If it takes long time, then you need to pass the token to another task after some time and then comes the need for a specific stack for this task; and then this use cas can be handled with a coop scheduler, you only have the concern of the jitter when re-entering in the task.

So I feel you can do a lot with simple schedulers like SchedulerARMAVR or SCoop and then if someone needs more, just invest in learning RTOS, and accept that the Arduino library are probably not usable as-is.

I would be pleased to record in this thread any suggeston for improving SCoop, as long as we respect the inherent border between RTOS and coop scheduler.

for example, one limitation of SCoop is the fact that any interrupts will use the current stack context, therefore we need to size the stack of each task to include the total interrupt stack size. we could imagine a specific context switch to force using the stack of the main loop().

another example is sleep mode. If you have say 3 task in paralell all containing a sort of loop with a delay() then you can for sure put the CPU in sleep mode for a certain duration representing the least or remaining delay of the pending tasks.
I ve tried this successfully on a MSP430 but the code is too big and not clean enough to be published at the moment.

lets engage the discussion!

fabriceo:
Hello

that the beauty of modern programing with templates,
in fact when you declare a SCoopTimer you can specify as a third paramter another object in charge of counting the time.
by default the object used is "SCtimerMs"
and this is visible in the source code line 240. here is the extract

// class for creating "timers"

template< class CHILD,
          unsigned TIME,
  class SCTIME = SCtimerMs >

struct SCoopTimer : SCoop< SCoopTimer< CHILD, TIME, SCTIME > > {

static SCTIME timer;
  static SCTIME thresold;




if you create you own object called SCtimerSec then you can pass it when declaring your timer, as a third parameter, like this



struct myTimer1 : SCoopTimer< myTimer1, 100, SCtimerSec > { // every 100 second
static void run() {
  // user code go here.
} } myTimer1;




and for creating you own SCtimerSec, just copy paste and rework the SCtimerMs :)

I have tested some month ago and rember it worked. Hope you ll get successfull :)
this template stuff is really amaizing

or you could alsways enter in the timer.run() every second and manage to continue only after 3600 ticks :) thats the old fashion way but without risk!

I've had a look at your library Fabrice, and:

You see that tiny speck of silver, high up in the sky? It's a jet aircraft cruising at 40, 000 feet. That's how far over my head your coding is!

;^)

I hoped that by changing the lines as suggested early in the SCoopME.h file:

typedef uint32_t SCmillis_t;  // define the size for any timers in milliseconds. can be changed to uint32_t

That I would get a long int for the Timer - but it had no effect.

I've worked round my problem by using a ordinary Task with:

struct read_pressure : SCoopTask< read_pressure > {

  static void setup() {}
  static void loop() {

    volatile static uint32_t last_reading = 0;

    if (millis() - last_reading > ONE_HOUR) {
...
// Do barometer pressure reading at 1 Hr intervals
...      
    last_reading = millis();
  }
  }
} read_pressure; // End of Task read_pressure.
[code]

It works OK, but is a bit of a 'kludge' and would be nicer if I could use a 32 bit Timer..

Jim

[/code]

fabriceo:
for coop scheduler, it is more up to the user to decide the context switch, by calling the yield(), therefore accomodating this constrains inside the core program.

So i'm using coop scheduling that does not involve yield, every scheduled function simply runs to completion.
I have found this to be the cleanest simplest design for a lot of things i need to write.

I also normally leave in asserts that first

  • make sure that ISRs don't take more cycles than some arbitrary small amount and hence don't mess with scheduling too much
  • the scheduler times function execution and asserts/alerts if something is falling behind too much or misses it's scheduling window more than x%

Hello guys!
I think this question has been asked in this thread but I am getting the same error when I try to compile the example code in @fabriceo 's guide! the library is amazing from what I gather and I would really like to us it in my project. Any suggestions of why this might not be working. @jimFord you had the same issue with SCoop.h how did you get around it??

Many thanks for your replies and responses
Regards
Ali

If you want help with your code you should provide details.

Anyway, even before doing that, be sure to read the previous posts in this thread: I suspect you'll find a solution.

Hi!
thanks for the quick reply :slight_smile:
I didnt install the library quickly so I managed to solve that problem!
You were right the solution was in the previous posts!
Many thanks
Regards :smiley:

So were you using Scoop or ScoopME 'alibaba9292'?

As I mentioned in an earlier post, I couldn't get the Scoop examples to compile, so I abandoned it and settled for using ScoopME on my project. With the exception of the timer limitation, it works well.

Jim

Hi Jimford

I did get the examples that you see in the Ardiuno IDE after installing the library to compile but I cant get some examples in the reference guide to compile but that may be because there are many syntax errors. Which examples are you talking about.
If you are taking about the examples included in the library when you download SCoop (examples 1-5 etc) then you also need to download the Timerup timerdown IOfilter libraries in addition to SCoop for them to compile.
Could you possibly show me where you downloaded the complete SCoopMe library. I cant find it.
Would you please give me a link from where you got the SCoopMe file from?

Thanks Regards
Ali

alibaba9292:
Could you possibly show me where you downloaded the complete SCoopMe library. I cant find it.
Would you please give me a link from where you got the SCoopMe file from?

Fabrice posted the link for SCoop and SCooMe in his initial post, but here it is anyway:

https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/downloads/list

Jim

thanks :slight_smile: appreciate it!

Hello again guys!

I have a question regarding using the scheduler library that you can download with the SCoop library. I have tried using shceduler library designed to be used with the ardiuno uno to implement multiple tasking but have been on partially successful. My program has to display a value input by the user using serial monitor and also read a value from an external sensor(loadcell) and display that as well. both these task work well independently of one another but when I combine and try them using scheduler I am only able to light both the seven segment displays. One shows 000(the one thats supposed to show sensor readings) and the other has its digits flickering. I am using 2, 3 digit seven segment displays and using 74hc595 shift registers to drive them. Also like I said the codes for doing each of these tasks works well independently how ever combining them causes problems. I think its the while(1) statement on line 171
I would really appreciate any help that you could give me. Also do you think I'd be better off using the SCoop to do this. If so could you give me some pointers on doing that because I am really confused how to do it.
I have attached the code with this as well.

#include <SchedulerARMAVR.h>


//variables for actual_value
const int digitPins1[3] = {
  4,5,6};                     //4 common anode pins of the actual_value
const int clockPin1 = 11;    //74HC595 Pin 11 
const int latchPin1 = 12;    //74HC595 Pin 12
const int dataPin1 = 13;     //74HC595 Pin 14
const byte digit1[10] =      //actual seven segment digits in bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01101101, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111  //9
};

int digitBuffer1[3] = {
  0};
int digitScan1 = 0;
float pressurekg = 0;

//variables for desired_value

const int digitPins2[3] = {
  A4,A3,A2};                 //4 common anode pins of the desired_display
const int clockPin2 = 7;    //74HC595-2 Pin 11 
const int latchPin2 = 8;    //74HC595-2 Pin 12
const int dataPin2 = 10;     //74HC595-2 Pin 14
const byte digit2[10] =      //desired seven segment digits in bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01101101, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111  //9
};
int digitBuffer2[3] = {
  0};
int digitScan2 = 0;
unsigned int desiredValue = 0; //desiredValue input from the computer using serial coomunication
char incomingByte;

//variable for averaging
const int numReadings = 10;
int readings[numReadings];      // the readings from the difference of analog inputs
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average


void setup(){                
  //setup for actual_display
  for(int i=0;i<3;i++)
  {
    pinMode(digitPins1[i],OUTPUT);
  }
  pinMode(latchPin1, OUTPUT);
  pinMode(clockPin1, OUTPUT);
  pinMode(dataPin1, OUTPUT);

  //setup for desired_display
  for(int i=0;i<3;i++)
  {
    pinMode(digitPins2[i],OUTPUT);
  }
  pinMode(latchPin2, OUTPUT);
  pinMode(clockPin2, OUTPUT);
  pinMode(dataPin2, OUTPUT);

  //setup for averaging process
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0;

  Scheduler.startLoop(loop2); //initiate our second loop
  //void loop always started by default//
}

//writes the actual_pressure value on display
void updateDispActual(){
  for(byte j=0; j<3; j++)  
    digitalWrite(digitPins1[j], LOW);

  digitalWrite(latchPin1, LOW);  
  shiftOut(dataPin1, clockPin1, MSBFIRST, B11111111);
  digitalWrite(latchPin1, HIGH);

  delayMicroseconds(100);
  digitalWrite(digitPins1[digitScan1], HIGH); 

  digitalWrite(latchPin1, LOW);  

  shiftOut(dataPin1, clockPin1, MSBFIRST, ~digit1[digitBuffer1[digitScan1]]);

  digitalWrite(latchPin1, HIGH);
  digitScan1++;
  if(digitScan1>2) digitScan1=0; 
}

//writes desired_pressure value to display
void updateDispDesired(){
  for(byte j=0; j<3; j++)  
    digitalWrite(digitPins2[j], LOW);

  digitalWrite(latchPin2, LOW);  
  shiftOut(dataPin2, clockPin2, MSBFIRST, B11111111);
  digitalWrite(latchPin2, HIGH);

  delayMicroseconds(100);
  digitalWrite(digitPins2[digitScan2], HIGH); 

  digitalWrite(latchPin2, LOW);  

  shiftOut(dataPin2, clockPin2, MSBFIRST, ~digit2[digitBuffer2[digitScan2]]);

  digitalWrite(latchPin2, HIGH);
  digitScan2++;
  if(digitScan2>2) digitScan2=0; 
}
//does the averaging process
void averaging(){
  // subtract the last reading:
  total= total - readings[index];         
  // read from the sensor:
  int a = analogRead(A0);// reads the loadcell +ve voltage
  int b = analogRead(A1);//reads the loadcell -ve voltage 
  int c = a-b; // calculates the differential voltage
  readings[index] = c; 
  // add the reading to the total:
  total= total + readings[index];       
  // advance to the next position in the array:  
  index = index + 1;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
    // ...wrap around to the beginning: 
    index = 0;                           

  // calculate the average:
  average = total / numReadings; 
  pressurekg = (average*1.11)-1;//converts average to pressure in kg
}

//runs the loop for actual value of pressure
void loop(){ 
  averaging();
  //Pressure display in kgs
  digitBuffer1[2] = (int(pressurekg)/100);
  digitBuffer1[1] = (int(pressurekg)%100)/10;
  digitBuffer1[0] = (int(pressurekg)%10);
  updateDispActual();
  Scheduler.delay(2);
  yield();
}

//runs the loop for input value from the computer
void loop2(){
  if (Serial.available() > 0) {   // something came across serial
    desiredValue = 0;         // throw away previous integerValue
    while(1) {            // force into a loop until 'n' is received
      incomingByte = Serial.read();
      if (incomingByte == '\n') break;   // exit the while(1), we're done receiving
      if (incomingByte == -1) continue;  // if no characters are in the buffer read() returns -1
      desiredValue *= 10;  // shift left 1 decimal place
      // convert ASCII to integer, add, and shift left 1 decimal place
      desiredValue = int(((incomingByte - 48) + desiredValue));
      if (desiredValue > 420)
        desiredValue = 420;

      Serial.println(desiredValue);
    }
  }      

  digitBuffer2[2] = int(desiredValue)/100;
  digitBuffer2[1] = (int(desiredValue)%100)/10;
  digitBuffer2[0] = (int(desiredValue)%10);
  updateDispDesired();
  Scheduler.delay(2);
  yield();
}

edit by mod: please include the code using the proper tags

finaly_display_sketch.ino (5.28 KB)

Hello
Seems you are just missing a yield() inside your while (1) !
Remember That coop scheduler never switch task itself

Hope this helps
Fabrice