Als ich ein altes Projekt durchging, hatte ich Code auf zwei Arduino Due, der so aussah
void loop()
{
foo();
delay(time);
}
Ich habe mir den größten Teil der Literatur über die Verwendung zu Herzen genommen und delay();
dies als neu kodiert
void loop()
{
static unsigned long PrevTime;
if(millis()-PrevTime>time)
{
foo();
PrevTime=millis();
}
}
Dies scheint jedoch zu einer Situation geführt zu haben, in der die beiden Geräte über einen Zeitraum hinweg driften, in dem sie dies zuvor nicht getan haben
Meine Frage ist zweifach:
- Warum sollte
if(millis()-PrevTime>time)
mehr Drift verursachen alsdelay(time)
? - Gibt es eine Möglichkeit, diese Abweichung zu verhindern, ohne zurück zu gehen
delay(time)
?
arduino-due
code-review
timing
ATE-ENGE
quelle
quelle
foo; delay;
) einen Zeitraum von mehr als 100 ms hat (es ist 100 ms + Zeit vonfoo
). Sie werden also eine Drift erleben (aber es ist diedelay
implementierte SW, die driftet). Denken Sie auf jeden Fall daran, dass selbst vollkommen gleiche Implementierungen "driften", da die Uhren nicht gleich sind. Wenn Sie eine vollständige Synchronisierung benötigen, verwenden Sie ein Signal, um die beiden Programme zu synchronisieren.Antworten:
Es gibt eine wichtige Sache, an die Sie denken müssen, wenn Sie mit der Zeit an einem Arudino jeglicher Form arbeiten:
Ihre Funktion foo () wird einige Zeit in Anspruch nehmen. Was diese Zeit ist, können wir nicht sagen.
Der zuverlässigste Weg, mit der Zeit umzugehen, besteht darin, sich nur auf die Zeit für das Auslösen zu verlassen und nicht darauf, wann die nächste Auslösung erfolgen sollte.
Nehmen Sie zum Beispiel Folgendes:
Die Variable
last
ist die Zeit, die die Routine ausgelöst hat * plus die Zeit,doSomething
die zum Ausführen benötigt wurde. Angenommen, esinterval
ist 100, unddoSomething
die Ausführung dauert 10 ms. Sie erhalten Auslösungen bei 101 ms, 212 ms, 323 ms usw. Nicht die erwarteten 100 ms.Eine Sache, die Sie tun können, ist, immer dieselbe Zeit zu verwenden, unabhängig davon, indem Sie sich an einen bestimmten Punkt erinnern (wie Juraj vorschlägt):
Jetzt hat die Zeit, die
doSomething()
benötigt wird, keine Auswirkung auf irgendetwas. Sie erhalten also Auslösungen bei 101 ms, 202 ms, 303 ms usw. Immer noch nicht ganz die 100 ms, die Sie wollten - weil Sie nach mehr als 100 ms suchen, die vergangen sind - und das bedeutet 101 ms oder mehr. Stattdessen sollten Sie verwenden>=
:Angenommen, in Ihrer Schleife passiert nichts anderes, erhalten Sie Auslösungen bei 100 ms, 200 ms, 300 ms usw. Beachten Sie jedoch das Bit: "Solange in Ihrer Schleife nichts anderes passiert" ...
Was passiert, wenn eine Operation, die 5 ms dauert, bei 99 ms stattfindet ...? Ihre nächste Auslösung wird auf 104 ms verzögert. Das ist ein Drift. Aber es ist leicht zu bekämpfen. Anstatt zu sagen "Die aufgezeichnete Zeit ist jetzt", sagen Sie "Die aufgezeichnete Zeit ist 100 ms später als sie war". Das bedeutet, dass unabhängig von den Verzögerungen, die Sie in Ihrem Code erhalten, Ihre Auslösung immer in Intervallen von 100 ms erfolgt oder innerhalb eines Ticks von 100 ms driftet.
Jetzt erhalten Sie Auslösungen bei 100 ms, 200 ms, 300 ms usw. Oder wenn es Verzögerungen bei anderen Codebits gibt, erhalten Sie möglicherweise 100 ms, 204 ms, 300 ms, 408 ms, 503 ms, 600 ms usw. Es wird immer versucht, sie so nahe wie möglich auszuführen das Intervall wie möglich unabhängig von Verzögerungen. Und wenn Sie Verzögerungen haben, die größer als das Intervall sind, wird Ihre Routine automatisch so oft ausgeführt, dass Sie die aktuelle Zeit einholen können.
Bevor du driftest . Jetzt hast du Jitter .
quelle
Weil Sie den Timer nach dem Vorgang zurücksetzen.
quelle
Für das, was Sie versuchen, ist delay () der geeignete Weg, um den Code zu implementieren. Der Grund, warum Sie if (millis ()) verwenden möchten, besteht darin, dass Sie zulassen möchten, dass die Hauptschleife weiter wiederholt wird, damit Ihr Code oder ein anderer Code außerhalb dieser Schleife eine andere Verarbeitung ausführen kann.
Zum Beispiel:
Dies würde foo () in dem angegebenen Intervall ausführen, während die Schleife dazwischen weiter ausgeführt werden kann. Ich habe die Berechnung von next_trigger_time vor den Aufruf von foo () gestellt, um die Drift zu minimieren, aber das ist unvermeidlich. Wenn die Drift ein wichtiges Problem darstellt, verwenden Sie einen Interrupt-Timer oder eine Art Takt / Timer-Synchronisation. Denken Sie auch daran, dass millis () nach einiger Zeit herumläuft und ich dies nicht berücksichtigt habe, um das Codebeispiel einfach zu halten.
quelle
long m - millis()
macht nicht das, was Sie vorhaben? Das ist auf dem Haus.Ihr Code ist korrekt.
Das Problem, auf das Sie stoßen, ist millis (): Es wird leicht unterzählt (die maximale Unterzählung beträgt nur knapp 1 ms pro Aufruf).
Die Lösung ist mit feineren Zecken wie micros () - aber das wird auch leicht unterzählen.
quelle