LED-Animation ohne delay.. der blanke Horror

Hallo,

habe hier ein Projekt, bei dem es mir sehr wichtig ist den Code nicht durch die delay-Problematik zu belasten, aber muss es denn wirklich so kompliziert sein?

Gegen Ende des Codes sind einige (z.T. unvollständige) Zeilen für eine spätere Erweiterung der Animation auskommentiert.

Habt Ihr eine Idee, wie man das einfacher schreiben könnte?

Gruß Chris

void animation01()
{
  Serial.println("");
  Serial.println("\t\t\t\t***  Globale Geraeteauswahl aktiviert!  ***");
  Serial.println("");LEDbit=LEDbit & LEDloeschmaske;
  LEDbit=LEDbit | 0b00000001;
  LEDschaltvorgang(LEDbit);
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 0)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00000011;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
    Serial.println("01");
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 1)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00000111;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
    Serial.println("02");
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 2)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00001111;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 3)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00011111;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 4)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00011110;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 5)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00011100;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 6)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00011000;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 7)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00010000;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 8)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00000000;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 9)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00000001;
    LEDschaltvorgang(LEDbit);
    animationsVerlaengerer++;
  }
  if (millis () > animationsZeitpunkt + animationsZeit + (animationsZeit * animationsVerlaengerer) && animationsVerlaengerer == 10)
  {
    LEDbit=LEDbit & LEDloeschmaske;
    LEDbit=LEDbit | 0b00000011;
    LEDschaltvorgang(LEDbit);/*
  LEDbit=LEDbit & LEDloeschmaske;
  LEDbit=LEDbit | 0b00000111;
  LEDschaltvorgang(LEDbit);
  delay(animationsZeit);
  LEDbit=LEDbit & LEDloeschmaske;
  LEDbit=LEDbit | 0b00001111;
  LEDschaltvorgang(LEDbit);
  delay(animationsZeit);
  LEDbit=LEDbit & LEDloeschmaske;
  LEDbit=LEDbit | 0b00011111;
  LEDschaltvorgang(LEDbit);*/
  animationsVerlaengerer = 0;
  animationsStatus = 0;
}
}

Chris72622:
Habt Ihr eine Idee, wie man das einfacher schreiben könnte?

Also meine Idee ist:

Du rufst aus der loop Deine Animationsfunktion auf.

Die Animationsfunktion schaut als erstes nach, wie viel Zeit seit dem letzten Aufruf vergangen ist. Wenn noch nicht genug Zeit vergangen ist, macht die Funktion gar nichts und kehrt mit “return” vorzeitig zur aufrufenden loop zurück. Dadurch kann die Funktion zigtausendmal aufgerufen werden, ohne dass etwas passiert. Erst nach Ablauf von STEPTIME wird eine neue Animationsphase berechnet und angezeigt.

Als Anzeigefunktion habe ich jetzt mal nur eine Anzeige auf dem seriellen Monitor gemacht.
Genauso kann man in der Anzeigefunktion aber auch LEDs ein und ausschalten statt 1 und 0 auszugeben.

Die Animation ist einfach ein einzelnes von rechts nach links laufendes Bit.

#define STEPTIME 300 // Dauer einer Animationsphase in Millisekunden

void setup() 
{
  Serial.begin(9600);
}

void showAnimation(byte b)
{ // Ein Byte bitweise auf dem seriellen Monitor mit Nullen und Einsen anzeigen
  for (int i=7;i>=0;i--)
  {
    Serial.print(bitRead(b,i));
  }  
  Serial.println();
}

void animation01()
{
  static unsigned long lastRuntime=0;
  static byte muster=0;
  if (millis()-lastRuntime<STEPTIME) return; // Vorzeitiges Ende, weil Zeit noch nicht um
  lastRuntime=millis(); // Zeit merken
  muster= muster<<1;    // Das Bit im Muster um eine Stelle nach lins schieben
  if (muster==0b00000000) muster=0b00000001; // Wenn das Bit links rausgeschoben wurde, rechts ein neues Bit setzen
  showAnimation(muster); // Animation darstellen
}

void loop() {
  animation01();
}

So ungefähr oder wie soll die Animation aussehen?

Oder schau mal hier rein:

http://forum.arduino.cc/index.php?topic=165552.0

Ich habe die allgemein für zeitliche Abläufe ohne delay geschrieben. Bisher hab ich seit dem noch nicht wieder erneut über komplizierte Zeitabläufe grübeln müssen.

Wow- was ein Machtwerk.

Nicht leicht zu verdauen für einen Anfänger, aber ich werde es mir definitiv mal im Detail anschauen.

jurs' Code liest sich, da spezieller auf meine Anforderung zugeschnitten, natürlich leichter.

Ich danke Euch Beiden herzlichst!

Gruß Chris

jurs:

static unsigned long lastRuntime=0;

Hallo jurs, gibt es einen bestimmten Grund, warum sich diese Zeile innerhalb der Funktion befindet und nicht auch oben bei..

