Hallo zusammen.
Ich habe eine Frage zu einem kleinen Arduino Uno-Projekt, welcher auf
meiner Modelleisenbahn mehrere Ampeln auf Kreuzungen schalten soll.
Jede T-Kreuzung hat 3 Ampeln. Insgesamt habe ich 3 T-Kreuzungen.
Mein Programm sieht folgendermaßen aus:
1
voidloop()
2
{
3
T_Kreuzung(1);
4
T_Kreuzung(2);
5
T_Kreuzung(3);
6
}
Die Funktion "T_Kreuzung()" beinhaltet die Schaltzustände der jeweiligen
LED der Ampeln und die Rot-/Grünphasen mittels delay().
Nun meine Frage:
Werden denn die T_Kreuzungsaufrufe alle parallel abgearbeitet oder
bedarf es hier "Multitasking", damit die Kreuzungen alle autark vom
Arduino gesteuert werden?
Denn es wird ja erst von Aufruf "T_Kreuzung(1)" auf "T_Kreuzung(2)"
gesprungen, wenn "T_Kreuzung(1)" einmal durchlaufen ist. Dann kommt
T_Kreuzung(2) und T_Kreuzung(3), wobei dann aber immer nur eine Funktion
ausgeführt wird und nicht die anderen weiterlaufen.
Wisst ihr was ich meine?
Vielen Dank für eure Hilfe!
Es wird jede Funktion einzeln ausgeführt, es läuft nichts parallel. Es
wird erst T_Kreuzung(1), dann T_Kreuzung(2) und zuletzt T_Kreuzung(3)
abgearbeitet. Sind alle abgearbeitet, wird wieder mit 1 begonnen.
Anton F. schrieb:> Die Funktion "T_Kreuzung()" beinhaltet die Schaltzustände der jeweiligen> LED der Ampeln und die Rot-/Grünphasen mittels delay().
Dann wird eben auch solange in der Funktion gewartet, wie dein Delay
lang ist.
Keiner der kleinen MC kann so etwas wie Mutltitasking, da sie alle nur
einen 'Kern' haben.
Besser ist es also z.B., eine Uhr (Ticker) laufen zu haben, die immer
mal wieder angesprungen wird, um zu checken, ob es Zeit für die nächste
Ampelphase ist. Schau mal nach dem Begriff 'Statemachine', die ist für
sowas gedacht.
Dann kann im Hauptprogramm z.B. noch eine Bedarfsampel (mit Tastern)
o.ä. abgefragt werden, ohne den Fluss des Programmes zu stoppen.
Ein möglicher Ansatz wäre die Definition von allen möglichen Zuständen
der Ampelanlage mit den Zeiten für die jeweilige Dauer. Also ein
kompletter Durchlauf.
Dein Timer wird vorgeladen mit der Zeitdauer für eine Ampelphase und
setzt die Ausgänge so, das die Ampeln alle das richtige anzeigen.
Wenn der Timer abgelaufen ist, wird er wieder mit der Dauer für die neue
Phase geladen und setzt wieder die Lampen... usw.
Nach der letzten Phase springts wieder zur ersten.
Das hat den Effekt, das du verschiedene Dauern für die Phasen angeben
und auch recht einfach ändern kannst.
Stichwort dafür neben der o.a. Statemachine ist 'Timer Interrupt',
'Array' und evtl. 'struct'.
In so ein Array speicherst du z.B. die Dauer und den Portzustand für
jede Ampelphase.
vergiss delay, arbeite mit Zustände und millis()
//init ampel_1 gruen
gruenphasestart[1]=millis();
loop
gruenphaseende[1]=millis()+gruendauer;
im loop fragst du nun ab
if(gruenphaseende[1]>gruenphasestart[1]+gruendauer)
schalte gelb[1];
if(gelbphaseende[1]>gelbphasestart[1]+gelbdauer)
schalte rot[1];
so in etwa.....
noch kein fertiges Programm, aber der Weg sollte klar sein.
oder per countdown
//Ampel_1 gruen init
gruenphaseende[1]=millis()+gruendauer;
im loop
if(!(gruenphaseende[1]-millis())) // millis(steigt) die Differenz geht
auf NULL, wenn NULL kommt gelb
schalte gelb(1);
kannst auch mit Flags arbeiten, gruenende abgelaufen gelb_flag setzten
Timer wird doch hier nicht zwingend gebraucht, der Arduino liefert
millis() und micros() mit.
Joachim B. schrieb:> gruenphaseende[1]=millis()+gruendauer;
Das macht alle 49 Tage einen Überlauf.
Welcher sich bei den nachfolgenden Vergleichen fatal auswirken kann.
Wenn es hier auch nur ein "Spielzeug" ist, ist das Vorgehen dennoch
grundsätzlich falsch.
Joachim B. schrieb:> if(!(gruenphaseende[1]-millis()))
Der Vergleich MUSS in genau in der passenden milli Sekunde gemacht
werden. Wird mal die wichtige ms übersprungen, klappts nicht. Und es
wartet 49 Tage auf die nächste Gelegenheit.
Anton F. schrieb:> Ok. Und wie kann ich das parallelisieren?
Im Programm
- kein "delay()" verwenden
- kein "busy waiting" verwenden
- nur "kooperatives Multitasking" mit nicht-blockierenden Funktionen
Ich würde eine Ampelkreuzung als "Finite-State-Machine" (Endlicher
Zustandsautomat) realisieren.
Und Deine gesamte Anwendung besteht dann aus drei konkurrierenden
State-Machines, die parallel ablaufen.
Ulrich F. schrieb:> Joachim B. schrieb:>> gruenphaseende[1]=millis()+gruendauer;> Das macht alle 49 Tage einen Überlauf.> Welcher sich bei den nachfolgenden Vergleichen fatal auswirken kann.
das ist falsch, die Differenz ist immer passsend hatte Karl Heinz schon
hier bewiesen (wusste ich auch nicht)
Beitrag "Re: rollover save timer in c"Ulrich F. schrieb:> Joachim B. schrieb:>> if(!(gruenphaseende[1]-millis()))> Der Vergleich MUSS in genau in der passenden milli Sekunde gemacht> werden. Wird mal die wichtige ms übersprungen, klappts nicht. Und es> wartet 49 Tage auf die nächste Gelegenheit.
war ja noch nicht fertiger Code, dein Einwand ist richtig und der TO
kann noch mal selber denken,
gefällt
if(millis()>=gruenphaseende[1])
besser? mir auch
Joachim B. schrieb:> gefällt>> if(millis()>=gruenphaseende[1])>> besser? mir auch
Nein, nur DIFFERENZEN sind problemlos über den Timer-Überlauf möglich:
Beim Starten der Grünphase:
unsigned long gruenphasebeginn=millis();
und als Prüfung auf Ende der Grünphase:
if(millis()-gruenphasebeginn>= gruenphasedauer) ...
So ein Vergleich geht immer gut, auch wenn millis() zwischen Beginn und
Ende der Grünphase überläuft.
Aber wie gesagt, ich würde das mit den drei Ampelkreuzungen sowieso über
drei gleichzeitig laufende State Machines lösen. Geeignete
Arduino-Library:
http://playground.arduino.cc/Code/SMlib
Da gibt es für jeden Zustand eine bereits vorprogrammierte "timeout(x)"
(mit 'x'= Timeout in Millisekunden) Funktion, und wenn diese Funktion
'true' zurückliefert, schaltet man eben in den nächsten Zustand.
Hallo Anton!
Sieh dir einfach unter den Beispielen den "Blink without delay" Sketch
an. Ich denke das wird dir weiter helfen.
Dann würde ich dir noch empfehlen das du dir die "switch case" Funktion
ansieht. Damit kannst du solche Abläufe super steuern.
MFG
Max
Joachim B. schrieb:> das ist falsch, die Differenz ist immer passsend hatte Karl Heinz schon> hier bewiesen
Wenn du men eine Differenz berechnen würdest...
Aber bei (d)einer Addition kommt immer eine Summe raus.
Karl Heinz hat recht:
Eine Berechnung millis()+interval ist falsch.
millis()-startzeit ist richtig
Joachim B. schrieb:> gefällt> if(millis()>=gruenphaseende[1])> besser? mir auch
Besser!
Aber, das Problem ist die Berechnung der Endzeit.
Und damit schlägt der Vergleich fehl, bzw. tut nicht immer das, was du
erwartest.
Merke:
Startzeit merken : Ja
millis() - startzeit >= interval : ja
endzeit = millis() + interval : Nein, verboten
millis() macht sowieso einen Wraparound, das ist nicht zu verhindern.
Aber deine Addition verursacht auch (manchmal) einen Überlauf. Der will
entweder vermieden, oder abgehandelt werden.
Jürgen S. schrieb:> Aber wie gesagt, ich würde das mit den drei Ampelkreuzungen sowieso über> drei gleichzeitig laufende State Machines lösen.
Ja!
>das ist falsch, die Differenz ist immer passsend hatte Karl Heinz schon>hier bewiesen (wusste ich auch nicht)>Beitrag "Re: rollover save timer in c"
Wobei die Stelle, die gepostet hast, einen kleine "Unsauberkeit"
enthält. Sie funktioniert nur auf den 16bit Prozessoren. Korrekter wäre
dieser Code:
Beitrag "Re: rollover save timer in c"
( Der Type Cast ist wichtig )
chris_ schrieb:> einen kleine "Unsauberkeit"
UINT8 ?
In der Arduinowelt verwendet man da unsigned long für alle beteiligten
Variablen/Konstanten.
Kostet Speicher, tuts dann aber auch unter Wasser.
Joachim B. schrieb:> ich lerne auch noch!
Willkommen im Club.
Unser aller Schicksal.
>UINT8 ?>In der Arduinowelt verwendet man da unsigned long für alle beteiligten>Variablen/Konstanten.
"Man" tut in der Arduinowelt gar nichts.
UINT8 ist Microsoft.
uint8_t
uint16_t
uint32_t
Ist aus "stdint.h" C99 Standard:
https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h
und ist in jedem Arduino Sketch automatisch eingebunden.
Franz schrieb:> uint8_t> uint16_t> uint32_t
In einem hast du recht:
Ist definiert.
Wird aber eher selten verwendend.
Hier der Prototype über den wir reden:
> unsigned long millis(void);
Aus: Arduino.h
Danke fürs zuhören.