While-Schleife wird nicht unterbrochen

Hallo,

ich habe da mal ein kleines Problem: Ich möchte eine LED ‘weich’ blinken lassen, aber nur wenn ZR24Zustand == 11. Die Funktion dazu wird im Loop-Teil aufgerufen. Drei Sekunden später wird der Wert der Variable verändert. Allerdings läuft die while-Schleife der LED immer noch und der Loop-Teil beginnt auch nicht von vorn. Was mache ich falsch? Wie kann ich die Schleife beim Ändern der Variable unterbrechen, während die LED gerade im Aus-Zustand ist. (Hinweis: common anode → also High und Low vertauscht)

Gruß
Paul

void ZR24Hla9() {
  ZR24Zustand = 11;
   while(ZR24Zustand == 11) {
    while(ZR24ge1_val != 0) {
      analogWrite(ZR24ge1, ZR24ge1_val);
      ZR24ge1_val --;
      delay(1);
    }
    delay(625);
    while(ZR24ge1_val != 255) {
      analogWrite(ZR24ge1, ZR24ge1_val);
      ZR24ge1_val ++;
      delay(1);
    }  
    delay(500);
  }
}
void loop() {
   ZR24Hla12();
   delay(3000);
   ZR24Hla3();
   delay(3000);
   ZR24Hla9();
   delay(3000);
   ZR24Zustand=15;
   
}

Techniker89:
Hallo,

ich habe da mal ein kleines Problem: Ich möchte eine LED ‘weich’ blinken lassen, aber nur wenn ZR24Zustand == 11. Die Funktion dazu wird im Loop-Teil aufgerufen. Drei Sekunden später wird der Wert der Variable verändert. Allerdings läuft die while-Schleife der LED immer noch und der Loop-Teil beginnt auch nicht von vorn.
Gruß
Paul

Dein Code hängt immer noch in der While-Schleife und kommt überhaupt nicht zur Wertänderung im Loop.

Lass die eigenen while-Schleifen weg, die Delay ebenfalls und arbeite mit millis() und Zuständen.

Suchtips: BlinkWithoutDelay und Nachtwächtererklärung zum Einarbeiten.

Gruß Tommy

Techniker89:
Hallo,

ich habe da mal ein kleines Problem: Ich möchte eine LED 'weich' blinken lassen, aber nur wenn ZR24Zustand == 11. Die Funktion dazu wird im Loop-Teil aufgerufen. Drei Sekunden später wird der Wert der Variable verändert. Allerdings läuft die while-Schleife der LED immer noch und der Loop-Teil beginnt auch nicht von vorn.

Wenn Du eine Schleife machst mit der Schleifenbedingung "while(ZR24Zustand == 11)", dann sollte die Variable ZR24Zustand wenigstens die Möglichkeit haben, sich innerhalb der Schleife zu ändern.

Wenn die Variable ZR24Zustand innerhalb des Schleifendurchlaufs konstant ist undsich nicht ändert, dann gibt es zwei Möglichkeiten:

  1. Die Bedingung ist beim ersten Schleifendurchlauf erfüllt
    In dem Fall ist die Bedingung auch bei JEDEM(!) weiteren Schleifendurchlauf erfüllt
    und Du hast dann natürlich eine Endlosschleife

  2. Die Bedingung ist nicht erfüllt
    In dem Fall ist sie bei allen weiteren Schleifendurchläufen ebenfalls nicht erfüllt
    und Du hast eine Schleife, die exakt NULL mal durchlaufen wird, also NIE ausgeführt wird

Merke:
Wenn Du am Anfang einer while-Schleife auf eine Bedingung mit einer Variablen testest,
dann achte darauf, dass die Variable sich innerhalb der Schleife auch ändern kann!
Sonst ändert sich nämlich auch die Schleifenbedingung nie,
und dann hast Du entweder eine Schleife, die entweder niemals oder unendlich oft ausgeführt wird!

Die unendliche Schleife kann man natürlich auch mit einem break; beenden,
je nach Kontext ist ein Verlassen auch via return; bzw. return retVal; möglich.

So endlos muss eine Schleife also nicht sein, nur weil sie eine invariante Bedingung hat.

Merke:

Denk dran, dass bei vernünftiger Programmierung loop() immer wieder dran kommt und mach aus demwhilegleich einif.

Eine Funktion, die Sekunden braucht, bis sie zurückkommt, taugt nichts, ausser für generelle Demos,
denn sie ist nicht mit anderen Arduino-typischen Aufgaben (z.B. Taster) kombinierbar.

