[Projekt] Multitasking

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
So ist er auch auf portablen Geräten lesbar. Das kannst Du auch noch nachträglich ändern.

Schön wäre auch, wenn Du unnötige Leerzeilen löschst.

Gruß Tommy

Okay, hab ich gemacht. Entschuldigung bitte.

epunkt:
Okay, Super das Du hier noch liest :))

Warum auch nicht.....

Mir fehlen die Enlosschleifen in deinen Tasks!
Von daher darfst du dich nicht wundern, dass es nur eine Runde läuft.

Endlosschleifen sind in dem Zusammenhang was ganz normales.
Jede Windows/Linux/FreeRtos usw. tut sowas, oder so ähnlich.
Darum findest du dieses auch in jedem meiner Beispiele.

Ansonsten, keine Ahnung, was dein Programm tun soll.
Und Formatiert ist es auch gruselig.
Ein STRG-T würde schon helfen..

Dankeschön für die Antwort. Strg T hab ich grade gemacht.....

Ich bin vielleicht etwas unbeholfen, nur zur Erklärung, nicht Entschuldigung, ich bin 62 und wollte mal etwas neues probieren. Arduino Mega + diese Motor Shield V1.0 | Seeed Studio Wiki H-Brücke sind die verbauten Komponenten.

Das Programm soll die einfachste Form einer Pendelzugsteuerung sein. Fahre nach rechts, bis der rechte Gleisbesetztmelder (GBM4) belegt meldet (digitalread(GBM4==0)) , dann bremse ab bis null, warte die Zeit die in takt festgelegt ist, beschleunige wieder nach links bis Zug am linken Bahnhof (GBM1) ist, bremse ab, warte die Zeit die in takt festgelegt ist usw.
Der Rest im Programm ist nur, damit der Zug auch losfährt, wenn er bei Programmstart irgendwo, jedenfalls nicht im rechten oder linken Bahnhof auf einem Gleisbesetztmelder steht und schlampige Rudimente aus Versuchen.

Das alles funktioniert bis auf das Warten.

Der Zug bremst, wartet beim ersten Durchlauf den takt ab, beschleunigt, bremst wieder am Gegenbahnhof, wartet dann aber nicht mehr, sondern fängt sofort wieder mit Beschleunigen in die Gegenrichtung an.

Irgendwas mach ich da falsch, und vielleicht kannst Du mir helfen. Das Problem beim Nachstellen ist, das der Code bei Dir sicher compiliert aber für den Ablauf eine fahrende Lok und die Gleisbesetzmelder braucht..

Danke und viele Grüße

Elmar

Dieses Multitasking System ist dafür gedacht, dass mehre Abläufe quasi gleichzeitig ablaufen können.
Wenn du es verwenden möchtest, wirst du dein Programm in Nebenläufigkeiten aufteilen müssen.

Im Moment, sehe ich noch nicht, wie man das tun könnte...

Das quasi gleichzeitig wird sofort benötigt wenn die zweite Strecke und der zweite Zug dazukommt. Mit einer Ausweichstelle in der Mitte der Strecke gehen dann sogar 3 Züge gleichzeitig. (Auf der Platte schon vorhanden)

Geht ja auch alles wie gewünscht, nur das Warten an den Bahnhöfen nicht. Wie kann ich das Lösn? Schau doch mal bitte....

--
Elmar

Hallo,

Edit: Bevor ich Ärger bekomme, gelöscht. In deinem eigenen Thread sehen wir uns vielleicht wieder.

epunkt:
Schau doch mal bitte....

Schade, dass die Kommunikation zwischen uns beiden nicht so richtig funktioniert.

Ein paar Probleme gibt es hier:
Du kaperst meinen Taskmakro Vorstellungsthread, anstatt einen eigenen aufzumachen.
Die Salamitaktik, die geht mir quer. (erst kein Code, dann eine unformatierte Wurst, dann 3 Züge)
Sagst mir, meine Makros funktionieren nicht. Bist aber nicht bereit dich den Bedingungen zu stellen (Endlosschleife einzubauen)
Und dann die dreiste Aufforderung: Mach du mal für mich....

Sorry, dass ich das etwas grob formuliert habe, aber, du siehst die Diskrepanz, zwischen deiner und meiner Erwartungshaltung, oder?


Meine Makros können dir den Bau von endlichen Automaten, Schrittketten und Ablaufsteuerungen vereinfachen.
Aber sie können nicht dein Denken ändern.
Das kannst nur du.

epunkt:
Geht ja auch alles wie gewünscht, nur das Warten an den Bahnhöfen nicht. Wie kann ich das Lösn?

Denke in Nebenläufigkeiten.

Und wenn es um 3 Züge geht, welche sich gleich verhalten sollen, dann wären eher die Taskobjekte für das Problem geeignet, als dieser eher primitive Vorläufer davon.
Auch ist da die Endlosschleife schon fest eingebaut.
Da gibts dann kein Weg drumrum.

Tipp:
Male Ablaufdiagramme, so wie es dir auf der Wikipedia Seite zu endlichen Automaten vorgemacht wird
Vielleicht verstehe ich dich ja dann.....

Servus, ich noch mal kurz.....

Keinesfalls wollte ich sagen, das die Makros nicht funktionieren.
Ich schrieb, das ich irgendwas nicht gerafft habe, weil taskWaitFor(condition) nur bei mir nur einmal
greift, und dann nie wieder.

Ist es so, das eine einmal gestartete Task eigentlich immer aktiv ist und nie endet?

Ich bin Deinem Rat gefolgt und hab mal überlegt wie Züge in der Wirklichkeit fahren (sollten :))
Da gibt es einen Fahrplan mit Abfahrtszeiten an den Bahnhöfen. Ab der Erkenntnis war alles ganz einfach, ein Array mit Abfahrtsminuten, also:

int BlinksAbfahrt[] = {2, 7, 12, 21, 26, 32, 36, 45, 49, 54, 58};
int BrechtsAbfahrt[] = {5, 10, 15, 24, 29, 34, 39, 48, 51, 56};
int BmitteAbfahrt[] = {3, 6, 8, 11, 13, 16, 22, 25, 27, 30, 33, 35, 37, 40, 46, 49, 50, 52, 55, 57, 59};

hat das Ganze einfach und flexibel gelöst. Läuft so natürlich auch für beliebig viele Züge, so sie unterschiedliche Gleise und Stromkreise haben und sieht auf der Platte ausreichend echt aus:)

die Makros für das beschleunigen und abbremsen sind nach wie vor drin und tun genau was sie sollen, nämlich das wenn grade gebremst wird bekommt der Prozessor trotzdem sofort mit wenn ein anderer Zug den Streckenabschnitt wechselt, was bei delay() definitiv nicht der Fall wäre.

--
Cool .signatures are so 90s...

Erstmal: Schön, dass es funktioniert.

Ist es so, das eine einmal gestartete Task eigentlich immer aktiv ist und nie endet?

Wenn sie die Gelegenheit bekommt, dann ja.

// leicht witzig, aber recht realistisch.


   const bool stromausfall = false;


   while(not stromausfall)
   {
     taskA();
     taskB();
   }

Hier Lesestoff wird das Prinzip beschreiben. Natürlich kann ein kleiner AVR das nicht alles, darum auch meine gnadenlos abgespeckte Version.

Schon vor Jahren habe ich mit dieser Konstruktion experimentiert.
Hier mein Link zu meinem Tutorial in 8 Lektionen mit 60 Seiten Dokumentation dazu und Sourcecn.
Vom einfachsten Multitasking bis zum Scheduler, Taskstart in Interrupts etc.
Für alle Prozessoren geeignet. In Deutsch.

@heweb
Schämst du dich gar nicht?

