[Projekt] Multitasking

combie: Build Meldungen aktivieren. Im Build Verzeichnis findet sich ein Ordner preproc Da findest du es aufgedröselt.

Danke für dieses Puzzle, aber ich hab's noch geschafft ;-)

Seltsamerweise funktioniert es jetzt auch ohne die Schleife in beginTask. Sehr undurchsichtig :-(

Hallo combie,

schön für die Mühen, aber ist das nicht ein umgedrehtes Intervall? Sieht alles nach negativer Logik aus. Und sollten Multitasking Aufgaben nicht eher vom OS verwaltet und ausgeführt werden statt vom Userprogramm?

Kannst aber gern weitermachen. Bin gespannt was noch bei rauskommt. :)

Je länger ich mich mit den Makros befasse, desto mehr bin ich davon begeistert :-)

Vor allem Anfänger dürften davon profitieren, die von den komplizierten Lösungen wie BlinkWithoutDelay abgeschreckt sind. Aber auch Fortgeschrittene können umfangreicheren Code damit sehr übersichtlich hinschreiben, besser als mit verschachtelten oder scheinbar unmotiviert aufeinanderfolgenden umfangreichen Abfragen. Die Effizienz könnte sogar höher sein als mit der sonst üblichen Technik, zumindest habe ich in dieser Richtung keine ernsthaften Bedenken.

Mich persönlich stört die while-Schleife zwischen taskBegin und taskEnd, und die läßt sich einfach vermeiden, wenn man in taskEnd mark=0 setzt. Eine Frage ist dabei, ob diese Schleife ein besseres Gefühl dafür vermittelt, wie der Code tatsächlich abläuft. Läßt man die Schleife weg, weil die Task nur einmal ausgeführt werden soll, dann hört sie tatsächlich garnicht auf, sondern wiederholt den letzten Teil (ab dem letzten case label) endlos. Dieses Verhalten empfinde ich jedenfalls wenig intuitiv.

Weitere Verbesserungsmöglichkeiten sind mir schon eingefallen, allerdings noch keine Lösungen dazu. Ich sollte die wohl einzeln zur Debatte stellen.

Das Timing mit taskPause ist etwas unpräzise, wie man am Ampel Beispiel sehen kann. Ersetzt man dort in piepser() den letzten taskSwitch durch TaskPause(500), dann müßte der letzte Piep eigentlich nach dem Umschalten der Ampel (alle 2000ms) erfolgen - tut er aber meist nicht! Für zeitgenaues Loggen oder die Erzeugung von Impulsen ist das eher ungeeignet.

Vielleicht könnten dafür zwei Makros verwendet werden, mit taskPause oder taskDelay für unexaktes Warten als direkter Ersatz für delay(), und taskResume o.ä. das relativ zum letzten timeStamp wartet? Die Implementierung wäre relativ simpel

#define taskResume(interval) timestamp+=interval; while (millis()-timestamp < (interval)) taskSwitch();

Nur der Name gefällt mir noch nicht.

Nett das du es mal wieder versuchst mit Tasks.

Ich hab es damals mal versucht, es ist aber leider untergegangen:

http://forum.arduino.cc/index.php?topic=165552.0

Das Projekt habe ich seit dem nicht weiter verfolgt. Die Erkenntnisse sind dann mit in die LCDMenuLib geflossen.

Jomelo: Nett das du es mal wieder versuchst mit Tasks.

Ich hab es damals mal versucht, es ist aber leider untergegangen:

http://forum.arduino.cc/index.php?topic=165552.0

Das Projekt habe ich seit dem nicht weiter verfolgt. Die Erkenntnisse sind dann mit in die LCDMenuLib geflossen.

Hi..

Hmm.. das hatte ich bisher nicht gefunden.... Das war wohl vor meiner Zeit hier ;-)

Dein TaskDingen verfolgt ja einen ganz anderen Ansatz. Den werde ich mir auch mal genau anschauen...

Mir drehte es sich eher darum, einen 1:1 DropIn für delay() und nebenläufige Fäden trotz lang laufender Schleifen zu bekommen.

Lange habe ich darüber gegrübelt, sowas wie einen TaskKontrollBlock einzuführen, aber dann verworfen. Das hätte mehr Möglichkeiten geschaffen, wäre aber auch um Größenordnungen komplexer geworden. Auch für den Anwender, und genau das wollte ich nicht.