Die unendliche Schleife kann man natürlich auch mit einem break; beenden,
je nach Kontext ist ein Verlassen auch via return; bzw. return retVal; möglich.

Auch per Goto kann man endlosen Schleifen entschlüpfen.

In meinen Augen sind Seitenausstiege als “Fehler” anzusehen.
Bestenfalls als Kompromiss mag ich sie akzeptieren.

combie:
Auch per Goto kann man endlosen Schleifen entschlüpfen.

Stimmt.

Für mich gehört das Statement aber nicht wirklich zum Sprachumfang von C++, deswegen habe ich das wohl verdrängt. :wink:

Ich halte breaks oft durchaus für sinnvoll, aber nicht als einzige Möglichkeit eine Schleife zu verlassen.

Meiner Meinung nach ist der "richtige" Weg aus einer While-Schleife eine unwahre Bedingung.
Mit einer If-Anweisung ein break() oder return() aufzurufen ist zwar möglich, aber meiner Meinung (meist) eine falsch Benutzung der While-Schleife.
Auch ein uneingeschrenkte Verwendung von goto() ist falsch weil C fast immer ohne damit auskommt.

Grüße Uwe

Hallo,

danke für eure Antworten!

und mach aus dem while gleich ein if

Da kann ich doch eigentlich kein if nehmen, oder?
Ich hätte es jetzt ansonsten mit for gemacht.

Du hast mit loop() schon eine Schleife! Das kann man dann Blockierungs-frei programmieren, so dass man nicht ständig den Prozessor mit delay() anhält, sonder abfragt ob die entsprechende Zeit schon abgelaufen ist. Dann kann mehrere Dinge quasi-gleichzeitig erledigen. Wichtig wenn man z.B. während der LED Blinkerei noch Taster abfragen muss

Schon klar, aber wie kann ich den ohne eine sich ständig wiederholende Schleife (also while) eine LED dimmen?

Techniker89:
Schon klar, aber wie kann ich den ohne eine sich ständig wiederholende Schleife (also while) eine LED dimmen?

In dem du deinen Betrachtungswinkel änderst und anfängst in Nebenläufigkeiten zu denken.
Ein Beispiel von vielen:
2 Tasten Dimmer

Ablaufsteuerung

Meine Standardantwort zu Ablaufsteuerungen:
Eine Stichworte Sammlung für Google Suchen:
Endlicher Automat,
State Machine,
Multitasking,
Coroutinen,
Ablaufsteuerung,
Schrittkette,
BlinkWithoutDelay,

Blink Without Delay
Der Wachmann

Multitasking Macros
Intervall Macros

Danke für den Code. Hab ihn mir mal angeschaut. Ich meine, das Prinzip verstanden zu haben. Bei mir funktioniert es zwar noch nicht, aber hier trotzdem nochmal mein Code:
(Bitte nicht böse werden, wenn wieder was daneben ist!)

void loop() {
// Signal ZR24
  if(ZR24Zustand == 11) {
      if(ZR24ge1_val == 0)  {
        ZR24ge1_Status = LOW;
        ZR24_LED_timestore_blink = millis();
     }
     if(ZR24ge1_val == 255)  {
        ZR24ge1_Status = HIGH;
        ZR24_LED_timestore_blink = millis();
     }

     if(millis() - ZR24_LED_timestore_blink > 500) {    
      if(ZR24ge1_Status == HIGH && millis() - ZR24_LED_timestore_dimm > 1)  {
       analogWrite(ZR24ge1, ZR24ge1_val);
       ZR24ge1_val --;
       ZR24_LED_timestore_dimm = millis();
      }
      if(ZR24ge1_Status == LOW && millis() - ZR24_LED_timestore_dimm > 1)  {
       analogWrite(ZR24ge1, ZR24ge1_val);
      ZR24ge1_val ++;
      ZR24_LED_timestore_dimm = millis();
     }
     } 
   }
}

Könnte es sein, dass dein neuer Code viel zu schnell ist?

Wenn deine sich ständig wiederholende Schleife die loop() ist, musst du nur die Helligkeit für jeden Durchlauf passend stellen.

Solange du noch in delay denkst: stell deine loop Zeit passend zur Dimm-Geschwindigkeit ein
z.B. in 5 sec von 0 … 255 = 5000ms / 256 = 20 ms

byte helligkeit= 0;
char richtung = 1;
void loop()  {
  analogWrite (led, helligkeit);
  if (helligkeit == 255) richtung = -1;
  if (helligkeit == 0) richtung =1;  
  helligkeit +=richtung; 
  delay(20); // schnell genug um "gleichzeitig" andere Aufgaben zu erledigen, langsam genug, damit in jedem Durchlauf helligkeit angepasst werden kann. 
}

Wenn du es richtig machen willst, rechnest du in jedem loop() Durchlauf die helligkeit aus der aktuellen Zeit aus, und kannst beliebig oft deinen loop-Durchlauf machen.

byte helligkeit= 0;
bool back = false;
unsigned long start;
const unsigned int dauer = 5000; // ms je Dimm-Phase 

void loop()  {
  if (millis() - start >= dauer) {
     start = millis();  // (millis() - start) = 0 .. dauer-1 
     back = ! back;
  } 
  byte helligkeit = (millis() - start) * 256 / dauer;  // 0 .. 255
  if (back) helligkeit = 255 - helligkeit;  // 255 .. 0   
  analogWrite (led, helligkeit);
}

Beide Beispiele machen bewusst etwas anderes als du eigentlich willst, damit du selbst auch was zu tun hast ; )

Hallo michael_x,

vielen Dank für deinen Code. Bei mir funktioniert er. Allerdings möchte ich ja zwischen den Dimm-Phasen je eine Pause einbauen. Ich hab's folgendermaßen probiert - hat mal wieder nicht geklappt:

if (millis() - start >= dauer) {
     start = millis();  // (millis() - start) = 0 .. dauer-1 
     back = ! back;
  }
  if(helligkeit == 0 || helligkeit == 255)  {
    start_pause = millis(); 
  }
  if(millis() - start_pause >= pause) {
  helligkeit = (millis() - start) * 256 / dauer;  // 0 .. 255
  if (back) helligkeit = 255 - helligkeit;  // 255 .. 0   
  analogWrite (ZR24ge1, helligkeit);
  }

Gruß
Paul

Edit:
So hab ich es jetzt doch hinbekommen:

if (millis() - start >= dauer) {
     start = millis();  // (millis() - start) = 0 .. dauer-1 
     back = ! back;
  }
  if (millis() - start_pause > pause) {
  helligkeit = (millis() - start) * 256 / dauer;  // 0 .. 255
  if (back) helligkeit = 255 - helligkeit;  // 255 .. 0
    if(helligkeit == 0 || helligkeit == 255)  {
    start_pause = millis(); 
  }   
  }
  analogWrite (ZR24ge1, helligkeit);

Allerdings wird die LED jetzt nicht mehr gleichmäßig und nur noch beim Einschalten gedimmt.

Ich würde die Pause einfach zurdauerhinzufügen:

const unsigned int dauer = 5000; // ms je Dimm-Phase 
const unsigned int gesamtdauer = 10000; // enthält zusätzlich die gewünschte Pause
...
unsigned long start;
bool back;
void loop() {
  if (millis() - start >= gesamtdauer) {
     start = millis();  // (millis() - start) = 0 .. gesamtdauer-1
     back = ! back;
  }

  unsigned int helligkeit = (millis() - start) * 256 / dauer;  // 0 .. , ab dauer > 255
  if(helligkeit > 255) helligkeit = 255; // bleibt zwischen dauer und gesamtdauer am Anschlag 
  if (back) helligkeit = 255 - helligkeit;  // 255 .. 0   
  analogWrite (ZR24ge1, helligkeit);
  }

Beachte die Definition der lokalen Variablen helligkeit : kann größer als 255 werden

Allerdings wird die LED jetzt nicht mehr gleichmäßig und nur noch beim Einschalten gedimmt

Anderer Leute Code ist immer schwerer zu verstehen als der eigene.
Manche Leute vergessen auch das wichtigste: die Variablendefinition. :wink:

Und damit auch einen Kommentar, was die Variable bedeutet und welche Werte zu erwarten sind.

(millis-start) hat einen Wert < dauer und legt eventuell die helligkeit fest
(millis-start_pause) kann einen Wert > pause annehmen, dann springt helligkeit sofort auf einen relativ großen Wert , abhängig von (millis-start).

Kann sein dass ich durch meinen Gegenvorschlag vorbelastet bin:
Dein Ansatz ist nicht sehr unterschiedlich, kann wohl noch etwas vereinfacht werden, damit leichter zu verstehen was du meinst, und kann sicher auch repariert werden dass er funktioniert.


Dass “gleichmässig dimmen” für manche hier komplizierter ist als den PWM Wert mit konstanter Rate zu ändern ( s. “logarithmisches Helligkeitsempfinden” ), lassen wir voererst hier mal aussen vor.

Danke, jetzt funktioniert's :slight_smile: