Pages: 1 2 3 [4] 5   Go Down
Author Topic: SCoop - multitask and Simple COOPerative scheduler AVR & ARM  (Read 22936 times)
0 Members and 1 Guest are viewing this topic.
Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 2
Posts: 85
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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() )
« Last Edit: December 29, 2013, 01:52:21 pm by kert » Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Kert!

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

Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 2
Posts: 85
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

I have tested some month ago and rember it worked. Hope you ll get successfull smiley
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 smiley 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:
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:
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]
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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%
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
Old programmer, new to Arduino
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi!
thanks for the quick reply smiley
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-grin
Logged

Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 2
Posts: 85
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Hertfordshire, U.K.
Offline Offline
Jr. Member
**
Karma: 2
Posts: 85
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thanks smiley appreciate it!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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 - downloaded 18 times.)
« Last Edit: April 11, 2014, 01:36:33 am by leo72 » Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Hope this helps
Fabrice
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thanks fabrecio!
Ill try this smiley
Logged

Pages: 1 2 3 [4] 5   Go Up
Jump to: