Aufruf von Funktionen die mehrmals durchlaufen werden müssen

Hallo,

wie ruft ihr für gewöhnlich Funktionen auf, die im Anschluss mehrmals durchlaufen werden müssen?

Ein Beispiel:

Ich habe eine Funktion geschrieben, die eine LED zweimal blinken lässt.

Die Funktion enthält delay und muss somit nur einmalig aufgerufen werden um zu funktionieren.

Da ich mich dazu entschlossen habe weitere Dinge innerhalb meines Codes ausführen zu wollen, habe ich mich dazu nun dazu entschieden, die Stellen mit delay zu entfernen.

Nachteil: Die neu geschriebene Funktion mit der LED muss nun mehrmals durchlaufen werden um ausgeführt werden zu können.

Wie macht man das mit so wenig Aufwand wie möglich? Ich löse das jedesmal irgendwie anders, habe aber gefühlt für mich noch nicht die optimale Variante gefunden.

Gruß Chris

Statusvariable und bedingt im loop() die Funktion aufrufen.

loop()
{
if (callfunktion ==1) meinefunktion();
...
}

Grüße Uwe

callfunktion dann am Ende von meinefunktion() wieder auf 0 setzen, so meinst Du das, oder?

Gruß Chris

Deiner Signatur nach, hast du blink withoutdelay schon mal gesehen :wink:

Das Problem ist, dass die Umstellung auf "ohne delay" in Wirklichkeit ein komplett anderer Ansatz ist.

loop() braucht keine Zeit mehr und läuft dafür unendlich oft. ( naja, fast )

Dann ist es auch egal, ob eine Funktion einmal oder mehrmals pro loop aufgerufen wird.

Das delay wird weniger durch millis() als durch Zustandsvariable ersetzt, in denen steht, wo das Proramm gerade ist. Nur wenn die Zustandsänderung von Zeit abhängt, braucht man dafür natürlich eine Konstruktion wie

if (millis() - StartZeit > Dauer ) { /* neuen Zustand einstellen */ }

Die Geschichte mit den Zuständen und ihren Änderungen wird gern "endlicher Automat" (Finite State Machine) genannt, und darüber ist dieses Forum so voll, dass ich hier nix sage.

In Uwe's Vorschlag ist callfunction vermutlich so eine Zustandsvariable

Hallo Chris,
ich löse das z.B. so:

const int ledPin = 13
void setup() {
              pinMode(ledPin, OUTPUT);      
              }
void loop() {
             blink_anz(3); //Aufruf mit 3 mal blinken
             }

void blink_anz(byte anz)
{
  static unsigned long previousMillis_led;        
  static unsigned long millisalt_led;
  static boolean ledState;
  static byte count;
  const int interval = 300;           
  if(millis()-millisalt_led>1000)
  {
   if(millis() - previousMillis_led > interval)
     {
       previousMillis_led = millis();   
       if (!ledState) ledState = HIGH;
       else{
            ledState = LOW; 
            count++;
            if(count>=anz){count=0;millisalt_led=millis();}
            }
       digitalWrite(ledPin, ledState);
     }
   }
}

Die Funktion blink_anz wird in der lopp immer wieder aufgerufen

so wie ich den Code auf die Schnelle überfliege, frage ich mich, warum der nur 3 mal blinkt. Count wird doch danach auf 0 gesetzt und die Routine sofort wieder aufgerufen.

Da fehlt mMn eine Aufrufbedingung und ggf. eine Rückmeldung, ob fertig geblinkt oder noch in Ausführung

Chris72622:
Nachteil: Die neu geschriebene Funktion mit der LED muss nun mehrmals durchlaufen werden um ausgeführt werden zu können.

Das ist kein Nachteil, das ist normal.

Dein Design sollte so sein, dass alle Funktionen in der loop() bei jedem Durchlauf aufgerufen werden, also viele tausend mal pro sekunde.

jede Aufgerufene Funktion entscheidet dann, ob was zu tun ist. wenn nicht, wird sie sofort wieder verlassen.
z.B.:

void loop() {
  myFunc1();
  myFunc2();
}
void myFunc1(){
  static unsigned long letztesEvent = 0;
  if (millis() - letztesEvent < 1000) return;
  letztesEvent = millis();
  // Code ausführen 
}
void myFunc2(){
  static unsigned long letztesEvent = 0;
  if (millis() - letztesEvent > 1000){
    letztesEvent = millis();
    // Code ausführen 
  }
}

myFunc1 und myFunc2 machen genau das gleiche.
ich mag die Variante myFunc1 lieber, weil hier verschiedene Abbruchkriterien einfach aufgelistet werden können.

void myFunc1(){
  static unsigned long letztesEvent = 0;
  if (!buttonpressed) return;
  if (Motordrehzahl > 0) return;
  if (millis() - letztesEvent < 1000) return;
  letztesEvent = millis();
  // Code ausführen 
}

@ ElEspanol
der blinkt immer wieder 3 mal und macht dann 1sec. Pause

Aber die Blinkfrequenz ist ja auch eine Sekunde.

Der Ansatz ist ja ok, aber als Beispiel um es Fragestellern zu veranschaulichen, ist es mMn so noch nicht ganz geeignet. Wenigstens eine Tasterabfrage oder irgendwas sollte rein, das als Aufrufbedingung dient.
Also Taster gedrückt, irgendeine Bedingung erfüllt, dann 3x blinken.

Weil das ist ja das ineressante.

Passt schon. :wink:

Ich danke Euch.

Gruß Chris

Aber die Blinkfrequenz ist ja auch eine Sekunde

Die Blinkfrequenz (interval)ist 300 ms

Ich gehe davon aus, dass bei einem doch recht wichtigen Thema wie diesem auch Anfänger mitlesen, da sollte man die Vorschläge nicht "halbfertig" stehen lassen. Das Chris das gerafft hat, ist mir klar.

Aber im Prinzip ist mit guntherb's "void myFunc1()" if buttonpressed schon alles gesagt. Die Lösung scheint am elegantesten.

ElEspanol:
Aber im Prinzip ist mit guntherb's "void myFunc1()" if buttonpressed schon alles gesagt. Die Lösung scheint am elegantesten.

Find ich auch

@combie: :wink:
man beachte die static Variablen:
Die Funktionen brauchen sich nicht drum zu kümmern, ob letztesEvent eventuell schon irgendwo anders existiert, denn jede hat ihr eigenes Exemplar.

Halte ich hier für die beste Lösung ...

michael_x:
@combie: :wink:
man beachte die static Variablen:
Die Funktionen brauchen sich nicht drum zu kümmern, ob letztesEvent eventuell schon irgendwo anders existiert, denn jede hat ihr eigenes Exemplar.

Halte ich hier für die beste Lösung ...

Naja ....

Die Funktion hat eine nicht offensichtliche Abhängigkeit.
Und zwar:
letztesEvent = 0;
Zufälliger Weise ist der Arduino interne MilliZähler auch 0 beim Start.

Die Funktion kann nur richtig funktionieren, wenn sie von Anfang an dauernd aufgerufen wird. Ist das nicht gegeben, wird sie versagen.

Es ist also nicht unbedingt möglich diese Funktion wiederzuverwenden, weil genau diese implizite Kontextabhängigkeit nicht unbedingt in der nächsten Situation reproduziert werden kann.
Diese Abhängigkeit ist nicht dokumentiert.
Sie geht auch nicht aus der Funktionsdeklaration hervor.
Ist also nicht "selbstdokumentierend"

Klarer:
Es wird in dieser Anwendung funktionieren. Aber auf lange Sicht öffnet es unerwünschten Seiteneffekten Tür und Tor.

Je mehr man solche "Dinger" in seinen Code einflechtet, desto schwieriger wird das Debuggen, desto fehlerträchtiger die Funktion. Und schwerer für andere zu durchblicken.

Das Verfahren bindet Variablen an/in Code.
Eigentlich wurde OOP (u.A.) genau dafür erfunden.

letztesEvent = 0;
Zufälliger Weise ist der Arduino interne MilliZähler auch 0 beim Start.

Das halte ich nicht für Zufall, das wird bewusst ausgenutzt.

Es ist also nicht unbedingt möglich diese Funktion wiederzuverwenden

Stimmt, setzt die Arduino Umgebung voraus. Ob ausser millis() noch mehr vorausgesetzt wird, wer weiss ? :wink:

Du solltest übrigens aufpassen:

static unsigned long letztesEvent = 0; // ist was komplett anderes als 

static unsigned long letztesEvent;
letztesEvent = 0; // so wohl nicht beabsichtigt.

Wenn man unterschiedliche Start-Offsets haben will, kann man das übrigens auch einbauen.

static unsigned long letzterAufruf = -ZYKLUS; // Erster Aufruf sofort nach Start

Das Verfahren bindet Variablen an/in Code.
Eigentlich wurde OOP (u.A.) genau dafür erfunden.

Da gebe ich dir natürlich recht.

Man kann auch eine FSM Library einbauen, sollte aber mindestens den Text

//poor example, but then again; it's just an example

gesehen haben.
Beispiele, die von einem ganzen Sack anderer Libraries abhängen, sind nicht leicht nachzuvollziehen.

OOP verlagert Probleme auf eine andere Ebene...
Schon toll, dass man hier c++ programmieren kann, aber es auch sein lassen kann. :wink:

Das halte ich nicht für Zufall, das wird bewusst ausgenutzt.

Ich habe eine Kaffeetasse, eine richtig große Programmiererkaffeetasse.
Auf der befindest sich ein Sprüchlein:
"Das kannste schon so machen, aber dann ist es halt Kacke."
Da kann ich dir gerne mal ein Bild von zukommen lassen.....

OOP verlagert Probleme auf eine andere Ebene...

In diesem Fall ließe sich das per OOP sauber lösen.
Man würde die Variable im Objekt kapseln und könnte sie trotzdem über Methoden manipulieren.

PS:
Du hast mich zu meiner Meinung dazu befragt...
Meine Meinung ist:
Mir fehlt die "Eleganz".

combie:
Mir fehlt die "Eleganz".

ich kann leider kein OOP, eleganter kann ich es nicht.

Aber das ist ok, es sollte ja auch ein Beispiel sein für Leute die gerade ihre ersten Softwareschritte gehen.

Aber die hast einige Themen angeschnitten, die mich interessieren:

combie:
Die Funktion hat eine nicht offensichtliche Abhängigkeit.
Und zwar:
letztesEvent = 0;
Zufälliger Weise ist der Arduino interne MilliZähler auch 0 beim Start.

Die Funktion kann nur richtig funktionieren, wenn sie von Anfang an dauernd aufgerufen wird. Ist das nicht gegeben, wird sie versagen.

Das verstehe ich nicht. So wie das sehe, wird die die Funktion beim ersten Aufruf, auch wenn der erst nach einigen Sekunden stattfindet, ausgeführt, weil die Bedingung "if (millis() - letztesEvent < 1000)" nicht erfüllt wird. Und ab da bei jedem Aufruf der >1000ms später stattfindet.

combie:
Es ist also nicht unbedingt möglich diese Funktion wiederzuverwenden, weil genau diese implizite Kontextabhängigkeit nicht unbedingt in der nächsten Situation reproduziert werden kann.
Diese Abhängigkeit ist nicht dokumentiert.

Kannst du das bitte näher erklären? Mir ist nicht klar, welche Abängigkeit du meinst.
Natürlich kann diese Funktion nur einmal pro Code verwendet werden. Wenn ich 2 LEDs unterschiedlich blinken lassen will, dann muss ich die Funktion mit neuem Namen nochmal anlegen.

Aber dann habe ich doch trotzdem keine Seiteneffekte, oder?

Leute die eine generelle Abneigung gegen OOP haben sollten dann bitte auch so konsequent sein und auf print()/println() zu verzichten. Das verwendet nämlich Vererbung und jeder Aufruf läuft über eine virtuelle Methode. Dass man auf einem 8 Bit/16MHz µC nicht die kompliziertesten Konstrukte aufziehen sollte ist klar, aber diese einfachen Sachen sind von der Performance in den aller meisten Fällen kein Problem, oder unterscheiden sich gar nicht von rein prozeduralem Code.

Serenifly:
Leute die eine generelle Abneigung gegen OOP haben sollten dann bitte auch so konsequent sein und auf print()/println() zu verzichten.

a) solange ich nicht weiß, dass print() OOP ist, kann ich es auch verwenden.
b) ich habe keine Abneigung gegen OOP, ich kanns einfach nicht.

Dich meinte ich damit nicht :slight_smile:

Wenn man es nicht kann ok, aber es gibt hier eine kleine Fraktion die OOP aus Prinzip ablehnt. Da fragt man sich schon wieso die die Arduino Software überhaupt verwenden.