Hallo erstmal, ich hab jetzt mit dem AVR-GCC angefangen, aber wenn ich mir so das Ergebnis anschaue, dann kommen mir Zweifel. Ich hab mir 2 Warteschleifen geschrieben, in der .lst-Datei sehen die dann so aus: void lcd_delay_5ms() { uint16_t i; /* t = i * loop / UC_CLOCK i = t / loop * UC_CLOCK the fastest way the compiler can do that loop takes 4 clocks: 2 clocks for decrementing and 2 clocks to branch take care that i does not exceed max(uint16_t) */ for (i=UC_CLOCK*0.005/4; i; i--) 1be: 80 e0 ldi r24, 0x00 ; 0 1c0: 94 e2 ldi r25, 0x24 ; 36 1c2: 42 97 sbiw r24, 0x12 ; 18 1c4: f1 f7 brne .-4 ; 0x1c2 ; } 1c6: 08 95 ret UC_CLOCK ist 7372800. r24 und r25 werden richtig geladen, aber in der nächsten Zeile wird 0x12 subtrahiert. inline void lcd_delay_50us() { uint8_t i; /* t = i * loop / UC_CLOCK i = t / loop * UC_CLOCK the fastest way the compiler can do that loop takes 3 clocks: 1 clocks for decrementing, 2 clocks to branch take care that i does not exceed max(uint8_t) */ for (i=UC_CLOCK*50e-6/3; i; i--) ; } dieser Block wird so übersetzt: 1fe: 8a e7 ldi r24, 0x7A ; 122 200: 82 50 subi r24, 0x02 ; 2 202: f1 f7 brne .-4 ; 0x200 r24 wird wieder richtig geladen, aber dann mit 2 decrementiert. Mach ich nur einen Denkfehler oder ist tatsächlich was faul? mfg werner
Warteschleifen werden gnadenlos wegoptimiert. Entweder die Zählvariable "volatile" deklarieren (siehe FAQ, 1. Punkt) oder in (inline) assembler schreiben -- für letzteres gibt es in #include <avr/delay.h> schon bißchen was Vorgefertigtes.
Nachtrag: Autsch Das Einfügen von volatile hatte unerwartete Auswirkungen, ich werd mir jetzt die vorgefertigten Routinen anschauen. Der Assembler Code is dadurch "etwas" länger geworden: lcd_delay_5ms() { 1be: cf 93 push r28 1c0: df 93 push r29 1c2: cd b7 in r28, 0x3d ; 61 1c4: de b7 in r29, 0x3e ; 62 1c6: 22 97 sbiw r28, 0x02 ; 2 1c8: 0f b6 in r0, 0x3f ; 63 1ca: f8 94 cli 1cc: de bf out 0x3e, r29 ; 62 1ce: 0f be out 0x3f, r0 ; 63 1d0: cd bf out 0x3d, r28 ; 61 volatile uint16_t i; /* t = i * loop / UC_CLOCK i = t / loop * UC_CLOCK the fastest way the compiler can do that loop takes 4 clocks: 2 clocks for decrementing and 2 clocks to branch take care that i does not exceed max(uint16_t) */ for (i=UC_CLOCK*0.005/4; i; i--) 1d2: 80 e0 ldi r24, 0x00 ; 0 1d4: 94 e2 ldi r25, 0x24 ; 36 1d6: 89 83 std Y+1, r24 ; 0x01 1d8: 9a 83 std Y+2, r25 ; 0x02 1da: 89 81 ldd r24, Y+1 ; 0x01 1dc: 9a 81 ldd r25, Y+2 ; 0x02 1de: 89 2b or r24, r25 1e0: 49 f0 breq .+18 ; 0x1f4 1e2: 89 81 ldd r24, Y+1 ; 0x01 1e4: 9a 81 ldd r25, Y+2 ; 0x02 1e6: 01 97 sbiw r24, 0x01 ; 1 1e8: 89 83 std Y+1, r24 ; 0x01 1ea: 9a 83 std Y+2, r25 ; 0x02 1ec: 89 81 ldd r24, Y+1 ; 0x01 1ee: 9a 81 ldd r25, Y+2 ; 0x02 1f0: 89 2b or r24, r25 1f2: b9 f7 brne .-18 ; 0x1e2 ; } 1f4: 22 96 adiw r28, 0x02 ; 2 1f6: 0f b6 in r0, 0x3f ; 63 1f8: f8 94 cli 1fa: de bf out 0x3e, r29 ; 62 1fc: 0f be out 0x3f, r0 ; 63 1fe: cd bf out 0x3d, r28 ; 61 200: df 91 pop r29 202: cf 91 pop r28 204: 08 95 ret mfg werner
Ja, logisch, volatile verbietet die Optimierung auf der genannten Variablen, d. h. es ist jedesmal ein Speicherzugriff durchzuführen. Ist vor allem gedacht, wenn man eine Variable als shared memory zwischen einer Interruptroutine und dem Hauptprogramm benutzen will, oder aber für memory mapped IO, bei dem eine Adresse nicht auf eine Speicherstelle sondern ein Peripherie-Steuerregister zeigt (gibt's ja auch beim AVR, und bei den extended IO registers des ATmega128 muß man das auch benutzen, die sind nicht mehr über IN oder OUT erreichbar).
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.