Go Down

Topic: SCoop - multitask and Simple COOPerative scheduler AVR & ARM (Read 27 times) previous topic - next topic

jimford

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

kert

#46
Dec 29, 2013, 07:50 pm Last Edit: Dec 29, 2013, 07:52 pm by kert Reason: 1
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() )

fabriceo

Hello Kert!

well indeed the magic solution is not yet known :)
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!

jimford


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
Code: [Select]

// 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

Code: [Select]
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:

Code: [Select]
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:

Code: [Select]

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]

kert


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%

alibaba9292

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

fpoto

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.

alibaba9292

Hi!
thanks for the quick reply :)
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 :D

jimford

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

alibaba9292

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

jimford


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



alibaba9292

#57
Mar 18, 2014, 08:26 pm Last Edit: Apr 11, 2014, 08:36 am by leo72 Reason: 1
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.

Code: [Select]
#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

fabriceo

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

Hope this helps
Fabrice


Go Up