Arduino: Verzögerung Mikrosekunden ()

8

Wie funktioniert die Funktion delayMicroseconds ()? Soweit ich weiß, ist der Vorteiler von timer0 auf 64 eingestellt. Für einen 16-MHz-Takt ergibt sich ein Wert von 4,0 us pro Zählung. Ich bin ein bisschen verwirrt in der Mathematik, um das 1uS-Intervall zu erreichen?

Hawk_08
quelle
4
In der Dokumentation heißt es: "Diese Funktion arbeitet sehr genau im Bereich von 3 Mikrosekunden und mehr. Wir können nicht garantieren, dass delayMicroseconds genau für kleinere Verzögerungszeiten funktioniert." In den Dokumenten für micros()heißt es: "Auf 16-MHz-Arduino-Karten (z. B. Duemilanove und Nano) hat diese Funktion eine Auflösung von vier Mikrosekunden (dh der zurückgegebene Wert ist immer ein Vielfaches von vier)."
RedGrittyBrick
Siehe auch electronic.stackexchange.com/q/22584/2191
RedGrittyBrick

Antworten:

8

Der Quellcode für diese Funktion ist ziemlich gut dokumentiert und befindet sich unter /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c auf Linux-Systemen. Windows-Systeme haben einen ähnlichen Pfad wie die Datei wiring.c. Bemühen Sie sich, die Datei zu finden und zu durchsuchen. Konzentrieren Sie sich vorerst nur auf diese einzelne Funktion, sie basiert nicht auf anderen Funktionen.

Wenn Sie den Code überprüfen, werden Sie feststellen, dass es nicht um Timer geht, sondern um Befehlszyklen. Der Code hängt stark davon ab, dass die Compileroptimierung für Sie genauso ist wie für den Entwickler der Bibliothek. Das ist eine Annahme des Autors! Die Anzahl der von jedem Befehl 'verbrannten' CPU-Zyklen ist im Atmel AVR-Befehlssatzdokument gut dokumentiert .

Zuerst wird geprüft, ob der Verzögerungswert gleich 1 ist, in diesem Fall wird nur von der Routine zurückgekehrt, die bereits über eine Mikrosekunde CPU-Zeit verbracht wurde.

Dann wird der Verzögerungswert mit vier ( <<=2) multipliziert . Die __asm__-loop wird in eine 4-CPU-Zyklusschleife kompiliert. 4 Zyklen × 4 = 16 Zyklen. 16MHz / (4 × 4) = 1MHz, was 1 us Zykluszeit benötigt, die Auflösung, nach der wir suchen.

Die letzten -2 Mikrosekunden (bevor die Schleife gestartet wird) sind wieder eine Korrektur des vom Overhead eingeführten Compilers. Das Aufrufen von __asm__-code von C erfordert einige zusätzliche Anweisungen zum Speichern von CPU-Registern.

Für ein normales Arduino bei 16 MHz wird nur der folgende Code kompiliert:

/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
        // calling avrlib's delay_us() function with low values (e.g. 1 or
        // 2 microseconds) gives delays longer than desired.
        //delay_us(us);
        // for the 16 MHz clock on most Arduino boards

        // for a one-microsecond delay, simply return.  the overhead
        // of the function call yields a delay of approximately 1 1/8 us.
        if (--us == 0)
                return;

        // the following loop takes a quarter of a microsecond (4 cycles)
        // per iteration, so execute it four times for each microsecond of
        // delay requested.
        us <<= 2;

        // account for the time taken in the preceeding commands.
        us -= 2;

        // busy wait
        __asm__ __volatile__ (
                "1: sbiw %0,1" "\n\t" // 2 cycles
                "brne 1b" : "=w" (us) : "0" (us) // 2 cycles
        );
}

Übrigens: Der kompilierte Code ist ziemlich genau, aber beachten Sie Folgendes: Auf Arduino sind zeitgesteuerte Interrupts konfiguriert, von denen die meisten nichts wissen. Wenn während der Ausführung von ein Interrupt empfangen wird delayMicroseconds(), ist das Timing von delayMicroseconds()falsch. Sie können Interrupts natürlich vor dem Aufruf stoppen delayMicroseconds()und anschließend aktivieren, dies wirkt sich jedoch wiederum auf die Timing-Genauigkeit durch die Dauer des kompilierten Codes zum Aktivieren / Deaktivieren aus.

Jippie
quelle
Oder wenn Sie die Arduino IDE nicht installiert haben, ist diese Datei unter github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
microtherion