Das Timing mit taskPause ist etwas unpräzise,

Ja ... Dass das Ganze kein Echtzeitbetriebsystem wird, dank des kooperativen Charakters auch nie werden können wird, darf einen nicht verwundern. Will man es exakter, wird man die Hardwaretimer bzw. ein RTOS bemühen müssen.

Im Ampel-Test war noch ein Denkfehler drin. Bei 5 Pieps im Abstand von 500ms kommt der letzte ja bereits nach 2000ms, also etwa zeitgleich mit der Weiterschaltung der Ampel, und nicht erst nach 2500ms. Tatsächlich kommt der letzte Piep sauber nach dem letzten Ampel-Zustand, wenn das Intervall auf 501ms hochgesetzt wird. So schlimm ist es mit dem Zeitversatz also doch nicht :-)

Bei meinen angedachten Erweiterungen geht es nicht um hochkomplizierte Funktionen aus Echtzeit-Systemen, sondern was sich mit vergleichbar einfachen Mitteln an Standard-Aufgaben sonst noch erledigen lassen kann.

Anfänger dürften davon profitieren, die von den komplizierten Lösungen wie BlinkWithoutDelay abgeschreckt sind

Das Problem bei Makros ist, dass der Compiler was anderes sieht als was der Anfänger geschrieben hat, und eventuelle Fehlermeldungen dadurch noch viel unverständlicher werden als sie für Anfänger so schon sind.

Wem BlinkWithoutDelay zu komplex ist, dem ist nicht zu helfen der wird auch mit den Multitasking Makros Probleme haben :confused:

Wenn man weiss, dass diese Makros dazu dienen, heimlich aus der umgebenden Funktion zu verschwinden um beim nächsten Mal genau dort wieder weiter zu machen, und man damit gedanklich zurecht kommt, schön. Damit kann man sicher übersichtlich AblaufSteuerungen definieren.

Wenn man weiss, dass diese Makros dazu dienen, heimlich aus der umgebenden Funktion zu verschwinden um beim nächsten Mal genau dort wieder weiter zu machen,

Das hast du schön formuliert. .... "heimlich" .... "verschwinden" .... ;-)

Was gefällt euch an Jomelos simpleThread nicht?

Hallo,

ich hatte ja schon einmal versucht zu fragen, bekam aber keine Antwort. Im Grunde ist das ja die negative Logik von deinem Intervall Projekt. Womit ich nicht klar komme vom Verständnis her ist folgendes. Man möchte doch das die loop so schnell wie möglich durchlaufen wird. Da widerspricht es doch das man hier feste Zeiten für andere Dinge frei gibt. Statt so schnell wie möglich sein Ding fertig zu machen und dann kann das nächste dran kommen.

ich hatte ja schon einmal versucht zu fragen, bekam aber keine Antwort.

Sorry, ich hatte deine Frage nicht verstanden. Auch jetzt noch nicht wirklich.

Aber, ich versuchs mal....

Man möchte doch das die loop so schnell wie möglich durchlaufen wird.

Das stimmt! Findet bei den ganzen Beispielen ja auch mehr als 100.000 mal pro Sekunde statt.

Da widerspricht es doch das man hier feste Zeiten für andere Dinge frei gibt.

Nöö... Denn die "freie" Zeit geht ja an loop(), im Grunde so, wie es das Interval Macro auch macht.

Ich halte es also nicht für inverse Logik, sondern eher für die gleiche.

Hmmm. War es das, was du wissen wolltest?

Nachtrag: Ah.... Ich glaube, jetzt... kommts mir...

Zu Anfang sagte ich:

taskSwitch() Gibt die Rechenzeit an andere Tasks ab. Es wird im nächsten Durchlauf an dieser Stelle weiter gearbeitet.

Der erste Teil ist wohl missverständlich. Die Rechenzeit wird nicht direkt an andere Tasks abgegeben, sondern der Programmfluss wird per Return an die aufrufende Funktion übergeben, und loop() kann dann andere Tasks aufrufen, bzw. zusätzliche Aufgaben erledigen. Das gilt natürlich auch für die anderen Taskkontrol Dinger..

skorpi08: Was gefällt euch an Jomelos simpleThread nicht?

Das kannte ich vorher noch nicht.