Wer sich McLeod nennt, der glaubt wohl auch an seine Einzigartigkeit :frowning:

Ich habe mir sein Tutorial nicht angeschaut - lohnt sich das denn?

Hallo, ich bin neu in diesem Forum und hoffe, das hier in diesen Threat noch jemand mitliest.
Ich benutze die Taskmacros von Combie. Die funktionieren soweit auch recht gut. Nun habe ich aber ein Problem, bei dem ich denke, das ich ohne eure Hilfe nicht mehr weiterkomme.
Das Problem ist folgendes:
Ich bzw. der Arduino arbeitet gerade in einem Task, aus dem ruft er eine andere Funktion auf. Nach Beendigung der anderen Funktion soll aber der ursprünglich Task nicht mehr weiter aufgerufen bzw. ausgeführt werden. Ich vermute, es liegt an der darin vorkommenden taskPause() oder er akzeptiert das return (um den Task zu verlassen) nicht und arbeitet stur nach dem Rücksprung in die Task die nächste Zeile (nach dem return) weiter.
Vielleicht kann mir da jemand auf die Sprünge helfen ??
Falls der Code (ewig lang und etwas unschön, weil ich ein alter Spagetti-Code-Programmierer bin) oder andere Angaben benötigt werden, kann ich das gerne machen, aber ich denke ihr stellt die Fragen, und ich liefere was nötig ist.
Vielen Dank im Voraus
Tom

Ich bzw. der Arduino arbeitet gerade in einem Task, aus dem ruft er eine andere Funktion auf. Nach Beendigung der anderen Funktion soll aber der ursprünglich Task nicht mehr weiter aufgerufen bzw. ausgeführt werden.

Könnte sein, dass das Konzept "Task" gar nicht passt, und du eher eine "State Machine" beschreibst.
?

Ich verstehe dein Problem auch nicht.

Das Konzept "Task anhalten", von Außen, ist bei den TaskMacros nicht vorgesehen.
Kooperation, ist das primäre Verhaltensmuster.

Möglichkeiten:

  • taskWaitFor() lässt die Task ruhen, bis die Bedingung wahr wird.
  • Task gar nicht erst aufrufen

Hallo erstmal, vielen Dank, dass ihr euch mein Problem mal angesehen habt.
Zur Erklärung bzw. Verdeutlichung, warum ich glaube, einen Task zu benötigen:
Eigentlich war die ursprüngliche Funktion kein Task. In der Funktion wurde, je nach Zustand der (Alarm-)Anlage, eine Taste auf einem TFT angezeigt. Das hat auch sehr gut funktioniert. Dann kam mir die Idee, die angezeigte Taste (rot) könnte nach Betätigung einen Countdown von 15 sekunden anzeigen. Während dieses Countdowns muss vom Benutzer eine Chipcard vor die Anlage gehalten werden. Wenn er das macht, springt die Taste wieder auf ihren Normalzustand um (grün).
Das hat aber nur mit einem Task funktioniert (zumindest ist mir keine andere Lösung eingefallen, um den Countdown darzustellen, ohne delay zu benutzen).
MIt dem Task läuft es auch so wie ich es mir vorstellte, aber sobald die ChipKarte an die Anlage gehalten wird, spring der Arduino von der Funktion (Alarm aus) wieder in den Task (Alarm an mit Countdown der Taste) und startet den Countdown neu.
Mein serieller Monitor zeigt mir das auch so an, das der Zustand von dem Task wieder geändert wird.
Ich weiss, das ist für Aussenstehende schwer zu verstehen, bzw. ich weiss nicht, wie ich es erklären soll.
Ich stelle mal einen Code-Abschnitt und einen Teil der Ausgabe am seriellen Monitor (mit jeweils ein paar Kommentaren meinerseits) zum hoffentlich besseren Verständnis mit ein.

Der Ausschnitt aus dem Programmcode

