Periodische Ereignisse mit verschiedenen Triggerfrequenzen managen

Hallo und guten Morgen,

ich möchte mehrere Variablen unabhängig voneinander hochzählen, z.B. soll sich a jede halbe Sekunde um 1 erhöhen, b alle 2 Sekunden, c alle 5 Sekunden, usw.
Grundsätzlich funktioniert das ganz gut nach dem Prinzip von Blink without Delay, allerdings muss ich dann für jede Variable previousMillis einzeln speichern, was bei vielen Variablen ziemlich unübersichtlich wird.
Die Frage lautet, ob das nicht eleganter geht. Z.B. dachte ich an die Möglichkeit, zu prüfen, ob currentMillis/1000 ohne Rest durch 2 oder 3 teilbar ist.
Wie würdet ihr das machen, wenn es um viele (+-50) unabhängige Prozesse (=Variablen) geht?

Freue mich über Anregungen, beste Grüße,

Helmuth

Hallo,

ich würde es wie Blink without Delay machen, aber mit Arrays arbeiten, damit kann man die Überprüfungen auch in Schleifen erledigen.
Beim Teilen hast du das Problem, dass due den Zeitpunkt genau treffen muss. Wenn deine Loop länger als 1ms dauert, kann es passieren, dass ein Ereignis vergessen wird oder ein Ereignis mehrmals aufgerufen wird, weil die Schleife schneller als 1ms ging.

Es kommt aber auf dein spezielles Problem an. Bei blinkenden Lampen kann man auch einfach

digitalwrite(LED_PIN, (millis()/1000)%2)

nehmen.

Helmuth:
Wie würdet ihr das machen, wenn es um viele (±50) unabhängige Prozesse (=Variablen) geht?

Arrays und Schleifen.

// multiTimer by 'jurs' for German Arduino Forum
struct timer {unsigned long takt; unsigned long lastMillis; unsigned long count;};

timer multiTimer[10]; // Anzahl der Timer definieren
int timers=sizeof(multiTimer)/sizeof(multiTimer[0]); // Anzahl der Timer aus der Arraygröße berechnen

void updateTimers()
{
  unsigned long now=millis();
  for (int i=0;i<timers;i++) 
  {
    while (now-multiTimer[i].lastMillis>=multiTimer[i].takt)
    {
      multiTimer[i].lastMillis += multiTimer[i].takt;
      multiTimer[i].count++;
    }
  }
}

void showTimers()
{
  for (int i=0;i<timers;i++)
  {
    Serial.print(multiTimer[i].count);
    Serial.print('\t');
  }
  Serial.println();
}


void setup()
{
  Serial.begin(9600);
  // Timer initialisieren auf 1, 2, 3, ... Sekunden
  for (int i=0;i<timers;i++) multiTimer[i].takt=(i+1)*1000;
}

void loop()
{
  updateTimers();
  showTimers();
//  delay(5000);
}

Edit/Nachtrag: Ich habe den Code gerade nochmal leicht abgeändert, so dass wenn Du in der loop längere Delays drin hast, die länger dauern als der kürzeste Takt, beim nächsten “updateTimers();” alle die Zählungen nachgeholt werden, die während des delay angefallen wären.

Elegant würde ich das über eine Klasse machen.
Nennen wir sie z.B. TimedCounter, deren Instanzen mit einer Periodendauer erzeugt werden, deren aktuellen Zähler man abfragen kann, und fragen kann, ob seit dem letzten abfragen was zu tun ist.

( Soweit deine Aufgabenstellung )

class TimedCounter 
{
     unsigned long _z;
     unsigned long _lastrun;
public:
      TimedCounter(unsigned long zyklus);  // Konstruktor
       unsigned long getCounts();    // liefert den Zähler
       boolean trigger();      // sollte zyklisch aufgerufen werden: true wenn seit dem letzten Mal der Zähler erhöht wurde
};
TimedCounter t1(1000); // zählt im  Sekunden-Takt
TimedCounter t2(5000); // zählt im 5 Sekunden-Takt
TimedCounter tarray[] = {
     TimedCounter(500),
     TimedCounter(3000)
};

void loop()
{
    if (  t1.trigger()  )  doSekundentask();
}
void doSekundentask()
{
    // wird jede Sekunde einmal gestartet
}

Das kann man erweitern um eine callback Funktion je Zähler, die jeweils im gewünschten Zyklus aufgerufen wird,
und um eine generelle Funktion für alle, die einmal in loop aufgerufen wird und alle Zähler bearbeitet, wenn es wirklich viele werden...

Bei deinen Anforderungen würde ich keine Hardwaretimer - Interrupts verwenden, damit die callback Funktionen auch mal ein bisschen länger dauern können und alles machen dürfen.

Detailfragen wie z.B. "wenn ein Zyklus verpasst wurde, soll er ausfallen oder gleich hintereinander mehrfach triggern" kann man so oder so lösen, muss man nur bedenken ...

Wie immer, sowas ähnliches gibts sicher schon fertig.
(Suchen und verstehen/lernen ob es genau das ist was man braucht und anpassen, ist auch Aufwand)

Ob man die callback Funktionen hochtrabend "Prozesse" nennen sollte, ist Geschmackssache :wink:

@Jurs: Super, genau das habe ich gesucht! Danke.

@Theseus: Danke für den Hinweis mit dem genauen Treffen. Habe schon gerätselt, warum das nur manchmal funktioniert hat...

Im weitesten Sinne geht es tatsächlich um (viele und schnell) blinkende Lampen - für einen "Lichtsythesizer" sollen alle Parameter (von z.B. Oszillatoren, Filtern, LFOs) unabhängig voneinander und mit verschiedenen Wellenformen manipuliert werden.

@michael_x: Danke für den eleganten Vorschlag.