Forum: Mikrocontroller und Digitale Elektronik Arduino Uno - LED-Projekt


von Anton F. (Gast)


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!

von Marco G. (grmg2010)


Lesenswert?

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.

von Anton F. (Gast)


Lesenswert?

Ok. Und wie kann ich das parallelisieren?

Gruß

von Matthias S. (Firma: matzetronics) (mschoeldgen)


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.

von spess53 (Gast)


Lesenswert?

Hi

>Und wie kann ich das parallelisieren?

Timer statt Delay benutzen.

MfG Spess

von Matthias S. (Firma: matzetronics) (mschoeldgen)


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.

: Bearbeitet durch User
von Joachim B. (jar)


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.

: Bearbeitet durch User
von Ulrich F. (Gast)


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.

von chris_ (Gast)


Lesenswert?

Hier eine sehr schöne Anleitung für Arduino-Multitasking:

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview

von Jürgen S. (jurs)


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.

von Joachim B. (jar)


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

von Jürgen S. (jurs)


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.

von Max90 (Gast)


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

von Ulrich F. (Gast)


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!

von Joachim B. (jar)


Lesenswert?

Ulrich F. schrieb:
> Merke:
> Startzeit merken : Ja
> millis() - startzeit >= interval : ja
> endzeit = millis() + interval : Nein, verboten

danke, ich lerne auch noch!

von Stefan K. (stefan64)


Lesenswert?

chris_ schrieb:
> Hier eine sehr schöne Anleitung für Arduino-Multitasking:
>
> https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview

Kann ich nur empfehlen, für Anfänger eine der besten Anleitungen zum 
(Fast-) Multitasking!

Fortgeschrittene können sich später mal FreeRTOS anschauen.

Gruß, Stefan

von chris_ (Gast)


Lesenswert?

>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 )

von Ulrich F. (Gast)


Lesenswert?

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.

von Franz (Gast)


Lesenswert?

>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.

von Ulrich F. (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.