Ich brauche eine programmierbare Pause mit möglichst hoher Präzision. Um dies zu erreichen, habe ich den folgenden GCC-Code:
void delay(unsigned char d){
volatile unsigned char i=d;
while(i>0) i--;
}
Was kompiliert wird zu:
1cc: 89 81 ldd r24, Y+1 ; 0x01
1ce: 81 50 subi r24, 0x01 ; 1
1d0: 89 83 std Y+1, r24 ; 0x01
1d2: 89 81 ldd r24, Y+1 ; 0x01
1d4: 81 11 cpse r24, r1
1d6: fa cf rjmp .-12 ; 0x1cc <__vector_1+0x2c>
(Ich zeige nur den Loop-Core-Code). Dies führt dazu, dass die Genauigkeit 7 Zyklen beträgt, was nicht sehr akzeptabel ist. Ich sehe jedoch, dass der Compiler seine Arbeit nicht so schnell wie möglich gemacht hat: Wenn die i
Variable r24-Register wäre, würde ich 3 Operationen sparen und der Code wäre fast doppelt so schnell.
Wie kann ich also dem Compiler mitteilen, dass ich diese Variable in einem Register haben möchte?
PS. Ich würde überlegen, mit programmierbarer Nummer auf nop
's zu pausieren. Aber ich kann mir nicht vorstellen, wie dies erreicht werden kann. AVR hat keine Anweisungen zum Verzweigen zur berechneten Adresse. Soweit ich weiß, ist der Stack in AVR nicht direkt zugänglich (wenn ich den erforderlichen Wert auf den Stack schieben und die ret
Anweisung ausführen könnte, um zur benötigten Programmadresse zu springen - es ist auch eine knifflige Aufgabe, aber es wäre zumindest beträchtlich).
UPDATE Nachdem ich volatile
zu register
Keyword gewechselt habe (wie in einer der Antworten beschrieben) habe ich folgenden Code:
14e: 81 50 subi r24, 0x01 ; 1
150: f1 f7 brne .-4 ; 0x14e <__vector_6+0x1c>
Also habe ich den Zyklus von 7 auf 2 Zyklen reduziert. Was viel besser ist, als ich erwarten konnte.
Deklarieren Sie Ihre Variable nicht als volatile
. Das zwingt den Compiler so ziemlich dazu, einen Speicherort zu verwenden und bei jeder Schleifeniteration darauf zuzugreifen. Verwenden Sie register
stattdessen.
Der herkömmliche Weg, um das zu erreichen, was Sie wollen, besteht darin, die Verzögerungs-Subroutine in der nativen Assemblersprache von Hand zu codieren. Fast alle Entwicklungs-Tool-Sets, die C-Compiler sind, haben die Fähigkeit, Assembler-Sprachmodule in den Build aufzunehmen.
Ein gängiger Trick erfahrener Programmierer besteht darin, zunächst die Verzögerungsroutine in C-Code zu erstellen, so wie Sie es getan haben. Wenn Sie sich dann den kompilierten Maschinencode ansehen, erhalten Sie eine Vorlage, wie Sie dieselbe Routine in Assembler-Code schreiben können, und können dann die kritischen Abschnitte optimieren. Der Vorteil dieses Ansatzes besteht darin, dass der Compiler Ihnen auf angenehme Weise "zeigt, wie" der Einstieg und der Ausstieg in die/aus der Unterroutine so eingerichtet werden, dass sie mit dem Aufruf aus dem Haupt-C-Code kompatibel sind.
David Tweed
volatile
? Das zwingt den Compiler so ziemlich dazu, einen Speicherort zu verwenden und bei jeder Schleifeniteration darauf zuzugreifen. Versuchen Sie esregister
stattdessen.Roman Matwejew
volatile
den Compiler daran gehindert, den Code "herauszuoptimieren", schien nichts zu tun. Würden Sie eine Antwort schreiben, die ich abstimmen und akzeptieren könnte?David