wenn man die Hauptschleife "loop" nicht mit delay anhalten will macht man das für gewöhnlich mit millis(). Das habe ich in der Variante
if ( (millis() - BlinkTimer) > (BlinkPeriod / 2)) // half period on half period off
{
BlinkTimer = millis();
// sonstiger code
auch hinbekommen.
Jetzt würde ich gerne eine function programmieren die so viel wie möglich von den obigen Codezeilen ins sich "kapselt".
So dass der Aufruf des Codes der immer erst nach verstreichen des angegeben Zeitintervalls ausgeführt wird idealerweise so aussieht:
if TimerIsOver (TimerNr,Interval){
// sonstiger code
}
Ich bin mit den Grundkonzepten des Programmierens gut vertraut und habe schon viele tausend Zeilen Code in Delphi geschrieben. Aber halt noch nicht viel in Arduino C++.
Wenn ich globale Variablen benutze weiß ich wie ich das programmiere. Ich strebe aber an, dass möglichst zu vermeiden weil man dann immer mehrere Stellen hat an denen man für einen neuen Timer
code schreiben muss.
So kurz wie oben pseudo-codiert geht wahrscheinlich nicht.
wie könnte man diesem Ideal möglichst nahe kommen?
StefanL38:
wie könnte man diesem Ideal möglichst nahe kommen?
Mir fällt dazu spontan ein, dass Du Klassen verwenden könntest. In loop() hättest Du dann z. B. lediglich
dingsda.action();
Sowohl die Abfrage, ob eine Zeit abgelaufen ist als auch den dazugehörenden Code hättest Du dann gekapselt - ich verschiebe so etwas in eigene Tabs, damit der Tab mit loop() möglichst übersichtlich bleibt.
also wenn du nur die globalen Variabeln vermeiden möchtest und die Loop so klein wie möglich halten willst, dann würde ich das so angehen.
/*
Blink without Delay
Funktionalität in eine Funktion ausgelagert
https://forum.arduino.cc/index.php?topic=639667.msg4196651#new
*/
// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN;// the number of the LED pin
void do_blink()
{
const long interval = 1000; // interval at which to blink (milliseconds)
static byte ledState = LOW; // ledState used to set the LED
static unsigned long previousMillis = 0; // will store last time LED was updated
if (millis() - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = millis();
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// here is where you'd put code that needs to be running all the time.
do_blink();
}
Die Funktion kümmert sich dann um das Zeitmanagement und ihre Variablen.
Durch die definition "static" überleben die Werte der Variablen von einem Aufruf zum nächsten.
Den Ledpin könnte man evtl. auch noch runter in die Funktion nehmen, aber ich vieleicht willst du mit dem LEDpin sonst auch noch was tun. Jedenfalls soll das System nun klar sein.
Wie schon geschrieben wurde, gibt es da eine ganze Reihe Lösungen für. Meine MobaTools enthalten die Klasse 'EggTimer', der ganau das macht. Wenn Du deswegen nicht gleich die ganze Lib installieren willst, ich hab's mal rauskopiert. Das könntest Du dann an den Anfang deines Sketches stellen:
class EggTimer
{
public:
EggTimer();
void setTime( long);
bool running();
long getTime();
private:
bool active;
long endtime;
};
// ========================= Class Definitions ============================================
////////////////////////////////////////////////////////////////////////////
// Class EggTimer - Timerverwaltung für Zeitverzögerungen in der Loop-Schleife
//
void EggTimer::setTime( long wert ) {
endtime = (long) millis() + ( (long)wert>0?wert:1 );
active = true;
}
bool EggTimer::running() {
if ( active ) active = ( endtime - (long)millis() > 0 );
return active;
}
long EggTimer::getTime() {
// return remaining time
if ( running() ) return endtime - (long)millis();
else return 0;
}
EggTimer::EggTimer()
{
active = false;
}
Wobei Du dann auch Definition und Deklaration zusammenführen könntest.
N.B. Bevor Beschwerden kommen: es funktioniert auch über die 49 Tage hinweg, obwohl nicht 'unsigned long' sondern 'long' verwendet wird. Die Variante spart aber pro Instanz 3 Byte des raren Ram. Einzige Einschränking: die Laufzeit des Timers muss < ca.24 Tage sein. Edit: Die Ersparnis resultiert daraus, dass ich mir durch die Verwendung von 'long' nur die Endzeit merken muss, und nicht Startzeit und Länge. 'long' an sich braucht natürlich genauso viel Platz wie 'unsigned long'. ( Ich habe auch das #endif' entfernt - s.u. den Beitrag von Tommy56 )
Wie Du siehst: Du hast die freie Auswahl unter vielen Lösungen
MicroBahner:
N.B. Bevor Beschwerden kommen: es funktioniert auch über die 49 Tage hinweg, obwohl nicht 'unsigned long' sondern 'long' verwendet wird. Die Variante spart aber pro Instanz 3 Byte des raren Ram.
Seit wann ist long kürzer als unsigned long? Der Unterschied zwischen beiden ist doch nur die Bedeutung des höchstwertigen Bits.
endtime (das Bilden von Summen) ist ein zum Scheitern verurteiltes Konzept.
Habe ich auch erst gedacht.
Und dann einen drumrum Test gebaut.
Ihm hat aber recht, es halbiert nur den maximalen Zeitraum.
Auf den kritischen Grenzen arbeitet es korrekt.
#include <util/atomic.h>
#define AtomicSection ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
class EggTimer
{
public:
EggTimer();
void setTime( long);
bool running();
long getTime();
private:
bool active;
long endtime;
};
// ========================= Class Definitions ============================================
////////////////////////////////////////////////////////////////////////////
// Class EggTimer - Timerverwaltung für Zeitverzögerungen in der Loop-Schleife
//
void EggTimer::setTime( long wert ) {
endtime = (long) millis() + ( (long)wert>0?wert:1 );
active = true;
}
bool EggTimer::running() {
if ( active ) active = ( endtime - (long)millis() > 0 );
return active;
}
long EggTimer::getTime() {
// return remaining time
if ( running() ) return endtime - (long)millis();
else return 0;
}
EggTimer::EggTimer()
{
active = false;
}
extern volatile unsigned long timer0_millis;
EggTimer timer;
void test()
{
timer.setTime(20);
while(timer.running())
{
Serial.print(timer.getTime());
Serial.println();
delay(1);
}
}
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println("Test 1");
AtomicSection timer0_millis = 0;
test();
delay(100);
Serial.println("Test 2");
AtomicSection timer0_millis = 0xFFFFFFFFUL / 2 -10;
test();
delay(100);
Serial.println("Test 3");
AtomicSection timer0_millis = 0xFFFFFFFFUL -10;
test();
}
void loop() {
// put your main code here, to run repeatedly:
}
Tommy56:
Seit wann ist long kürzer als unsigned long? Der Unterschied zwischen beiden ist doch nur die Bedeutung des Das #endif muss aus dem Code noch raus.
Ups, ja danke, da habe ich zuviel kopiert.
Und das mit den Beschwerden hat wohl doch seine Berechtigung - denn sie kamen ja trotzdem schon
Whandall:
long ist Quatsch für millis() oder micros() und spart natürlich keinen RAM.
endtime (das Bilden von Summen) ist ein zum Scheitern verurteiltes Konzept.
long ist genau deshalb nötig, damit das mit der endtime funktioniert. Und dann spart es ( ok, indirekt ) eben auch Ram, da ich mir nur die Endzeit merken muss, und nicht Startzeit und Länge.
Nicht immer nur die ausgetretenen Pfade zu wandeln erweitert den Horizont 8)
Gut, war in #5 wohl etwas zu knapp geschrieben. Wer wirklich Intervalle > 24 Tage braucht, kann es in der Tat nicht nutzen. Das dürften aber nicht allzuviele sein. Und grenzenlos ist es ja auch mit der unsigned long Variante nicht. Knappes Ram dürfte öfter vorkommen, als die Notwendigkeit von Intervallen > 24 Tage.