Die anderen Antworten sind sehr gut, aber ich möchte näher darauf eingehen, wie das micros()
funktioniert. Es liest immer den aktuellen Hardware-Timer (möglicherweise TCNT0
), der ständig von der Hardware aktualisiert wird (tatsächlich alle 4 µs aufgrund des Prescaler von 64). Anschließend wird die Anzahl der Timer 0-Überläufe hinzugefügt, die durch einen Timer-Überlauf-Interrupt (multipliziert mit 256) aktualisiert wird.
Somit können Sie sich auch innerhalb eines ISR auf die micros()
Aktualisierung verlassen. Allerdings , wenn Sie zu lange warten , dann verpassen Sie den Überlauf zu aktualisieren, und dann wird das Ergebnis zurück nach unten gehen (dh Sie 253 erhalten wird, 254, 255, 0, 1, 2, 3 usw.)
Dies ist micros()
- etwas vereinfacht, um Definitionen für andere Prozessoren zu entfernen:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
Der obige Code ermöglicht den Überlauf (er überprüft das TOV0-Bit), damit er mit dem Überlauf fertig wird, wenn die Interrupts ausgeschaltet sind, aber nur einmal - es ist nicht vorgesehen, zwei Überläufe zu behandeln.
TLDR;
- Verzögern Sie nicht innerhalb eines ISR
- Wenn du sie machen musst, kannst du das dann mit
micros()
aber nicht millis()
. Auch delayMicroseconds()
ist eine Möglichkeit.
- Verzögern Sie nicht länger als 500 µs oder Sie werden einen Timer-Überlauf verpassen.
- Selbst kurze Verzögerungen können dazu führen, dass Sie eingehende serielle Daten verpassen (bei 115200 Baud erhalten Sie alle 87 µs ein neues Zeichen).
micros()
ist kein ISR. Es ist eine normale Funktion. Mit dem TOV0-Flag können Sie die Hardware testen, um festzustellen, ob der Timer-Überlauf aufgetreten ist (aber noch nicht verarbeitet wurde).Es ist nicht falsch , eine
millis()
odermicros()
mehrere Interruptroutinen zu verwenden.Es ist falsch, sie falsch zu verwenden.
Die Hauptsache hier ist, dass, während Sie sich in einer Unterbrechungsroutine befinden, "die Uhr nicht tickt".
millis()
undmicros()
wird sich nicht ändernmicros()
.Sie können also auf jeden Fall die aktuelle Zeit in Ihrem ISR abrufen
millis()
odermicros()
herausfinden, aber erwarten Sie nicht, dass sich diese Zeit ändert.Es ist der Mangel an Änderungen in der Zeit, auf den in dem von Ihnen angegebenen Zitat hingewiesen wird.
delay()
hängt davon abmillis()
, zu wissen, wie viel Zeit vergangen ist. Da es sich nicht ändert,delay()
kann es niemals enden.Also im Wesentlichen
millis()
undmicros()
wird Ihnen sagen, wann Ihr ISR aufgerufen wurde, egal wann Sie sie in Ihrem ISR verwenden.quelle
micros()
Updates. Es liest immer das Hardware-Timer-Register.Der zitierte Satz ist keine Warnung, sondern lediglich eine Aussage darüber, wie die Dinge funktionieren.
Es ist an sich nichts Falsches daran, eine ordnungsgemäß geschriebene Interrupt-Routine zu verwenden
millis()
oder zu verwendenmicros()
.Auf der anderen Seite ist es per definitionem falsch, irgendetwas in einer falsch geschriebenen Interruptroutine zu tun.
Eine Interruptroutine, die mehr als ein paar Mikrosekunden benötigt, um ihre Arbeit zu erledigen, ist höchstwahrscheinlich falsch geschrieben.
Kurz gesagt: Eine ordnungsgemäß geschriebene Interrupt-Routine verursacht oder stößt nicht auf Probleme mit
millis()
odermicros()
.Bearbeiten: In Bezug auf "Warum sich micros ()" unregelmäßig verhält ", wie in einer Webseite " Überprüfung der Arduino micros-Funktion " erläutert , ist
micros()
Code auf einem gewöhnlichen Uno funktional äquivalent zuDies gibt ein vorzeichenloses Vier-Byte-Long zurück, das aus den drei niedrigsten Bytes von
timer0_overflow_count
und einem Byte von dem Timer-0-Zählregister besteht.Das
timer0_overflow_count
wird vomTIMER0_OVF_vect
Interrupt-Handler ungefähr einmal pro Millisekunde inkrementiert , wie in einer Untersuchung der Webseite mit der Arduino-Millis-Funktion erläutert .Bevor ein Interrupt-Handler gestartet wird, deaktiviert die AVR-Hardware Interrupts. Wenn (zum Beispiel) ein Interrupt-Handler fünf Millisekunden lang mit noch deaktivierten Interrupts ausgeführt wird, werden mindestens vier Timer-0-Überläufe übersehen. [Interrupts, die in C-Code im Arduino-System geschrieben wurden, sind nicht wiedereintrittsfähig (können mehrere überlappende Ausführungen innerhalb desselben Handlers korrekt verarbeiten), aber es könnte ein wiedereintrittsfähiger Assembler-Handler geschrieben werden, der Interrupts wieder aktiviert, bevor ein zeitaufwendiger Prozess beginnt.]
Mit anderen Worten, Timer-Überläufe stapeln sich nicht. Wenn ein Überlauf auftritt, bevor der Interrupt des vorherigen Überlaufs verarbeitet wurde,
millis()
verliert der Zähler eine Millisekunde, und die Diskrepanztimer0_overflow_count
macht sich wiederum auchmicros()
um eine Millisekunde negativ bemerkbar.Betrachtet man "kürzer als 500 μs" als oberes Zeitlimit für die Interrupt-Verarbeitung, "um zu verhindern, dass der Timer-Interrupt zu lange blockiert", könnte man auf knapp 1024 μs (z. B. 1020 μs) aufsteigen und
millis()
würde die meisten noch funktionieren Zeit. Ich betrachte einen Interrupt-Handler, der mehr als 5 μs als träge, mehr als 10 μs als träge und mehr als 20 μs als schneckenartig betrachtet.quelle
micros()
sich unberechenbar verhalten? Und was meinst du mit "richtig geschriebener Interruptroutine"? Ich nehme an, es bedeutet "kürzer als 500us" (um zu verhindern, dass der Timer-Interrupt zu lange blockiert wird), "flüchtige Variablen für die Kommunikation verwenden" und "Bibliothekscode nicht so oft wie möglich aufrufen". Gibt es noch etwas anderes?