#define STEPTIME 300 // Dauer einer Animationsphase in Millisekunden

..?

Gruß Chris

Chris72622:

jurs:

static unsigned long lastRuntime=0;

Hallo jurs, gibt es einen bestimmten Grund, warum sich diese Zeile innerhalb der Funktion befindet und nicht auch oben bei…

#define STEPTIME 300 // Dauer einer Animationsphase in Millisekunden

…?

Weil sich das bewährt hat und ich damit nicht alleine bin es so zu machen.

#define Definitionen schreibe ich immer in Großbuchstaben und meistens oben in den Code.

#define Definitionen werden vom Präprozessor vor dem eigentlichen Kompiliervorgang ausgewertet und gelten immer für die gesamte Datei.

Variablen, die nur innerhalb einer einzigen Funktion verwendet werden, deklariere ich innerhalb dieser Funktion, also dort, wo die Variable verwendet wird. Wenn eine Variable zwischen zwei Funktionsaufrufen den Inhalt und die Gültigkeit behalten sollen, muß die Variable “static” deklariert sein.

Man könnte natürlich auch eine “globale” Variable oben im Programm deklarieren, aber dann muß man später aufpassen, dass in anderen Funktionen diese Variable nicht aus Versehen fälschlicherweise für etwas verwendet wird, für das sie nicht vorgesehen ist. Als “lokal” definierte Variable innerhalb einer Funktion ist diese Variable automatisch davor geschützt, dass man dieselbe Variable in einer anderen Funktion verwendet, da achtet der Compiler auf den Gültigkeitsbereich.

Global deklarierte Variablen ==> Jede Funktion kann diese Variable verwenden
Lokal deklarierte Variablen ==> Nur in der Funktion, in der die Variable deklariert wurde, kann sie verwendet werden

Danke jurs,

soweit alles verstanden, bis auf Folgendes:

Wenn ich in Deinem Code das static weglassen würde, hätte dies keinerlei Auswirkungen auf den Programmablauf, oder?
Welchen Nutzen hat das static dann in diesem Fall?

Gruß Chris

Chris72622:
Wenn ich in Deinem Code das static weglassen würde, hätte dies keinerlei Auswirkungen auf den Programmablauf, oder?

Nein, das "static" in meinem Code kannst Du nicht weglassen.

"static" ist notwendig für Variablen, die

  • lokal innerhalb einer Funktion deklariert sind und
  • zwischen verschiedenen Funktionsaufrufen den Variableninhalt beibehalten sollen

Wenn das "static" bei lokalen Variablen fehlt, haben lokale Variablen beim Aufruf der Funktion einen "unbestimmten Inhalt" von zufälligen Speicherstellen aus dem RAM. Das kann bei einer schnell nacheinander aufgerufenen Funktion zwar (mehr oder weniger zufällig) dann auch der Speicherinhalt vom vorherigen Funktionsaufruf sein, aber das ist nicht sichergestellt.

Das "static" wegzulassen, wenn die Variableninhalte lokaler Variablen zwischen Funktionsaufrufen erhalten bleiben sollen, ist ein Programmfehler. Selbst wenn das Programm in manchen Fällen trotz Programmfehler richtig zu funktionieren scheint.

Wo Du das "static" immer weglassen kannst: Bei der Deklaration von globalen Variablen "oben im Programm", also Variablendeklarationen außerhalb von Funktionen.

jurs:
Wenn das "static" bei lokalen Variablen fehlt, haben lokale Variablen beim Aufruf der Funktion einen "unbestimmten Inhalt" von zufälligen Speicherstellen aus dem RAM.

In diesem Fall wird doch aber der "Inhalt" von lastRuntime bereits ganz am Anfang der Funktion über..

lastRuntime=0;

..klar definiert, oder besteht da ein schwerwiegender Denkfehler meinerseits?

Gruß Chris

Chris72622:
oder besteht da ein schwerwiegender Denkfehler meinerseits?

Ja, Denkfehler.

Der Denkfehler bezieht sich offenbar auf die abweichende Initialisierung von "static" Variablen.

Wenn Du eine "initialisierte static Variable" in einer Funktion hast, z.B.:
static unsigned long lastRuntime=0;
dann wird die Initialisierung genau EINMAL BEIM PROGRAMMSTART ausgeführt, danach nie wieder.
Im übrigen - und wie bereits mehrfach erwähnt - behält eine static Variable immer ihren Inhalt zwischen den Funktionsaufrufen. D.h. wenn die Funktion beendet ist und irgendwann wieder aufgerufen wird, hat die Variable denselben Inhalt wie zu dem Zeitpunkt als die Funktion vorher beendet wurde.

Wenn Du eine "normale initialisierte Variable" als lokale Variable in einer Funktion hast, z.B.:
unsigned long lastRuntime=0;
dann wird diese Variable BEI JEDEM FUNKTIONSAUFRUF neu initialisiert.

Jetzt hab auch ich es endlich kapiert. :roll_eyes:

Danke! XD

Gruß Chris