Ich versuche, DMX-Daten zu knacken, und das erfordert 4us-Impulse. Ich habe nicht viel Glück mit den Ergebnissen und überprüfe, wie gut der Arduino verzögert ... Scheint ziemlich schrecklich darin zu sein.
Hier ist ein kurzer kleiner Test, den ich gemacht habe:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
Und die Ergebnisse:
8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 8 8 8 8 4 8 4
Ich war ein bisschen schockiert darüber, wie schlecht die Genauigkeit ist. Es ist doppelt so viel Zeit, wie ich verzögern wollte, aber es stimmt nicht einmal damit überein, wo ich einfach durch 2 teilen könnte!
Kann ich irgendetwas tun, um korrekte und konsistente Ergebnisse zu erzielen?
arduino-mega
timers
time
Bwoogie
quelle
quelle
Antworten:
Wie in den vorherigen Antworten erläutert, ist Ihr eigentliches Problem nicht die Genauigkeit von
delayMicroseconds()
, sondern die Lösung vonmicros()
.Um Ihre eigentliche Frage zu beantworten , gibt es jedoch eine genauere Alternative zu
delayMicroseconds()
: Die Funktion_delay_us()
von AVR-libc ist zyklusgenau und beispielsweisemacht genau das, was es sagt. Die wichtigste Einschränkung ist, dass das Argument eine Konstante zur Kompilierungszeit sein muss. Sie müssen,
#include <util/delay.h>
um Zugriff auf diese Funktion zu haben.Beachten Sie auch, dass Sie die Interrupts blockieren müssen, wenn Sie eine genaue Verzögerung wünschen.
Bearbeiten : Wenn ich beispielsweise einen 4-µs-Impuls auf PD2 (Pin 19 auf dem Mega) erzeugen würde, würde ich wie folgt vorgehen. Beachten Sie zunächst den folgenden Code
macht einen 0,125 µs langen Impuls (2 CPU-Zyklen), da dies die Zeit ist, die benötigt wird, um den Befehl auszuführen, der den Port festlegt
LOW
. Fügen Sie dann einfach die fehlende Zeit verzögert hinzu:und Sie haben eine zyklusgenaue Impulsbreite. Es ist anzumerken, dass dies mit nicht erreicht werden kann
digitalWrite()
, da ein Aufruf dieser Funktion etwa 5 µs dauert.quelle
Ihre Testergebnisse sind irreführend.
delayMicroseconds()
tatsächlich Verzögerungen ziemlich genau (für Verzögerungen von mehr als 2 oder 3 Mikrosekunden). Sie können den Quellcode in der Datei /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (auf einem Linux-System oder auf einem ähnlichen Pfad auf anderen Systemen) überprüfen.Die Auflösung
micros()
beträgt jedoch vier Mikrosekunden. (Siehe z. B. die Garretlab-Seite übermicros()
.) Daher werden Sie niemals einen Messwert zwischen 4 Mikrosekunden und 8 Mikrosekunden sehen. Die tatsächliche Verzögerung beträgt möglicherweise nur einige Zyklen über 4 Mikrosekunden, Ihr Code meldet sie jedoch als 8.Versuchen Sie, 10 oder 20
delayMicroseconds(4);
Aufrufe hintereinander auszuführen (indem Sie den Code duplizieren, nicht mithilfe einer Schleife), und melden Sie dann das Ergebnis vonmicros()
.quelle
digitalWrite()
, was mehrere Mikrosekunden dauert, um ausgeführt zu werden.delayMicroseconds()
? Ich sehe das nicht besser als abzurunden. ¶ In Bezug auf die Ursache der Ungenauigkeit hängt das Timing vom umgebenden Code ab, wenn die Routine inline wird. Sie können Montage- oder Demontagelisten lesen, um zu sehen. (Siehe "Erstellen von Baugruppenlisten" in meiner Antwort auf die Frage Äquivalent für PORTB in Arduino Mega 2560 , die ohnehin für Ihr Bitbang-Projekt relevant sein kannmicros()
hat eine gut dokumentierte Auflösung von 4 µs.Sie können die Auflösung verbessern, indem Sie den Vorteiler für Timer 0 ändern (das wirft natürlich die Zahlen aus, aber Sie können das kompensieren).
Verwenden Sie alternativ Timer 1 oder Timer 2 mit einem Prescaler von 1, wodurch Sie eine Auflösung von 62,5 ns erhalten.
Das wird sowieso langsam.
Ihre Ausgabe stimmt genau mit der Auflösung von 4 µs überein,
micros()
verbunden mit der Tatsache, dass Sie manchmal zwei "Ticks" und manchmal einen bekommen, je nachdem, wann Sie das Timing gestartet haben.Ihr Code ist ein interessantes Beispiel für Messfehler.
delayMicroseconds(4);
verzögert sich um fast 4 µs. Ihre Versuche, es zu messen, sind jedoch schuld.Wenn ein Interrupt auftritt, wird das Intervall etwas verlängert. Sie müssen Interrupts ausschalten, wenn Sie eine genaue Verzögerung wünschen.
quelle
Bei der Messung mit einem Oszilloskop stellte ich Folgendes fest:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 μs reale Verzögerung.Wenn Sie also eine Verzögerung von 35 μs wünschen, benötigen Sie:
quelle
Die Arduino-Implementierung ist recht allgemein gehalten und daher in einigen Anwendungen möglicherweise nicht so effektiv. Es gibt einige Möglichkeiten für kurze Verzögerungen, von denen jede ihre eigenen Mängel aufweist.
Verwenden Sie nop. Jeder ist eine Anweisung, also 16. von uns.
Verwenden Sie tcnt0 direkt. Jeder ist 4us, da der Prescaler auf 64 eingestellt ist. Sie können den Prescaker ändern, um eine Auflösung von 16 us zu erreichen.
Verwenden Sie Ticks. Sie können einen Systick-Klon implementieren und als Grundlage für die Verzögerung verwenden. Es bietet eine feinere Auflösung und Genauigkeit.
bearbeiten:
Ich habe den folgenden Block verwendet, um die verschiedenen Ansätze zeitlich abzustimmen:
Zuvor hatte ich den Timer0-Prescaler auf 1: 1 zurückgesetzt, sodass jeder TCNT0-Tick 1/16 Mikrosekunde beträgt.
ich hoffe es hilft.
quelle
TCNT0
1 CPU-Zyklus dauert.