void alarm_an(unsigned char zimmer)
{
  Serial.println("*** alarm_an ***");
  /*
    Serial.print("alarm_zimmer_1: ");
    Serial.println(alarm_zimmer_1);
    Serial.print("alarm_zimmer_2: ");
    Serial.println(alarm_zimmer_2);
  */
  if (zimmer == Zimmer_1)
  {
    //    zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1 aus", 2);
    //    zimmer_1_btn.drawButton(false);
    alarm_zimmer_1 = ON;
    alarm_tft_anzeige(Zimmer_1, "alarm1");
  }
  else if (zimmer == Zimmer_2)
  {
    zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, RED, WHITE, "Alarm 2 aus", 2);
    zimmer_2_btn.drawButton(false);
    alarm_zimmer_2 = ON;
  }
  alarm_status = 1;
  Wire.beginTransmission(8);
  Wire.write(alarm_status);
  Wire.endTransmission();
  relay_SetStatus(zimmer, ON);         // Relais für alarmauslösendes Zimmer an
}

Task alarm_tft_anzeige(unsigned char zimmer, char alarm_taste_status[])
{
  Serial.println("*** alarm_tft_anzeige ***");
  Serial.print ("zimmer: ");
  Serial.println (zimmer);
  Serial.print ("alarm_taste_status: ");
  Serial.println (alarm_taste_status);
  Serial.print ("alarm_taste_tft_has_to_be_changed: ");
  Serial.println (alarm_taste_tft_has_to_be_changed);
  Serial.print ("countdown: ");
  Serial.println (countdown);
  taskBegin();
  while (1)
  {
    if (zimmer == Zimmer_1)
    {
      if (alarm_taste_status == "ok")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, GREEN, BLACK, "Zimmer 1 OK", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "alarm1")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1 aus", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "alarm2")
      {
        if (alarm_taste_tft_has_to_be_changed == true)
        {
          zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "Alarm 1", 2);
          zimmer_1_btn.drawButton(false);
          alarm_taste_tft_has_to_be_changed = false;
          return;
        }
      }
      else if (alarm_taste_status == "warten")
      {
        if (countdown > 14)
        {
          alarm_taste_tft_has_to_be_changed = true;
          // alarm_taste_status = "alarm2";
          alarm_tft_anzeige(Zimmer_1, "alarm2");
          return;
        }
        if (keycard_status == true)
        {
          keycard_check = false;
          alarm_aus(Zimmer_1);                              //<-- hier springt er zu der Funktion, welche ich im ersten Posting gemeint habe
          /*          alarm_taste_status = "ok";
                    alarm_taste_tft_has_to_be_changed = true;
                    alarm_tft_anzeige(Zimmer_1, "ok");*/
          return;                                                  //<-- hier sollte er den Task dann verlassen und gut is, aber er überspringt diese Anweisung bzw. er sollte nie hier landen, da der alarm_taste_status eigentlich "ok" sein sollte
        }
        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, BLACK, text[countdown], 2);
        zimmer_1_btn.drawButton(true);

        countdown++;
        taskPause(1000);
      }
      else
      {
        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, RED, WHITE, "ERROR", 2);
        zimmer_1_btn.drawButton(false);
      }
    }
    else if (zimmer == Zimmer_2)
    {
      zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, RED, WHITE, "Alarm 2 aus", 2);
      zimmer_2_btn.drawButton(false);
      alarm_zimmer_2 = ON;
    }
  }
  taskEnd();
}

