Ich arbeite an einem Projekt mit einem cortex-m4 mcu (LPC4370). Und ich muss eine Verzögerung einfügen, während ich die Compiler-Optimierung einschalte. Bisher bestand meine Problemumgehung darin, einen digitalen Ausgang innerhalb einer for-Schleife auf und ab zu bewegen:
for (int i = 0; i < 50000; i++)
{
LPC_GPIO_PORT->B[DEBUGPIN_PORT][DEBUG_PIN1] = TRUE;
LPC_GPIO_PORT->B[DEBUGPIN_PORT][DEBUG_PIN1] = FALSE;
}
Aber ich frage mich, ob es einen besseren Weg gibt, GCC zu täuschen.
Antworten:
Der Kontext dieser Inline-Verzögerung ohne Abhängigkeit fehlt hier. Ich gehe jedoch davon aus, dass Sie während der Initialisierung oder eines anderen Teils des Codes, in dem er blockieren darf, eine kurze Verzögerung benötigen.
Ihre Frage sollte nicht sein, wie man GCC zum Narren hält . Sie sollten GCC sagen, was Sie wollen.
Von der Oberseite meines Kopfes wird diese Schleife ungefähr 5 * T Uhren sein.
( Quelle )
Fairer Kommentar von Colin zu einer anderen Antwort . Es ist nicht garantiert, dass ein NOP Zyklen auf einem M4 benötigt. Wenn Sie die Geschwindigkeit verringern möchten, ist ISB (Flush Pipeline) möglicherweise die bessere Option. Siehe das allgemeine Benutzerhandbuch .
quelle
nop
stattdessen auch eine Nichtanweisung verwenden, die nicht aus der Pipeline entfernt wird.-O0
macht ein bisschen schlechter als 5 * T , so etwas wie 8 Anweisungen mit ein paar Overheads. Es wäre besser, eine kurze optimierte Schleife zu erstellen (oder zumindest eine, die auf die gleiche Weise kompiliert wird, ohne Pragmas zu verwenden) und__asm__ __volatile__("");
zu verhindern, dass GCC die Schleife entfernt, dh so etwas .Verwenden Sie einen Timer, falls Sie einen zur Verfügung haben. Der SysTick ist sehr einfach zu konfigurieren. Die Dokumentation finden Sie im Cortex M4-Benutzerhandbuch (oder M0, wenn Sie sich im M0-Teil befinden). Erhöhen Sie eine Zahl in ihrem Interrupt, und in Ihrer Verzögerungsfunktion können Sie blockieren, bis die Zahl eine bestimmte Anzahl von Schritten erhöht hat.
Ihr Teil enthält viele Timer, wenn der Systick bereits verwendet wird und das Prinzip dasselbe bleibt. Wenn Sie einen anderen Timer verwenden, können Sie ihn als Zähler konfigurieren und sich nur das Zählregister ansehen, um einen Interrupt zu vermeiden.
Wenn Sie es wirklich in Software tun möchten, können Sie es in
asm("nop");
Ihre Schleife einfügen.nop
muss keine Zeit in Anspruch nehmen, der Prozessor kann sie aus seiner Pipeline entfernen, ohne sie auszuführen, aber der Compiler sollte die Schleife trotzdem generieren.quelle
wait_microseconds(100);
Dinge im Code haben.Nicht um andere Antworten hier abzulenken, aber genau welche Längenverzögerung benötigen Sie? Einige Datenblätter erwähnen Nanosekunden; andere Mikrosekunden; und noch andere Millisekunden.
NOP
,JMP
-bis-next-Anweisung oder andere zeitraubende Anweisungen sind ausreichend.for
Schleife (abhängig von der CPU-Rate) verursacht werden, längere können jedoch das Warten auf einen tatsächlichen Timer rechtfertigen.Kurz gesagt, alles hängt vom Peripheriegerät ab.
quelle
Der beste Weg ist die Verwendung von On-Chip-Timern. Systick-, RTC- oder Peripherie-Timer. Diese haben den Vorteil, dass das Timing präzise und deterministisch ist und leicht angepasst werden kann, wenn die CPU-Taktrate geändert wird. Optional können Sie die CPU sogar in den Ruhezustand versetzen und einen Weckinterrupt verwenden.
Schmutzige "Busy-Delay" -Schleifen hingegen sind selten genau und weisen verschiedene Probleme auf, z. B. eine "enge Kopplung" an einen bestimmten CPU-Befehlssatz und eine bestimmte Uhr.
Einige bemerkenswerte Dinge:
Wenn Sie darauf bestehen möchten, eine schmutzige Besetztschleife zu generieren, reicht es aus, nur
volatile
den Schleifeniterator zu qualifizieren. Zum Beispiel:Dies erzeugt garantiert verschiedene Mistcodes. Zum Beispiel
-O3 -ffreestanding
gibt ARM gcc :Von da an können Sie theoretisch berechnen, wie viele Ticks jeder Befehl benötigt, und die magische Zahl 50000 entsprechend ändern. Pipelining, Verzweigungsvorhersage usw. bedeuten jedoch, dass der Code möglicherweise schneller als nur die Summe der Taktzyklen ausgeführt wird. Da der Compiler beschlossen hat, den Stack einzubeziehen, könnte auch das Zwischenspeichern von Daten eine Rolle spielen.
Mein ganzer Punkt hier ist, dass es schwierig ist, genau zu berechnen, wie viel Zeit dieser Code tatsächlich benötigt. Trial & Error-Benchmarking mit einem Scope ist wahrscheinlich eine sinnvollere Idee als theoretische Berechnungen.
quelle