Moin, ich gebe zu, den Text zu schreiben war leichter, als einen aussagekräftigen Titel zu finden. Ich hoffe, ich habe nicht einfach nur nicht die richtigen Suchbegriffe im Kopf, um eine Lösung zu googlen :-/ Ich versuche mal, meine Problemstellung abstrakt zu beschreiben, um euch nicht unnötig mit fachlichen Nebensächlichkeiten zu belästigen. Bei Bedarf kann ich aber natürlich auch gern Details liefern. Folgende Ausgangssituation: Über einen Arduino Mega steuere ich diverse Relais an, die dann u.A. Elektromagneten steuern. Der Anwendungsfall erfordert dabei regelmäßig Pausen, also z.B. 1. Releais 1 durchschalten (=> PIN X = HIGH) 2. Delay(500) 3. Relais 1 aus 4. Delay(10000) 5. Relais 2 durchschalten 6. Delay(500) ... Stand jetzt passiert das in der loop Funktion. Die läuft dadurch recht lang, das störte bislang aber auch nicht. In einer späteren Ausbaustufe soll noch eine Überwachung des Systems durch von Sensoren ausgelöste Interrupts implementiert werden. Nun möchte ich in den Ablauf gerne per Netzwerk eingreifen können: Ich will per HTTP Anfrage ebenfalls Steuerbefehle absetzen (zB ebenfalls einen Magneten für 500ms anschalten). Diese sollen dann "direkt" durchgeführt werden. In der aktuellen loop Fkt (ein mal) auf neue Requests zu prüfen, ist also kein valider Ansatz. Überlegt habe ich folgendes: Variante 1 In der loop Fkt bei "jeder Gelegenheit" auf Requests prüfen und ggfs. ausführen. Also z.B. statt dem o.g. 10s Delay 10x jeweils nur 1s Delay und dann auf Requests prüfen. Requests dann an passender Stelle abarbeiten. + sehr einfach unzusetzen - Fühlt sich gehackt an - Verzögert die eigentliche "Geschäftslogik" trotzdem Variante 2 Ich verändere die Architektur des Systems: Die Logik wird aus dem Arduino raus in eine eigene Anwendung verlagert, die dann auch von der UI angesprochen wird. Der Arduino nimmt per HTTP nur noch einzelne Befehle vom neuen Backend entgegen (Magnet X für n ms an), die mit jeder loop ausgelesen und umgesetzt werden können. + recht einfach umzusetzen + Wäre die bevorzugte Lösung, wenn meine Anforderungen anders nicht vernünftig abgebildet werden können - erfordert weitere dauerhaft verfügbare Hardware für das neue Backend - Logik und Umsetzung liegen auf zwei Systemen. Mein Bauchgefühl sagt, dass das Debugging unangenehmer wird Variante 3 Ich nutze einen Timer, um regelmäßig nach neuen Requests zu gucken. Das wäre dann aber auch nicht viel anders als ein asynchron laufender Webserver, siehe Variante 4 Variante 4 Ein asynchroner Webserver, der die Requests entgegen nimmt und abarbeitet. + wenig Aufwand - Wenn ein Magnet zB für 1s geschaltet wird, müsste dort ein entsprechendes Delay aufgerufen werden (geht das überhaupt?). Solange wäre dann die loop Fkt blockiert, ein zuvor aktiviertes Relais wäre dann ggfs. zu lange aktiv. Variante 5 Ich führe eine Liste von "Jobs", also einem Tupel aus Ausführungszeitpunkt t und einem Task T (zB Magnet A anschalten, etc.). loop() prüft, ob der nächste Job in der Vergangenheit liegt und führt ihn dann aus. Jobs werden erzeugt 1. in setup() 2. aus einem speziellen Task heraus: bislang hat die loop Methode die Logik ja immer wieder neu gestartet. Da das jetzt entfällt, lege ich als letzten Job einen Task an, der alle Jobs für den automatischen Ablauf erneut erzeugt. 3. durch einen (asynchronen) Webserver, der die HTTP Requests umsetzt. + Zumindest in der Theorie eine vollständige Lösung, klingt für mich am vielversprechendsten - Aufwendigste Lösung. Insbesondere, da aus höheren Programmiersprachen gewohnter Komfort wie Listen mit dynamischer Länge oder automatischer Sortierung fehlt. Einen Semaphor oÄ bräuchte man auch, damit die Liste nicht vom Webserver und der loop Methode parallel manipuliert wird. Vielleicht übersehe ich auch irgendwas und das geht viel leichter? Ich bin schließlich nicht der erste mit so einem Problem? Dann immer her mit den Ideen. Beruflich komme ich aus der Java Ecke. Entgegen der herrschenden Vorurteile lösen wir da nicht jedes Problem mit mehr RAM oder einer schnelleren CPU - aber es ist zumindest immer eine valide Option ;) Gruß RoCMe
Tja, das Arduino Konzept ist genial, solange du im Mainloop millis() abfragst und die Arbeit verteilst. Wenn das nicht mehr ausreicht, verursacht die Arduino Lib mehr Probleme, als sie löst. Einen zusätzlichen Vorschlag hätten wir noch. Wenn die Echtzeitanforderungen nichts zu hoch sind - eine Linux-Platine. Da kannst du sogar einen der Java Webserver benutzen.
https://www.mikrocontroller.net/articles/Statemachine https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
:
Bearbeitet durch User
es gibt auch genug Arduino Cores mit eingebautem RTOS: die ESP haben FreeRTOS drin, die Mbed Cores haben RTX drin und sind z.B. im RP2040, Nano33 oder Portenta. Gut, letzterer ist etwas teuer.
Sowas kann man auch prima mit einer "State-Maschine" innerhalb eines ausreichend-schnellen Timer-INT lösen.
Schreib dir ne eigene Delay-Routine. Sobald millis() einen anderen Wert hat als zB millis2 hat zählst du in deiner eigenen Delay-Routine alle Variablen rauf. Dann setzt du millis2=millis(). Gleichzeitig führst du bei jedem Durchlauf von deiner eigenen Delay-Routine alle wichtigen Funktionen weiter aus, die dann nicht blockiert werden. Fertig Dafür brauchts keine Lib oder ein kompliziertes OS.
Robert M. schrieb: > Vielleicht übersehe ich auch irgendwas und das geht viel leichter? Ich > bin schließlich nicht der erste mit so einem Problem? Dann immer her mit > den Ideen. Ich verwende eine vereinfachte Form von dem Adam Dunkels seinen Protothreads.
Zur Anregung und selbst draufkommen: Benutze keine delay Anweisung. Tipps dazu hat Harry schon geliefert: Beitrag "Re: Arduino: Handling parallel anfallender Aufgaben" Ein Timer der jede Millisekunde eine Variable hochzählt, sollte zur Lösung führen. Dann hat man genug Freiraum in der Mainroutine für einen Web-Server.
Robert M. schrieb: > Variante 1 > In der loop Fkt bei "jeder Gelegenheit" auf Requests prüfen > und ggfs. ausführen. Also z.B. statt dem o.g. 10s Delay 10x > jeweils nur 1s Delay und dann auf Requests prüfen. Requests > dann an passender Stelle abarbeiten. > + sehr einfach unzusetzen > - Fühlt sich gehackt an Tatsächlich? Aber es fühlt sich für Dich nicht "gehackt" an, den Begriff des Algorithmus (also einer Vorschrift, die nach endlich vielen Schritten terminiert) zwanghaft auf einen permanent laufenden Prozess (wie zum Beispiel das Steuern eines Elektrokraftwerkes oder einer Produktionslinie) anzuwenden? Wo finde ich übrigens an einer Turing-Maschine die I/O-Ports?
Uwe K. schrieb: > Zur Anregung und selbst draufkommen: Benutze keine delay Anweisung. Ist zwar notwendig, aber noch nicht hinreichend. Allgemeiner: Keine Warteschleifen benutzen -- also auch nicht darauf warten, dass ein bestimmter Input-Port einen bestimmten Wert annimmt.
so kompliziert ist weiterer Task auch nicht:
1 | void io_thread_fn() { |
2 | // setup |
3 | DigitalOut led(p6); |
4 | |
5 | // loop |
6 | while(true) { |
7 | led = !led; |
8 | ThisThread::sleep_for(100ms); |
9 | } |
10 | } |
11 | |
12 | Thread io_thread; |
13 | |
14 | void setup() { |
15 | io_thread.start(io_thread_fn); |
16 | } |
17 | |
18 | void loop() { |
19 | // tu irgendwas |
20 | } |
Ein RPi Pico hat genug Ressourcen dafür.
:
Bearbeitet durch User
> Dafür brauchts keine Lib oder ein kompliziertes OS.
Dafür nicht, aber kommen beim Webserver immer mehr Anforderungen dazu?
WLAN, Fehlermeldungen, Datenlogger....
Relay und 500ms sehen dagegen recht Problemlos aus.
Sieht eher so aus, als sollte man das Problem von der umgekehrten Seite
anpacken. Lib oder OS für den Webserver auswählen, über USB nachdenken
und eine Hardware auswählen, an die man auch ein Relais anschließen
kann.
"Blink without delay" wurde bereits genannt. Lass mal 3 LEDs unabhängig voneinander blinken, dann klärt sich auch der Relais-Ablauf, der Webserver und das 500ms Gepulse von deinem Relais.
ein sehr einfacher Weg: loop() { //lässt alle 10ms jeden Task 1x laufen Task1(); Task2(); Task3(); delay(10); } In den einzelnen Tasks keine Delays nutzen (!!!) sondern Zustandsmaschinen und Timervariablen hochzählen Task1() { //inkrementiert ca. alle 10ms timer1++; if ( timer1 == 100 ) { timer1 = 0; //mache jede Sekunde irgendwas... } } Wenn du ein genaueres Timing willst, kannst du beim Eintritt in die loop Funktion millis abfragen und am Ende auch. Dann weißt du, wie lange ein Durchlauf gedauert hat und du kannst die Differenz zu 10ms gezielt warten. Das ist sehr einfach und rudimentär und auch nicht sehr genau, reicht zumindest bei meinen Projekten aber bisher immer aus.
Heinz schrieb: > Das ist sehr einfach und rudimentär und auch nicht sehr genau, reicht > zumindest bei meinen Projekten aber bisher immer aus. Tja, dann kannst auch du noch ein wenig lernen, wie man sehr einfach die berühmt-berüchtigte millis() Funktion benutzt und damit recht genaue Zeitintervalle hinkriegt, ganz ohne Schätzung und Handstände. Beitrag "Re: mehrere Millis()"
schrieb Heinz doch das man die millis für genaueres Timing benutzen kann. Der Haken ist eher das es kooperatives Multitasking ist, alle müssen brav mitspielen. Wenn ein Display aber z.B. 50 ms für einen Refresh braucht kann da nicht ein Sensor zyklisch mit 10 ms gelesen werden. Variante 4 mit AsyncWebserver ist elegant. Mit einer EventQueue kann man das asynchrone Konzept dann weiterführen, so wie es JavaScript auch macht.
1 | Thread event_thread; |
2 | events::EventQueue queue; |
3 | DigitalOut relais(p10); |
4 | |
5 | void setup() { |
6 | event_thread.start(callback(&queue, &events::EventQueue::dispatch_forever)); |
7 | |
8 | // webserver startet dies um ein Relais 2s einzuschalten |
9 | relais = 1; |
10 | queue.call_in(2s, [](){ |
11 | relais = 0; |
12 | } |
13 | ); |
14 | } |
Es können beliebige Funktionen in die Q geworfen werden, call_in() ruft die Funktion dann nach der angegebenen Zeit auf. Die Funktion selber läuft im event_thread, könnte aber auch in der loop() laufen. Q calls dürfen auch aus einer ISR erfolgen, damit kann man also ein printf aus einer ISR starten. Das geht wie gesagt z.B. beim RPi Pico und sollte auch beim Pico W(ireless) funtkionieren, da hätte man dann auch den Webserver. Ich werde es ausprobieren.
Moin, vielen Dank fur die schnellen und zahlreichen Antworten. Ganz generell: Ich will aus Gründen beim Arduino bleiben, hätte ich vielleicht noch mal erklären sollen. Die meisten Vorschläge (Analogie zu "3 LEDs unabhängig blinken lassen", State Machine, etc.) intepretiere ich zusammengefasst so, dass ich mir für jedes Relais / jeden Magneten merke, wann er das nächste mal seinen Zustand wechseln soll und dann in loop jeweils prüfe, ob etwas geändert werden muss. Das hatte ich als "Variante 0" im Kopf, habs aber direkt wieder verworfen :D Hintergrund: Eigentlich gibt es selten was zu tun, die meiste Zeit würde also unnötig geprüft, ob man nun endlich mal den Zustand ändern kann. Da klang für mich Neuling die Verwendung von delay als Mittel der Wahl. Energiesparen ist ja aktuell in und so ;) Aber vermutlich ist das einfach ein Unterschied zur Entwicklung in "großen" Rechnern, in denen es inzwischen viele Möglichkeiten für Parallelisierung gibt. Letztlich ist meine Variante 5 ja auch nur eine Variante dieses Vorschlags: Statt bei jedem Durchlauf für jedes Relais zu prüfen, ob ein Status geändert werden muss, merke ich mir, wann das nächste mal überhaupt etwas geändert werden muss (und was). Damit entfallen unnötige Prüfungen, und ich kann länger idlen. Wobei, vielleicht ist das auch mein Denkfehler: Ich habe das Delay bislang verstanden als "leg dich schlafen, ich weck dich, wenn du weiter machen sollst." Wenn da intern nichts anderes als ne Zählshleife durchläuft, bringt das auch keinerlei Einsparungen mehr.
Robert M. schrieb: > Folgende Ausgangssituation: Über einen Arduino Mega steuere ich diverse > Relais an, die dann u.A. Elektromagneten steuern. Der Anwendungsfall > erfordert dabei regelmäßig Pausen,... Nein, das tut er nicht, sondern die verschiedenen Teil-Aufgaben sollen zu unterschiedlichen Zeiten erledigt werden. Eigentlich ist es ganz einfach: programmiere nicht stur geradeaus, wie man es bei einem PAP oder dem Rumpelstilzchen gelernt hat (heute: back ich, morgen: brau ich, übermorgen: ...), sondern stelle deine Programmierweise um von geradeaus auf ereignisorientiert und schaffe dir 'verzögerte Ereignisse' oder auf neudeutsch delayed events. Das ist einfach ein Mechanismus in der Firmware, dem man Aufträge geben kann zum Erzeugen eines Ereignisses nach einer vorgebbaren Zeitspanne. Etwa so: bool Add_Delayed_Event (dword millisec, dword aEvent); Dazu brauchst du eine Systemuhr, also etwas, das eine Uhrzeit in der Firmware führt. Die muß nicht mit der tatsächlichen Uhrzeit übereinstimmen, aber sie muß die Zeit in sinnvollen Stücken zählen können, z.B. 1 ms oder 10 ms - und zwar unabhängig von deinen sonstigen Programmteilen. Das bei Arduino-Jüngern allseits beliebte 'millis' ist dazu ungeeignet. Tja und in der Grundschleife in main() fragt man nur die normale Event-Warteschlange ab, ob da etwas vorhanden ist und wenn, holt man es ab und reagiert sinnvoll darauf. Mal kurz ein Szenario: Event 'evBitteLüften' kommt rein also begin MacheFensterAuf(); Add_Delayed_Event (500, evFensterSchließen); end Dann kommt nach 500 ms der Event 'evFensterSchließen' rein und man kann darauf sinnvoll reagieren. In der Zwischenzeit kann man sich um andere Dinge kümmern oder in der Grundschleife kreiseln. W.S.
RPi Pico und Nano33 sind Arduinos, meine Beispiele sind das Arduino Framework, eben die mit Mbed core. Gibt es nur nicht für die kleinen AVR Arduinos. Mein letztes Beispiel macht genau das was W.S. meint, aber in elegant und mit eingebauten Mitteln wenn man den richtigen Arduino hat.
:
Bearbeitet durch User
Robert M. schrieb: > Die meisten Vorschläge (Analogie zu "3 LEDs unabhängig blinken lassen", > State Machine, etc.) intepretiere ich zusammengefasst so, dass ich mir > für jedes Relais / jeden Magneten merke, wann er das nächste mal seinen > Zustand wechseln soll und dann in loop jeweils prüfe, ob etwas geändert > werden muss. > > Das hatte ich als "Variante 0" im Kopf, habs aber direkt wieder > verworfen :D Genau so geht das aber nun mal. Ich renne in der loop herum, bis millisec() größer Zeit(x) ist, setze Zeit(x) auf einen neueren Wert und springe dann zu einer Funktion "void machewas()". > Hintergrund: Eigentlich gibt es selten was zu tun, die meiste Zeit würde > also unnötig geprüft, ob man nun endlich mal den Zustand ändern kann. Das ist nun mal der doofe Job des µC, mangels Gewerkschaft beklagt der sich aber nicht über seine stupide Beschäftigung. > Da > klang für mich Neuling die Verwendung von delay als Mittel der Wahl. Delay ist doof, weil es den Programmablauf blockiert. > Energiesparen ist ja aktuell in und so ;) Unfug, ob in der loop herumrennen oder delay zu machen, ändert nichts am Stromverbrauch. Mal messen? Und überhaupt, um welche Größenordnung reden wir denn? Mein aktuell vollendeter Aufbau mit ProMini (3V3, LED und Regler entfernt) und etwas Peripherie nimmt um die 5 mA aus dem LiIon-Akku, das sind um 20 MILLIwatt. Hätte ich stattdessen einen Nano oder Uno, würde schon deren USB-Baustein die Stromaufnahme mindestens verdoppeln.
Robert M. schrieb: > Wenn da intern nichts anderes als ne Zählshleife > durchläuft, bringt das auch keinerlei Einsparungen mehr. Das hast du richtig erkannt!
1 | #include <TaskMacro.h> |
2 | |
3 | |
4 | /**
|
5 | * Auf Tastendruck wird 5 mal geblinckt
|
6 | */
|
7 | |
8 | |
9 | |
10 | |
11 | const byte taster = 4; // Taster zwischen Pin und GND |
12 | const byte led = 13; // Led zwischen Pin und GND |
13 | const unsigned long interval = 500; // ms |
14 | const byte zyklen = 5; // Anzahl Blinker |
15 | |
16 | bool blinkAnforderung = false; // Merker für Blink Anforderung |
17 | |
18 | Task blink() |
19 | {
|
20 | static byte i = 0; // Wiederholungszaehler |
21 | taskBegin(); |
22 | while(1) |
23 | {
|
24 | taskWaitFor(blinkAnforderung); |
25 | for(i=0;i<zyklen;i++) |
26 | {
|
27 | digitalWrite(led, HIGH); // leuchte an |
28 | taskPause(interval); |
29 | digitalWrite(led, LOW); // leuchte aus |
30 | taskPause(interval); |
31 | }
|
32 | blinkAnforderung = false ; // Anforderung konsumieren |
33 | }
|
34 | taskEnd(); |
35 | }
|
36 | |
37 | |
38 | Task tastenAuswertung() |
39 | {
|
40 | taskBegin(); |
41 | while(1) |
42 | {
|
43 | taskWaitFor(!digitalRead(taster)); |
44 | blinkAnforderung = true; |
45 | }
|
46 | taskEnd(); |
47 | }
|
48 | |
49 | |
50 | Task showLoops() |
51 | {
|
52 | static unsigned long loops = 0; |
53 | loops++; |
54 | taskBegin(); |
55 | for(;;) |
56 | {
|
57 | taskPause(1000); |
58 | Serial.print("Loops pro Sekunde: "); |
59 | Serial.println(loops); |
60 | loops = 0; |
61 | }
|
62 | taskEnd(); |
63 | }
|
64 | |
65 | |
66 | |
67 | |
68 | void setup() |
69 | {
|
70 | Serial.begin(9600); |
71 | pinMode(led, OUTPUT); |
72 | pinMode(taster,INPUT_PULLUP); |
73 | }
|
74 | |
75 | void loop() |
76 | {
|
77 | blink(); |
78 | tastenAuswertung(); |
79 | showLoops(); |
80 | }
|
Wie schon gesagt, nach dem Adam Dunkels seinem Strickmuster.
Robert M. schrieb: > Wobei, vielleicht ist das auch mein Denkfehler: Ich habe das Delay > bislang verstanden als "leg dich schlafen, ich weck dich, wenn du weiter > machen sollst." Wenn da intern nichts anderes als ne Zählshleife > durchläuft, bringt das auch keinerlei Einsparungen mehr. Wenn man von einem "richtigen Computer" kommt sind die Einschraenkungen eines Mikrocontrollers schon gravierend: delay() [bei Arduino-Framework] ist im wesentlichen eine Zaehlschleife, eventuell von TimeTick unterbrochen. "Ich will beim Arduino bleiben" solltest Du etwas besser spezifizieren: Wenn Du heute auf die Arduino-Webseite (www.arduino.cc) guckst, hast Du kleine Achtbitter (Arduino Uno oder Nano, AVR), M0-ICs (z.B. Arduino Nano RP2040, Arduino MKR 1XXX, M0-Kern[e]) oder gar den Portenta H7 (enthaelt den STM32H747, das ist ein M7-Kern und ein M4-Kern). Du merkst es gibt viele Moeglichkeiten: Ich (ich ich ich!!!!) finde den Arduino Zero als das beste wo es gibt wegen des InCircuit-Debuggers. Ist so wie ein StLink fuer STM32. Die geschenkte Lebenszeit ist jeden Cent wert. Gruesse Th.
Uiuiui, ihr seid Mal wieder so schnell, ich komme nicht hinterher :D @jojos: Sorry, wieder was gelernt :) Ich wusste wohl, dass es verschiedene Arsuinos gibt, aber diese Bezeichnungen sagten mir nichts. Hätte ich googlen sollen. @Manfred Das mit dem Energiesparen war nixht ganz ernst gemeint. Ich habe aber wirklich angenommen, dass ein Delay weniger Strom frisst => noch mal was gelernt. Letztlich konnte ich mir einfach nicht vorstellen, dass das kontinuierliche Durchlaufen "State of the Art" ist. Das aktuelle Setup besteht aus einem Arduino Mega (bequem, weil viele IO Pins). Das ist wohl nixh wichtig, wenn man Hardware Anpassungen für den Moment ausschließen will. In der loop selbst handlen bekäme ich sicher hin. Aber vielleicht gibt es ja doch noch eine elegante Möglichkeit für "delayed events" auf dieser Plattform?
Robert M. schrieb: > Letztlich konnte ich mir einfach nicht vorstellen, dass das > kontinuierliche Durchlaufen "State of the Art" ist. Ein sleep() mit PowerDown gibt es noch (mit anderen Problemen und Benefits). Aber was eine CPU noch machen? Je nach Input (also: z.B. Flanken an I/O-Ports, Timer) kannst Du Interrupts verwenden (praktisch als Input Deiner State-Maschine in Deine grosse Loop). Oder z.B. kleine RTOS-System (z.B. FreeRTOS). Hat auch seinen Charme, sind stabil und braucht nur einen Timer-Interrupt. Gruesse Th.
Robert M. schrieb: > Letztlich konnte ich mir einfach nicht vorstellen, dass das > kontinuierliche Durchlaufen "State of the Art" ist. Doch schon.... z.B. Bei einem Arduino Mega, bringt das schlafen nicht all zuviel. Um eine evtl. vorhandene Batterie zu schonen sollte man ein anderes Board wählen. Dann kann man über schlafen nachdenken. Robert M. schrieb: > Aber vielleicht gibt > es ja doch noch eine elegante Möglichkeit für "delayed events" auf > dieser Plattform? Gibt es. Verschiedenste. Solche Queues wollen allerdings auch angelegt und verwaltet werden. Das von mir gezeigte Programm schafft auf einem UNO etwas über 72 Durchläufe pro Millisekunde. Ja, alle 3 Tasks kommen ein mal pro Durchlauf an die Reihe.
Hallo EAF, das sieht sehr spannend aus, werde mir das morgen Mal genauer ansehen. Beim drüber Lesen ist mir nur aufgefallen, dass du in der Loop Methode die drei Tasks jedes Mal neu ausführst. Müssten die nicht eigentlich nur einmal gestartet werden und dann "von selbst" laufen? Aber vielleicht ist es auch einfach nur schon zu spät für mich :)
Robert M. schrieb: > Hintergrund: Eigentlich gibt es selten was zu tun, die > meiste Zeit würde also unnötig geprüft, ob man nun > endlich mal den Zustand ändern kann. Da klang für mich > Neuling die Verwendung von delay als Mittel der Wahl. Hmm. Eigentlich hätte ich "sleep()" für das Mittel der Wahl gehalten. Weiss nur nicht, ob Arduino so etwas kennt...
Robert M. schrieb: > Müssten die nicht eigentlich nur > einmal gestartet werden und dann "von selbst" laufen? Da es bei dieser einfachen Kooperativen Variante keinen Scheduler gibt, geht es nicht anders. Hier ist loop() der "Scheduler", welcher dafür sorgt, dass jeder mal dran kommt. Habe auch noch eine OOP fähige Variante im Köcher, die eben diesen Automatismus implementiert hat.
Grummler schrieb: > Eigentlich hätte ich "sleep()" für das Mittel der Wahl > gehalten. Weiss nur nicht, ob Arduino so etwas kennt... Jain! Die µC können es. Lohnt nur nicht wirklich. Beim Mega ist noch ein ATmega16U2 drauf, und noch so Beiwerk, wie Opamp, Regler, Led
Robert M. schrieb: > In der loop selbst handlen bekäme ich sicher hin. Aber vielleicht gibt > es ja doch noch eine elegante Möglichkeit für "delayed events" auf > dieser Plattform? Ohne Betriebssystem programmiert man völlig anders als mit OS. "Delayed events" macht man mit "state machines" Schrittschaltwerken Zustandsautomaten (verschiedene Wörter für ein- und dasselbe). Also einfach ein switch-statement in das Programm loop() eingebaut und bei jeder Zustandsänderung wird weitergeschaltet. Mit verschachtelten Zustandsautomaten kann man beliebige Abläufe realisieren. Eine Zustandsänderung kann zum Beispiel die Änderung des Rückgabewerts der Funktion millis() sein. In den Beispielen gibt es "Blink ohne Delay". Da wird gezeigt, wie es geht. Ein großer Vorteil beim Arbeiten mit Schrittschaltwerken ist die Tatsache, daß die Ausgabe aller Schrittvariablen genau zeigt, wo das Programm gerade steht. Hilft ungemein bei der Fehlersuche. Gruß Klaus (der soundsovielte)
Manfred schrieb: > Ich renne in der loop herum, bis millisec() größer Zeit(x) ist,... und was machst du, wenn 2 oder mehr Aktionen zu unterschiedlichen Zeiten getan werden müssen? Wenn also sich die Zeitspannen quasi überlappen? Nein, das ist alles Geradeausprogrammierung - und die führt immer zu unsäglichen Komplikationen, wenn mehr als nur jeweils eines getan werden soll. Die Lösung ist eben, einen zweiten Akteur zu haben, was in meinem Beispiel eben der Uhrtick ist, der ganz einfach neben dem Hauptprogramm läuft und zum rechten Zeitpunkt eine Mitteilung schickt "jetzt ist es Zeit, dies (oder das) zu tun". Die nächste und weitaus aufgebohrtere Version wäre ein Multitasksystem, aber sowas eher fettes braucht man in einer durchschnittlichen Mikrocontroller-Anwendung zumeist nicht. Nochwas: Dieses Proto-Zeugs von Adam Dunkel ist eine mittelgroße Katastrophe, denn es ist im Grunde nur eine Präprozessor-Akrobatik und dahinter steckt eine switch-case-Anweisung. Eigentlich nur eine Verkleidung, so daß man das C-Konstrukt dahinter nicht mehr erkennen kann. Dafür würde ich den Adam teeren und federn. W.S.
Robert M. schrieb: > @Manfred Das mit dem Energiesparen war nixht ganz ernst gemeint. Ich > habe aber wirklich angenommen, dass ein Delay weniger Strom frisst => > noch mal was gelernt. Wenn du willst, dass "delay(...)" Strom spart: Das kannst du leicht nachrüsten, ist extra im (AVR-)Arduino-Core vorgesehen. "delay" ruft wiederholt "yield()" auf, während es wartet. "yield()" ist als Weak-Funktion definiert, d.H. extra zum Überschreiben aus deinem Code, und ist normalerweise leer. Da kannst du irgendwelche Hintergrund-Sachen drin erledigen, oder auch einen passenden Stromspar-Sleepmode aktivieren. Du darfst nur nicht so tief schlafen, dass der "millis"-Timer die CPU nicht mehr wecken kann... Vorsicht: Andere Cores können das anders handhaben, könnte mir z.B. vorstellen dass die ESP8266-Arduino-Cores das yield schon anderweitig belegt haben...
:
Bearbeitet durch User
W.S. schrieb: > Manfred schrieb: >> Ich renne in der loop herum, bis millisec() größer Zeit(x) ist,... > > und was machst du, wenn 2 oder mehr Aktionen zu unterschiedlichen Zeiten > getan werden müssen? Wenn also sich die Zeitspannen quasi überlappen? Ich verstehe nicht, was Du mir damit sagen willst. Für mehrere Dinge habe ich eben mehrere Zeiteinträge, deren Ablauf in der loop geprüft wird. Da blinken zwei LEDs, alle paar Sekunden wird eine Spannung geholt und ins Display geschrieben, alle xx Sekunden auf die SD-Karte, die Taster werden abgefragt - immer im Kreis herum. Wenn eine Zeit um ist, geht es in die Unterroutine, die den Befehl ausführt. Während dessen Ausführung gibt es natürlich keine Abfrage der anderen, aber ich sehe keinen Weg, das zu verhindern - der µC ist nunmal ein seriell ablaufendes Gebilde. Damit ergibt sich vielleicht ein Versatz der Zeiten, z.B. alle 15 Sekunden können auch mal 15,1 Sekunden sein. Die absolute Folge läuft aber nicht weg, wenn man es geschickt macht.
W.S. schrieb: > Manfred schrieb: >> Ich renne in der loop herum, bis millisec() größer >> Zeit(x) ist,... > > und was machst du, wenn 2 oder mehr Aktionen zu > unterschiedlichen Zeiten getan werden müssen? Dann gibt es mehrere Timer-Abfragen mit "if". > Wenn also sich die Zeitspannen quasi überlappen? ??? Die ZeitSPANNEN, in denen nix passiert, sind völlig egal. Interessant sind nur die ZeitPUNKTE, zu denen sich irgend etwas ändert. > Nein, das ist alles Geradeausprogrammierung Ich denke, Du hast Manfred missverstanden. Und -- ja, innerhalb der SPS-Hauptschleife ist das ein Geradeaus-Programm: Eine ellenlange Abfolge der Form "Prüfe Bedingung; wenn erfüllt, dann gib dies und jenes aus."
Manfred schrieb: > Ich verstehe nicht, was Du mir damit sagen willst. Ich versuche mal, es dir zu erklären: Wenn du nur einen einzigen Ablauf hast, dann kannst du dir in aller Ruhe die Zeit bis zur nächsten Aktion ausrechnen, also - Aktion - Warten - nächste Aktion usw. Wenn du aber mehrere Abläufe hast, die nicht aneinander gekoppelt sind (was wiederum einen einzigen größeren Ablauf darstellen würde), dann hast du zwar berechenbare Wartezeiten innerhalb jedes dieser Abläufe, aber da die Abläufe nicht miteinander gekoppelt sind, gibt es auch keine vorher berechenbare Zeitspanne zwischen einer Aktion in einem Ablauf und einer anderen Aktion in einem anderen Ablauf. Du müßtest für jeden Ablauf eine separate Zeitsteuerung vorsehen oder diese Zeitsteuerung für alle Abläufe eben komplett als separate Funktion auskoppeln. Im einfachsten Fall ist es das, was ich vorgeschlagen habe, im andern Extrem ist es das RTOS, was den Abläufen (sprich Threads) die Rechenzeit häppchenweise zuteilt. Nun klaro? W.S.
W.S. schrieb: > Ich versuche mal, es dir zu erklären: > Wenn du nur einen einzigen Ablauf hast, dann kannst du > dir in aller Ruhe die Zeit bis zur nächsten Aktion > ausrechnen, Das geht auch, wenn man mehrere Abläufe hat: Bei jeder Aktion eines bestimmten Ablaufes kann man ausrechnen, zu welchem Zeitpunkt die nächste Aktion in diesem Ablauf notwendig wird. > Wenn du aber mehrere Abläufe hast, die nicht aneinander > gekoppelt sind (was wiederum einen einzigen größeren > Ablauf darstellen würde), dann hast du zwar berechenbare > Wartezeiten innerhalb jedes dieser Abläufe, Korrekt. Genau das schrieb ich. Naja, nicht ganz: Interessant ist nicht, wie lange man warten muss , sondern interessant ist, zu welchem Zeitpunkt in der Zukunft irgend eine Aktion notwendig ist. > aber da die Abläufe nicht miteinander gekoppelt sind, gibt > es auch keine vorher berechenbare Zeitspanne zwischen einer > Aktion in einem Ablauf und einer anderen Aktion in einem > anderen Ablauf. Das ist doch auch völlig unnötig: Da man jederzeit auf seine Armbanduhr gucken kann, ist es niemals erforderlich zu sagen: "Wir müssen jetzt 10 Sekunden warten" -- man kann das immer umformen zu: "Jetzt ist es 27 Uhr 83:20 -- die nächste Aktion ist also 27 Uhr 83:30 notwendig." Und genau auf diese Bedingung(en) prüft man. > Du müßtest für jeden Ablauf eine separate Zeitsteuerung > vorsehen oder diese Zeitsteuerung für alle Abläufe eben > komplett als separate Funktion auskoppeln. Nein! Man löst sich einfach von der Vorstellung, dass das WARTEN das Wesentliche wäre, das eine genau bestimmte Zeitdauer lang stattzufinden hätte, und versteht, dass die nächste AKTION das Entscheidende ist, die zu einem bestimmten, in der Zukunft liegenden ZEITPUNKT stattzufinden hat! Und in der Hauptschleife prüft man dann einfach alle diese Bedingungen für die Aktionen ab, und welche Bedingung erfüllt ist, deren Aktion wird ausgeführt. Ganz einfach.
Grummler schrieb: > Und in der Hauptschleife prüft man dann einfach alle diese > Bedingungen Eben. Etwa so: if (Event_available()) { E = Get_event(); Dispatch_event(E); } Womit wir wieder am Ausgangspunkt wären. Für die Vergleiche der Aktions-Zeitpunkte ist ein Vergleich mit der aktuellen Zeit (mit deiner Taschenuhr sozusagen) notwendig, weswegen eine Systemuhr in der Firmware benötigt wird. Folglich kann man all dieses Gucken auf die Taschenuhr und die Vergleiche mit den nächten Aktions-Zeitpunkten auch aus dem Grundprogramm herausnehmen und der Systemuhr als Zusatzaufgabe geben. Eben das, was ich bereits weiter oben geschrieben habe. W.S.
Das artet mal wieder (leider typisch) in akademische Diskussionen aus. Dem TO ist damit sicher nicht geholfen. Der ist offensichtlich absoluter Anfänger in der Materie, und sollte zum Einstieg sich wirklich "Blink without Delays" als Erstes anschauen! Danach den (wirklich guten) Artikel über Statemachines hier im Forum lesen. (und verstehen!) Und DANN kann man über irgendein RTOS reden...
W.S. schrieb: > Eben. > Etwa so: > if (Event_available()) > { E = Get_event(); > Dispatch_event(E); > } Naja... > Für die Vergleiche der Aktions-Zeitpunkte ist ein Vergleich > mit der aktuellen Zeit (mit deiner Taschenuhr sozusagen) > notwendig, weswegen eine Systemuhr in der Firmware benötigt > wird. Beides korrekt. > Folglich kann man all dieses Gucken auf die Taschenuhr > und die Vergleiche mit den nächten Aktions-Zeitpunkten > auch aus dem Grundprogramm herausnehmen und der Systemuhr > als Zusatzaufgabe geben. Man kann -- aber man muss nicht . > Eben das, was ich bereits weiter oben geschrieben habe. Nicht ganz. Du schriebst von "Events" und "Warteschlange". Das verträgt sich nur bedingt mit Echtzeitfähigkeit; deswegen funktionieren klassische SPS auch nicht so.
Harry L. schrieb: > Das artet mal wieder (leider typisch) in akademische > Diskussionen aus. "Themenfremd" und "Selbstdarstellung" fehlt noch. Tu' Dich bitte mit Peter im "Verein rechthaberischer Oberlehrer" zusammen -- aber verschone dieses Forum mit der Benotung fremder Beiträge, um die niemand gebeten hat. Wenn Du Beitrag "Re: Arduino: Handling parallel anfallender Aufgaben" akademisch findest, solltest Du Dich auf die Alternative "Knete oder Buntstifte?" beschränken. > Dem TO ist damit sicher nicht geholfen. Das kann der TO sicher ganz gut allein beurteilen.
Moin, TO hier :) ja, die Diskussion hat mich zwischenzeitlich etwas abgehängt. Das liegt aber vermutlich weniger an der Thematik, da hoffe ich, zuminfest grob mitzukommen, und mehr an der Schlagzahl hier ;) Ich bin sicher ziemlicher Einsteiger. Das Beispiel mit den LEDs kannte ich aber durchaus, hatte das zugrundeliegende Konzept wie erwähnt aber aus den falschen Gründen verworfen. Den genannten Artikel zu State Machine habe ich nur überflogen, behaupte aber, dass mein Vorwissen dazu noch ausreicht :) Also: Ne Nacht lang drüber geschlafen und zu dem vorläufigen Entschluss gekommen, es alleine zu machen. Vielleicht doch kurz grob die Fachlichkeit erklärt, dann wird es hoffentlich verständlicher: Es geht um das Steuern einer (sehr kleinen) analogen Modellbahn mit ein paar geerbten Zügen. Ich habe ein paar Gleise, Weichen und Signale gekauft und ein Regal gebaut, auf dem sie hin- und herfahren können. In einer ersten Ausbaustufe wurde das ganze ausschließlich manuell bedient. Dann wurde automatisiert und der Arduino zog in die Schublade, in der schon die manuellen Schalter versteckt sind. Aktuell kann er einen Zug wählen, ihn auf ein Gleis auf der anderen Seite schicken, dort Rückwärtsfahrt anordnen und den Zug wieder in die Ausgangsposition bringen. Signale und Weichen werden dabei über Magnete gestellt, die ich jeweils mit Relais bediene. Perspektivisch ist eine Idee, regelmäßig meinen Kalender zu prüfen und quasi als Erinnerung kurz vor einem Termin einen Zug zu starten. Wichtig: Ja, heutzutage gibt es digitale Bahnen, die das alles schon können. Aber: - das ist sehr teuer und lohnt für eine so kleine Bahn kaum - Das "Flair" von Oppas Bahn würde ich gern behalten. Digital würde einiges fehlen, was den Betrieb damals gerade ausgemacht hat - Wo bliebe die Herausforderung? Fertig kaufen kann jeder ^^ Also: Die Bahn bleibt analog, davon bringt ihr mich nicht ab;) Als nächste Ausbaustufe nun die Idee, ein Webinterface anzubieten, über das man die Baun steuern kann. Und damit das oben beschriebene Problem, dass ich nicht mehr stumpf mit delay() in loop arbeiten kann. Und jetzt zu meinem Lösungsansatz: Signale und Weichen sind schon heute als Klassen implementiert. Aktuell setzt die Funktion zum "auf Fahrt stellen" eines Signals den zugeordneten PIN auf HIGH, wartet eine Zeit (delay!) und setzt den PIN wieder auf LOW. Diese Klassen werde ich jeweils als State Machine implementieren. loop iteriert über alle Signale und Weichen und stößt jeweils einen Durchlauf der State Machine an. Außerdem wird hier auf HTTP Requests geprüft. Und jetzt kommen wir zum eigentlich spannenden Teil ;) Dort erhaltene Befehle werden in einen (oder mehrere) Tasks übersetzt. Ein Task ist zunächst relativ generisch (der Javajaner in mir denkt an eine abstrakte Klasse) und besitzt im Wesentlichen nur wenige Informationen: - Ist er neu, oder wurde er schon gestartet? - Gibt es andere Tasks, auf die gewartet werden muss, bevor dieser gestartet werden kann? Entsprechend gibt es dann mehrere konkrete Tasks. Ein Taskhandler (mal wieder eine State Machine ;) ) prüt in jeder loop, ob es Tasks gibt, die gestartet werden können, und gibt diese dann an das ausführende Element. ZB kann ein Signal den Task "Auf Fahrt stellen" bekommen, markiert ihn als begonnen und stellt den Pin auf HIGH. Nacjlh einigen Durchläufen ist die Wartezeit durch, der Pin wird zurück auf LOW gesetzt und der Task als erledigt markiert. Der Taskhandler findet in der nächsten Iteration den abgeschlossenen Task und entfernt ihn, wobeo ggfs blockierte Tasks frei werden. Eine komplette Fahrt wird dann zB abgebildet durch: 1. Für jede Weiche der gewünschten Fahrstraße ein Task zum Ausrichten in die gewünschte Richtung 2. Signal für den Zug auf Fahrt stellen. Vorbedingung: Darf erst nach Abschluss aller Tasks aus 1. gestartet werden. 3. Fahrstrom anschalten. darf erst nach 2 starten 4. n Sekunden (die erwartete Zeit fur eine Zugfahrt + x) warten. Darf erst nacu 3. gestartet werden. 5. Fahrstrom aus. Natürlich erst nach Abschluss von 4. 6. ... Vermutlich ist das schon ziemlich over engineered, aber hey , ich hab Spaß an sowas :D Ggfs. könnte man noch je Deadlock Erkennung bauen, eine maximale Zeit vorgeben, bis ein Task erledigt sein muss etc., aber das würde den Rahmen wohl endgültig sprengen :D Ob die Hardware / Plattform und meine doch sehr begrenzten C++ Kenntnisse ausreichend sind, sehen wir dann frühestens nächste Woche. Aktuell bin ich im Urlaub und hab nur mein Smartphone. Das gesamte Konzept ist also bislang nichts weiter als Spinnerei in meiner nem Kopf - und jetzt hier niedergeschrieben, damit ich mich später noch an den Unsinn erinnern kann ;)
Was noch gar nicht erwähnt wurde (oder?) ist dass die Zeitdauer für die Behandlung eines Ereignisses klein genug bleiben muss so dass die Abläufe an anderer Stelle nicht aus dem Takt geraten. Gerade wenn man mit Peripherie kommuniziert kann das aufwendig werden. Ich musste z.B. gerade bei der Aktualisierung der Uhrzeit auf einem Display das Zeichnen der Ziffern in mehrere Jalousien zerteilen die dann unter 1.5ms Dauer bleiben um keine Pakete auf dem CAN-Bus zu verpassen. Leider sind solche Zeitzusagen gerade in Bibliotheken kaum bis gar nicht dokumentiert. LG, Sebastian
Genau, das ist klar. Das Fachwort heißt "kooperatives multithreading", wenn ich hier richtig aufgepasst habe, uns bedingt,dass sich alle an die Spielregeln halten und die Loop nicht blockieren. Dürfte hier aktuell nicht das große Problem sein, wenn der Magnet ein paar Mal länger an bleibt bringt es niemanden um, und HTTP Requests sind iA ja froh, wenn sie überhaupt Mal irgendwann bedient werden ;)
Sebastian schrieb: > Was noch gar nicht erwähnt wurde (oder?) ist dass die > Zeitdauer für die Behandlung eines Ereignisses klein > genug bleiben muss Steuerungsfritzen "behandeln" keine "Ereignisse". Sie prüfen Bedingungen und setzen Zustände und Ausgangs- signale. (Zumindest war das früher so, in der guten alten Zeit.)
Robert M. schrieb: > Das Fachwort heißt "kooperatives multithreading", Nur in der PC-Welt. > wenn ich hier richtig aufgepasst habe, uns bedingt, dass > sich alle an die Spielregeln halten und die Loop nicht > blockieren. Nur in der PC-Welt. Auf der SPS gibt es (soviel ich weiss) keine Ausdrucks- mittel, die die Hauptschleife blockieren könnten. Das geht technisch einfach nicht. > Dürfte hier aktuell nicht das große Problem sein, wenn > der Magnet ein paar Mal länger an bleibt bringt es > niemanden um, und HTTP Requests sind iA ja froh, wenn > sie überhaupt Mal irgendwann bedient werden ;) Du bastelst lieber ein kompliziertes, unvollkommen arbeitendes System selbst, als etwas wirklich Neues zu lernen? Na dann...
Grummler schrieb: > Nur in der PC-Welt. das wurde hier doch nun reichlich demonstriert das es dem µC auch kooperatives und präemptives MT gibt. Sogar kombiniert, EventQueue im Thread. Das kann man sogar mehrfach instanziieren um mehrere Queues mit unterschiedlichen Prioritäten zu bekommen. Aber sicher sind Schrittketten ein Standard in SPS. Für SPS auf µC wurde hier mal OpenPLC genannt, ist da vielleicht auch einen Blick Wert. Habe ich noch nicht benutzt, aber wenn man damit Code für Logik generieren kann, dann hat das auch was.
:
Bearbeitet durch User
Grummler schrieb: > Robert M. schrieb: > >> Das Fachwort heißt "kooperatives multithreading", > > Nur in der PC-Welt. Und erst recht auf einem µC. Grummler schrieb: >> wenn ich hier richtig aufgepasst habe, uns bedingt, dass >> sich alle an die Spielregeln halten und die Loop nicht >> blockieren. > > Nur in der PC-Welt Und erst recht auf einem µC! Grummler schrieb: > Auf der SPS gibt es (soviel ich weiss) keine Ausdrucks- > mittel, die die Hauptschleife blockieren könnten. Das > geht technisch einfach nicht. Und ob das geht! Halte dich lieber etwas im Hintergrund, wenn du dich nicht vollständig blamieren willst!
:
Bearbeitet durch User
J. S. schrieb: > Grummler schrieb: >> Nur in der PC-Welt. > > das wurde hier doch nun reichlich demonstriert das > es dem µC auch kooperatives und präemptives MT gibt. Der µC von heute war der PC von vorgestern. Der Begriff "Task" kommt von den Multi-User-Multi-Tasking- Maschinen her; auf der SPS kommt man klassisch völlig ohne diesen Begriff aus. Man braucht ihn nicht -- und zwar selbst dann nicht, wenn die SPS zahlreiche völlig unabhängige Abläufe steuern soll. Das, was der TO versucht, hat er selbst m.E. schon völlig korrekt benannt: Overengineering. > Sogar kombiniert, EventQueue im Thread. Das kann man > sogar mehrfach instanziieren um mehrere Queues mit > unterschiedlichen Prioritäten zu bekommen. Völlig anderes Thema. Natürlich ist es u.U. nachteilig, wenn man GEZWUNGEN ist, JEDE Aktion über Schrittketten auszudrücken. FUP und AWL sind sicher nicht das Nonplusultra der Ausdrucksmittel, das ist unstrittig. Darüber sollte man aber m.E. erst diskutieren, wenn man das Konzept der Schrittketten BEHERRSCHT... > Aber sicher sind Schrittketten ein Standard in SPS. Ich würde noch einen Schritt weiter gehen: Die Schrittkette ist für den Programmablauf das, was für die Daten das relationale Datenmodell ist. Wer Schrittketten verstanden hat, sollte sich auch in programmierbarer Logik schnell zu Hause fühlen.
Harry L. schrieb: > Grummler schrieb: >> Robert M. schrieb: >> >>> Das Fachwort heißt "kooperatives multithreading", >> >> Nur in der PC-Welt. > > Und erst recht auf einem µC. Darum ging es nicht: Der Begriff "Multitasking" ist im Kontext klassischer Ablaufsteuerungen schlicht überflüssig -- das ist eine Erfindung der "richtigen" Informatiker. Die sind nämlich stolz wie Bolle darauf, dass ihre Blechtrottel inzwischen auch das können, was jede beliebige Gatterschaltung schon immer konnte: Mehrere unabhängige Dinge zur gleichen Zeit tun. Oder würdest Du bei einer Relaissteuerung von "massiv paralleler Hardware" sprechen? -- Und doch ist es so, denn sie wertet alle booleschen Gleichungen parallel in Hardware aus... > Grummler schrieb: >>> wenn ich hier richtig aufgepasst habe, uns bedingt, dass >>> sich alle an die Spielregeln halten und die Loop nicht >>> blockieren. >> >> Nur in der PC-Welt > > Und erst recht auf einem µC! Darum ging es nicht: Im Kontext der Steuerungstechnik... ach halt, warte. Ich wiederhole mich. > Grummler schrieb: >> Auf der SPS gibt es (soviel ich weiss) keine Ausdrucks- >> mittel, die die Hauptschleife blockieren könnten. Das >> geht technisch einfach nicht. > > Und ob das geht! Mag sein. Bei dem AWL/FUP-Kram, den ich gemacht habe, kam man damit nicht in Berührung. Soll heißen: Normalerweise blockiert nix. > Halte dich lieber etwas im Hintergrund, wenn du dich > nicht vollständig blamieren willst! Sag' mal... tickst Du noch ganz sauber? Könntest Du diese fortgesetzten persönlichen Anranzereien mal bitte unterlassen? Soweit ich weiss, ist dies hier ein DISKUSSIONSFORUM , und das, was ich hier tue, nennt man DISKUTIEREN . Das muss Dir nicht gefallen, das solltest Du aber klaglos hinnehmen. Ich muss Dich nämlich auch ertragen, und so bemühe mich auch, Dich nicht grundlos (!) anzukacken.
ich weiß nicht wie alt deine SPS sind, aber in aktuellen laufen Tasks. Und in den Tasks sind Programminstanzen. Es gibt langsame oder schnelle oder getriggerte Tasks. Grummler schrieb: > Der µC von heute war der PC von vorgestern. und deshalb kann man da ohne zu jammern auch mehrere Threads laufen lassen. Kooperativ oder Präemptiv. SPS können heute mehr als nur Relais klappern lassen, die machen mittlerweile auch Bildverarbeitung.
:
Bearbeitet durch User
J. S. schrieb: > ich weiß nicht wie alt deine SPS sind, Siemens S5/S7. Codesys war noch kein echtes Thema; FUP/AWL war (aufgrund der vorhandenen Codebasis) das Mittel der Wahl. > aber in aktuellen laufen Tasks. Und in den Tasks sind > Programminstanzen. Es gibt langsame oder schnelle oder > getriggerte Tasks. Mag sein. Das bedeutet aber noch lange nicht, dass "Task" auf der SPS dasselbe bedeutet wie "Task" unter Unix... (Schließlich bedeutet "Liste" für Tcl auch nicht dasselbe wie "Liste" für C++...) > Grummler schrieb: >> Der µC von heute war der PC von vorgestern. > > und deshalb kann man da ohne zu jammern auch mehrere > Threads laufen lassen. Kooperativ oder Präemptiv. Sicher KANN man. Habe ich nie bestritten. Es gibt aber KEINE Äquivalenz der Form "Maschine soll mehrere unabhängige Dinge tun" == "es MÜSSEN mehrere Tasks gestartet werden". > SPS können heute mehr als nur Relais klappern lassen, > die machen mittlerweile auch Bildverarbeitung. ??? Bildverarbeitung kann ich auch auf einem FPGA machen -- genauso, wie der auch eine Maschine steuern könnte. Ich verstehe das Argument nicht.
Grummler schrieb: >> Das Fachwort heißt "kooperatives multithreading", > > Nur in der PC-Welt. Grummler schrieb: > Sicher KANN man. Habe ich nie bestritten. dann gibt es hier min. 2 Grummler. Spielt euer Nullsummenspiel alleine weiter.
W.S. schrieb: > Dieses Proto-Zeugs von Adam Dunkel ist eine mittelgroße > Katastrophe, Verlangt ja keiner, dass dir das schmeckt! Mache einfache einen Bogen drum. W.S. schrieb: > denn es ist im Grunde nur eine Präprozessor-Akrobatik und Ja, Präprozessor Arbeit. Und? W.S. schrieb: > dahinter steckt eine switch-case-Anweisung. Alternativ: Computed Goto W.S. schrieb: > Eigentlich nur eine > Verkleidung, so daß man das C-Konstrukt dahinter nicht mehr erkennen > kann. Hier liegt dein Hauptirrtum! In einem ordentlichen Multitasking System schreibt man eine Schleife, wenn man eine Schleife braucht. Selbst wenn sie 14 Tage läuft, marschieren die anderen Tasks ungestört weiter. Bei deinem Vorschlag muss man die Schleifen zerreißen, so dass die nicht mehr als solche klar zu erkennen sind. Da muss man im Kommentar schreiben: Dieses ist eine ausgerollte Schleife. Das "zerreißen" erledigen bei den ProtoThreads die Makros. Man schreibt eine Schleife, oder eine Wartezeit(taskPause), wann man sie braucht. Das macht die Anwendungslogik klarer. W.S. schrieb: > Dafür würde ich den Adam teeren und federn. Wurdest du schon so oft geteert und gefedert, dass du das auf andere projizierst? Auf den kleinen µC herrscht eher Speichermangel vor. Die ProtoThreads benötigen keinen (permanenten) Stackframe für jede ihrer Tasks. Der Scheduler kann schlicht gehalten werden. Und TaskControll Strukturen braucht es auch nicht unbedingt. Es ist eine schlanke Variante, welche einige Vorzüge ausgewachsener Multitasking Systeme mitbringt, aber längst nicht alle. Sie muss auch nicht jedem schmecken....
Grummler schrieb: > Nicht ganz. > Du schriebst von "Events" und "Warteschlange". Das > verträgt sich nur bedingt mit Echtzeitfähigkeit; Nun ja, eine solche Lösung ist darauf angewiesen, daß so eine Botschaft (aka Event) nicht nur in eine Warteschlange gestopft wird, sondern auch gelesen und darauf reagiert wird. Aber dafür ist das ganze Konstrukt einfach und billig zu haben und bedarf nur wenigen Lernens. Wenn man da sogleich nach einem RTOS ruft, dann wäre dort die Einarbeitung erheblich umfänglicher - und für viele eher überschaubare Anwendungen wäre sowas auch übertrieben. Mit den genannten Warteschlangen scheint hier aber fast jeder Zweite ein mentales Problem zu haben. Nun, sowas trifft man weitaus häufiger an, als es einem auffällt - jedenfalls dann, wenn eine Firmware nicht trampelig geradeaus programmiert wurde. Ich denke da z.B. an sowas wie COM-Port-Treiber, die Fifos für die Sende- und Empfangs-Daten haben. Eben um das physische Sende- und Empfangs-Gehampel von den höheren Programmschichten zu entkoppeln. Genau so ist es auch mit einem Fifo (aka Warteschlange) bei den Benachrichtigungen (aka Events), die zwecks Entkopplung zwischen der generierenden Stelle in der Firmware und der reagierenden Stelle benötigt werden. Ich entsinne mich dunkel, hier schon mal ein passendes Stück C-Quelle zu diesem Thema gepostet zu haben. Das Ganze ist wirklich nicht umfänglich und leicht zu verstehen und anzuwenden. Harry L. schrieb: > Das artet mal wieder (leider typisch) in akademische Diskussionen aus. > Dem TO ist damit sicher nicht geholfen. > ... > Und DANN kann man über irgendein RTOS reden... Nö, über irgendein RTOS will jedenfalls ICH hier nicht reden, denn sowas ist zum einen aufwendig und zum anderen hier herzlich übertrieben. Und dem TO ist (wenn er nicht nur lesen sondern auch verstehen kann) sehr wohl geholfen. Und akademische Diskussionen sind was ganz anderes. W.S.
W.S. schrieb: > Dieses Proto-Zeugs von Adam Dunkel ist eine mittelgroße > Katastrophe, denn es ist im Grunde nur eine Präprozessor-Akrobatik und > dahinter steckt eine switch-case-Anweisung. Es ist schon sehr elegant und zeigt schön, wie man mehrere Statemaschines quasi parallel ausführen kann. Niemand zwingt dich, es zu verwenden. Man kann natürlich die switch/case Statemaschines direkt hinschreiben. Wichtig ist nur, daß nirgends auf etwas gewartet, sondern die CPU-Zeit sofort zur Mainloop zurück gegeben wird. Gewartet wird immer nur indirekt, indem Flags oder Countdown-Zähler abgeprüft werden. Man kann auch parallel einen Scheduler laufen lassen, dem man einfach Callbacks und die gewünschte Wartezeit übergibt. Der Vorteil gegenüber einem RTOS ist, daß die Reihenfolge immer deterministisch ist und somit keine Task eine andere mittendrin unterbrechen kann und Datensalat oder Deadlocks fabriziert.
Robert M. schrieb: > Vermutlich ist das schon ziemlich over engineered Das mag bei einer kleinen Anwendung zutreffen, aber es ist ein problemlos ausbaufähiges Konzept mit dem Anwendungen wachsen können ohne dass man sich verzettelt.
Robert M. schrieb: > Genau, das ist klar. Das Fachwort heißt "kooperatives multithreading", > wenn ich hier richtig aufgepasst habe, uns bedingt,dass sich alle an die > Spielregeln halten und die Loop nicht blockieren. Kennen die alten Hasen von Windows 3.11. Beim Drucken stotterte die Musikwiedergabe (trotz DMA) und wenn man doof programmiert, bleibt der ganze Rechner hängen. Falls sich jemand für die genannten Protothreads interessiert, ich habe sie in mehreren Anwendungen eingesetzt und einen deutschen Aufsatz dazu geschrieben: http://stefanfrings.de/net_io/protosockets.html Ich habe mich bemüht, nicht allzu sehr durchblicken zu lassen, dass ich die Protohtreads nicht mag. Als Kontrast dazu habe ich dort beschrieben, wie ich Zustandsautomaten ohne Protothreads implementiere: http://stefanfrings.de/multithreading_arduino/index.html In dieser Diskussion wurde angesprochen, Ereignisse in Warteschlangen zu packen und dann abzuarbeiten. In vielen Anwendungen kann das sicher auch sehr hilfreich sein, durchschaubaren Code zu schreiben. Man kann das auch gut mit Zustandsautomaten kombinieren. Ist auf jeden Fall ein Thema, mit dem man sich früher oder später auseinander setzen sollte. Nicht nur bei Mikrocontrollern, sondern bei Softwareentwicklung allgemein.
Manfred schrieb: > Delay ist doof, weil es den Programmablauf blockiert. Nein, Delay() ist dazu da, den Programmablauf zu blockieren, auch wenn User wie "amDelayErkenntManDenAnfänger (Gast)" das noch nicht erkannt haben: Das braucht aus mindestens 2 Gründen: 1. Um schnell was zusammenzuschustern und Zeit zu sparen 2. Um ein größeres Programm kurzzeitig für eine Debugausgabe (die Zeit braucht) anzuhalten und keine Variablenänderung zu verpassen. Just my 2 cents (Meinung!)
Ein Großteil der Menschen bevorzugt wohl einen linearen Programmablauf, weil es so schön einfach ist. Zustandsautomaten sind nicht jedermanns Sache, daher wurde kooperatives Multitasking erfunden. Und um den dabei normalerweise notwendigen erhöhten RAM-Bedarf zu vermeiden, wurden die Protothreads erfunden. Alles nur "syntactic sugar", um die Komplexität in einen unteren Layer zu verbannen und sich das Leben leichter zu machen. Bezahlen muß man dafür mit dem Verlust der Schrittvariablen, die einem ständig signalisieren, wo das Programm gerade auf etwas wartet. Auch präemptives (laut EAF: ordentliches) Multitasking wird ja außerhalb von Realtimeanwendungen nur dafür gebraucht, daß man bei langen Schleifen nicht an Andere denken muß und das "Auseinanderreißen" einsparen kann, also einfach Bequemlichkeit. Just my 2 cents (Meinung!)
Klaus S. schrieb: > Zustandsautomaten sind nicht jedermanns > Sache, daher wurde kooperatives Multitasking erfunden. Dass ist so dermaßen falsch, dass sicherlich noch nicht einmal das Gegenteil richtig ist. Zustandsautomaten haben nichts mit Multitasking zu tun, ist somit auch kein Ersatz. Multitasking hat nichts mit Zustandsautomaten zu tun, ist somit auch kein Ersatz. Was allerdings richtig ist, dass die beiden gerne mal gemeinsam auftreten. Multitasking/Multithreading sagt einfach nur, dass verschiede, logisch getrennte Handlungsabläufe/-stränge vorliegen. Welche quasi gleichzeitig abgearbeitet werden sollen. Zustandsautomaten, gerade auch Schrittketten, beruhen darauf, dass die Dinge fein nacheinander, geordnet, abgearbeitet werden. Das sind zwei völlig verschiedene Baustellen. Das eine möchte ein (quasi) gleichzeitig erreichen, das andere ein nacheinander Eine einzelne Schrittkette braucht kein Multitasking, oder blockadefreie Programmierung. Das wird erst wichtig, wenn sich die Automaten rudeln.
Ich benutze sowas, damit lassens sich mit #define delay(n) TH_DELAY(n) relativ einfach "arduino code" auf State-machine bringen und von der Blockierung wegkommen. Habe nur das wichtigste kopiert.
1 | #define THREADS 10 |
2 | uint8_t th_seq[THREADS]; // seq values |
3 | uint64_t th_now[THREADS]; // delay values |
4 | uint8_t thseq; // current thread state, same as seq[thid] |
5 | uint8_t thid; // current thread |
6 | uint8_t thseqtemp; // temp counter |
7 | #define thnow th_now |
8 | |
9 | #define CODE_ELAPSED(current,old,time) (((current)-(old)) > ((time)*1000)) #define TH__BRK if(thseq==thseqtemp++) #define TH_YIELD() } TH__BRK { #define TH_DELAY(n) thnow[thid]=micros64(); } TH__BRK if(!CODE_ELAPSED(millis64() , thnow[thid] , n)) ; else { |
10 | #define TH_START(A) thseq=th_seq[thid=A]; thseqtemp=0; { TH_YIELD() |
11 | #define TH_END() } if(++thseq>thseqtemp) thseq=0; seq[thid]=thseq; |
12 | |
13 | uint64_t micros64() { static uint32_t low, high; uint32_t val = micros(); if (val < low) high++; low = val; return (uint64_t) high << 32 | low; } |
EAF schrieb: > Dass ist so dermaßen falsch, dass sicherlich noch nicht einmal das > Gegenteil richtig ist. > Zustandsautomaten haben nichts mit Multitasking zu tun, ist somit auch > kein Ersatz. Wenn man die Nase dicht am Problem hat, sieht man die Unterschiede und nicht die Gemeinsamkeiten. Dann sind alle Bäume verschieden. Tritt man einige Schritte zurück, sieht man die Gemeinsamkeiten, den Wald. Deshalb vorweg: Es gibt grob 5 Möglichkeiten für Multitasking. 1. Hardware. Kombinatorische Logik, 1 CPU je Task, Quantenrechner 2. Kette von Zustandsautomaten. Das machen SPSen und kleine uC-Systeme (wie Arduino) und simulieren so das parallele Abarbeiten durch quasiparalleles. 3. Kooperatives Scheduling. Das zerlegt die Programme im Scheduler in die Pakete, die einen Zustandsautomaten bilden, jedes Programm einen und meistens a la RoundRobin einen nach dem Anderen. Das machen etliche der RealTime-Operatingsysteme für Mikrocontroller, kann man in der Wikipedia-Übersicht nachschauen. 4. Interruptgesteuerte Priorisierungshierarchien. Viele uCs (AVR8 nicht!) haben im Interruptcontroller ein (auch einstellbares) Priorisierungsschema. Damit kann man sich selbst relativ einfach ein Multitaskingsystem bauen, allerdings eben nur prioritätsgesteuert. 5. Präemptive Betriebssysteme. Da starten je nach Auslegung ein oder mehrere Interrupts jeweils den Scheduler nach unterschiedlichen Algorithmen (z.B.RR,PRIO,MRS,EDF...) Und da der TO nach "Handling parallel anfallender Aufgaben" gefragt hatte, sprechen wir hier genau darüber. Und nicht über einen einzelnen Zustandsautomaten, den Du sehr richtig beschrieben hast. Sollte das meinem Text nicht zu entnehmen gewesen sein, habe ich mich wohl zu undeutlich ausgedrückt. Ich bitte dafür um Nachsicht. Mir ging es um die organisatorische Äquivalenz von Lösung 2 und Lösung 3. Einmal muß der Programmierer die Arbeit machen, im anderen Fall hat jemand Anderes vorgearbeitet, kooperatives Scheduling eingerichtet und der Programmierer kann ganz bequem linear programmieren. Ist das jetzt besser verständlich? Gruß Klaus (der soundsovielte)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.