void alarm_aus(unsigned char zimmer)
{
  Serial.println("*** alarm_aus ***");
  Serial.print("alarm_zimmer_1: ");
  Serial.println(alarm_zimmer_1);
  Serial.print("alarm_zimmer_2: ");
  Serial.println(alarm_zimmer_2);
  if (zimmer == Zimmer_1)
  {
    if (alarm_zimmer_1 == ON)
    {
      alarm_zimmer_1 = OFF;
      if (menue_flag == true)
      {
        menue_flag = false;
        display_change = true;
      }
      else
      {
        alarm_taste_tft_has_to_be_changed = true;
        alarm_tft_anzeige(Zimmer_1, "ok");
        //        zimmer_1_btn.initButton(&tft, 175, 70, 300, 40, WHITE, GREEN, BLACK, "Zimmer 1 OK", 2);
        //        zimmer_1_btn.drawButton(false);
        display_change = false;
        //      alarm_taste_tft_has_to_be_changed=true;
      }
    }
  }
  else if (zimmer == Zimmer_2)
  {
    if (alarm_zimmer_2 == ON)
    {
      alarm_zimmer_2 = OFF;
      if (menue_flag == true)
      {
        menue_flag = false;
        display_change = true;
      }
      else
      {
        zimmer_2_btn.initButton(&tft, 175, 130, 300, 40, WHITE, GREEN, BLACK, "Zimmer 2 OK", 2);
        zimmer_2_btn.drawButton(false);
        display_change = false;
      }
    }
  }
  Serial.print("display_change: ");
  Serial.println(display_change);
  if (alarm_zimmer_1 == OFF && alarm_zimmer_2 == OFF)
  {
    alarm_status = 2;
    keycard_check = 0;
    Wire.beginTransmission(8);
    Wire.write(alarm_status);
    Wire.endTransmission();
    countdown = 0;
  }
  relay_SetStatus(zimmer, OFF);        // Relais für alarmauslösendes Zimmer aus
}

Und hier der Ausschnitt aus dem seriellen Monitor

*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: warten
alarm_taste_tft_has_to_be_changed: 1
countdown: 2
*** keycard_access ***
Authorize with access card or Chip
Authorized access chip
keycard_status: 1
keycard_card: 0
keycard_chip: 1
menue_flag: 0
alarm_status: 1
keycard_check: 0
 
*** keycard_alarm_aus ***
*** alarm_aus ***
alarm_zimmer_1: 1
alarm_zimmer_2: 0
*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: ok                           //<-- an der Stelle ist alles ok
alarm_taste_tft_has_to_be_changed: 1
countdown: 3
display_change: 0
*** alarm_aus ***
alarm_zimmer_1: 0
alarm_zimmer_2: 0
display_change: 0
*** alarm_tft_anzeige ***                    //<-- hier sieht man, das er in den Task zurückkehrt, also auch noch ok
zimmer: 24
alarm_taste_status: warten                   //<-- hier setzt er eigenwillig seinen alten Zustand (vor Verlassen des Task) wieder ein (So ein pösser Pursche ;-))
alarm_taste_tft_has_to_be_changed: 1
countdown: 0
*** keycard_access ***
Authorize with access card or Chip
*** alarm_tft_anzeige ***
zimmer: 24
alarm_taste_status: warten
alarm_taste_tft_has_to_be_changed: 1
countdown: 0

Hoffentlich habe ich jetzt endgültig alle Klarheiten beseitigt :wink:
Tom

Dein Problem ist delay(), ohne brauchst Du keinen Tasks.

Schau Dir BlinkWithoutDelay in den Beispielen der IDE an und verstehe es.
Dabei kann Dir z.B. die Nachtwächtererklärung helfen.

Gruß Tommy

if (alarm_taste_status == "alarm1")

So vergleicht man keine Zeichenketten, sondern Zeiger.
Und das willst du sicherlich nicht.

@Tommy56
OK - Ich verstehe BlinkWithoutDelay und meine ich hätte das auch schon mal versucht. Aber ich werde es nochmal versuchen, es in meinen Fall einzubauen. Ich melde mich dann wieder, wenn ich ein Ergebnis habe (hoffentlich ein positives).

@combie
Sorry für die doofe Gegenfrage - wie würde es denn richtig gehen? Mit strcmp?

Danke schon mal im Voraus
Tom