Auch sind meine taskMacros vermutlich schon einiges älter. Hatte sie nur noch nicht aufs Arduino Umfeld portiert.

Hallo,

genau jetzt gehts in die richtige Richtung. Ich hatte noch keine Zeit mir das näher anzuschauen, habe nur deine Texte hier dazu mehrfach gelesen. Also macht der Task sein Ding und wenn er fertig ist gehts in der loop normal weiter. Und wenn der Task selbst aktuell nichts zu tun hat blockiert er auch nicht sondern gibt sich sofort wieder frei zurück an loop. Okay, dass verstehe ich. Wie dein Intervall nur eben anders verpackt. Sehr schön. :) Werde das sicherlich bald selbst testen.

Was mache ich falsch :confused:

kurze Einleitung:

ich möchte einen Servo ansteuern und nebenbei andere dinge erledigen doch ich bekomme diese Fehlermeldung und habe keinen schimmer was falsch ist

Bitte um richtigstellung !!

#include 
#include 
Servo servo1;

void setup() {
  
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}

void loop() {

 bewegung();

}

void bewegung() {

 taskBegin();

  int position;


  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position);  
    taskPause(20);               
  }

  
  for(position = 180; position >= 20; position -= 2)
  {                                
    servo1.write(position);  
    taskPause(20);     
   
  }
  
}

Fehlermeldung:

 In function 'void bewegung()':

sketch_aug04a:40: error: expected '}' at end of input

 };

  ^

exit status 1
expected '}' at end of input
#include 
#include 
Servo servo1;

void setup() {
  
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}

void loop() {

 bewegung();

}

void bewegung() {  
  static int position;
  taskBegin();

  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position);  
    taskPause(20);               
  }

  
  for(position = 180; position >= 20; position -= 2)
  {                                
    servo1.write(position);  
    taskPause(20);     
   
  }
  taskEnd();
  
}

Die Meldung sagt: taskEnd(); vergessen

int position muss static werden, damit der Wert über mehrere Taskdurchläufe erhalten bleibt. Die Deklaration sollte vor taskBegin stattfinden.

Evtl fehlt da noch eine Schleife, damit sich das Gezappel wiederholt.

Danke für die Rückmeldung nach deinen Verbesserungen klappte es wie gewollt! Hoffe das es dabei bleibt!!

Hoffe das es dabei bleibt!!

Ach, sicher..... ich habe da volles Vertrauen.

Danke für die Rückmeldung

Und, dir Danke für den Test!

was mach ich jetzt falsch :(

#include 
#include 
Servo servo1;

void setup() {
  
  Serial.begin(38400);     
  delay(500);
  servo1.attach(9);

}



void bewegung() {

if(Serial.available())
{
  if(Serial.read() == "1")
  {


 taskBegin();

 static int position;

  while(1){
  
  for(position = 20; position < 180; position += 2)   
  {
    servo1.write(position);  
    taskPause(20);               
  }

  
  for(position = 180; position >= 20; position -= 2)
  {                                
    servo1.write(position);  
    taskPause(20);     
   
  }
  }
  }
  taskEnd();
}


void count() {

  taskBegin();

  static int zahl = 0;

  while(1) {

    Serial.println(zahl);
    zahl++;
    taskPause(100);
  }
  taskEnd();
}

void loop() {

 bewegung();
 count();
}

Fehlermeldung:

In function 'void bewegung()':

sketch_aug04a:19: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]

   if(Serial.read() == "1")

                       ^

sketch_aug04a:48: error: a function-definition is not allowed here before '{' token

 void count() {

              ^

sketch_aug04a:67: error: expected '}' at end of input

 }

 ^


exit status 1
ISO C++ forbids comparison between pointer and integer [-fpermissive]

Der Fehler ist genau was da steht. Du kannst C Strings (d.h. Null-terminiere char Arrays) nicht mit == vergleichen. Und C Strings und Integer vergleichen geht erst recht nicht. Wir sind hier nicht in Visual Basic. C++ ist zwar nur schwach typisiert und macht einige implizierte Konvertierungen, aber du musst schon etwas auf die Datentypen achten. Da steht was von Zeigern weil Arrays in Zeiger auf das erste Element zerfallen.

Das willst du allerdings auch nicht. 1 als ein ASCII Zeichen ist '1' (ASCII Code 49 und ein Integer) und nicht "1"