Arduino Uno - LED-Projekt

Gast #4379009
Lesenswert?

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
void loop()
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!
(Firma: matzetronics) #4379025
Lesenswert?

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.
(Firma: matzetronics) #4379037
Lesenswert?

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.
#4379040
Lesenswert?

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.
Gast #4379352
Lesenswert?

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.
#4379877
Lesenswert?

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.
#4379922
Lesenswert?

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
#4379966
Lesenswert?

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.
Gast #4379984
Lesenswert?

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
Gast #4380133
Lesenswert?

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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren