Hallo,
mir ist etwas widerfahren, was ich mir beim besten Willen nicht
erklären kann. Ich programmiere den ATMega16 in avr-gcc. Einige
Zählvaraiablen (long int), die vorher mit i, k, usw. benannt waren,
habe ich umbenannt in Count1, Count2, ... um die Aussagekraft zu
erhöhen. Dabei ist mir ein Ersetzungsfehler unterlaufen:
...
for (Count1=0;Count1<6;Count1++)
{
LED[Count1] anmachen
// Verzögerungsschleife
for (Count2=0;Count2<15000;Count2++)
{
Delay++;
}
LED[Count1] ausmachen
}
...
while(1)
{
...
for (Count1=0;Count2<15000;Count2++)
...
}
Vor der Endlosschleife wird eine Kaskade von 6 LEDs über ein
Schieberegister einmal von links nach rechts zum Aufleuchten gebracht.
In der for-Schleife der Endlosschleife müsste richtigerweise
'Count2=0' statt 'Count1=0' stehen. Das wirkt sich in der
fehlerhaften Version so aus, dass der Aufleuchte-Vorgang der LEDs, der
vor der Endlosschleife steht, dann dreimal so lang dauert.
Wie kann es sein, dass ein logischer Fehler in einem sequentiellen
Programm sich auf eine vorher abgearbeitete Anweisung auswirkt?
Verletzt das nicht das Kausalitätsprinzip? Hat irgendjemand eine Idee?
Gruß Jörg
Ich vermute, dass Du nur den Optimizer strapazierst. Denn wenn
"Count2" und "Delay" nicht als "volatile" deklariert sind, dann
ist
for (Count2=0;Count2<15000;Count2++)
{
Delay++;
}
aus Sicht des Compilers funktionell identisch mit
Count2 = 15000;
Delay += 15000;
und je nach Kontext, Laune und Wasserstand kann es sein, dass der
Compiler das mal wegoptimiert, mal nicht.
Daher: Delay-Funktion der Library verwenden.
Siehe Doku der avr-libc, unter "Busy-wait delay loops".
Oder <avr/delay.h>
Timer nur nötig, wenn man zwischendurch besseres zu tun hat als einfach
nur Schleifchen zu drehen.
@jupp
Das Delay++ war dafür gedacht, dass die Schleife nicht wegoptimiert
wird, obwohl die Gefahr lt. A.K. ja trotzdem besteht.
@alle
Das scheint mir aber bei mir nicht der Fall zu sein. Denn der von mir
oben beschriebene schnellere Fall, zeigt die LEDs immer noch deutlich
länger als wenn nur die beiden Befehle:
Count2 = 15000;
Delay += 15000;
statt der Schleife ausgeführt werden.
Was höchstens sein könnte ist, dass beim schnelleren Fall die Schleife
zu:
for (Count2=0;Count2<15000;Count2++)
{
}
Delay+= 15000;
teiloptimiert wird
und im langsamen Fall unverändert als
for (Count2=0;Count2<15000;Count2++)
{
Delay++;
}
ausgeführt wird.
Die *.lst scheint mir die Assemblerdatei zu sein, die man diesbezüglich
untersuchen sollte, oder?
Gruß
Jörg
"Die *.lst scheint mir die Assemblerdatei zu sein, die man
diesbezüglich untersuchen sollte, oder?"
Ja.
Generell empfiehlt sich aber, erkennbar sinnlosen Code entweder ganz zu
vermeiden, oder dem Compiler unter Verwendung von "volatile" die
Optimierung abzugewöhnen. Alles andere ist ausgesprochen fragil.
In der lst-Datei finde ich die Zeile
ldi r24,lo8(14999)
Ich konnte dieses 'lo8' (und auch 'hi8', das auch vorkommt) weder
im Datenblatt des ATMega, noch im 'Instruction Set' von Atmel noch in
'io.h' oder 'iom16.h'. Kannst Du mir vielleicht sagen, was deren
Bedeutung ist und wo ich hätte suchen müssen?
Gruß Jörg
Hallo
das soll jetzt nicht in eine Assembler-Fragestunde ausarten, aber auf
Seite 120 des Atmel-Instruction-Set steht für den Befehl 'SBIW', dass
dort zwingend immer ein Register-Paar übergeben werden muss, als
Beispiel ist dort 'sbiw r25:r24,1' angegeben. In meinem lst-File
taucht aber die Zeile
sbiw r24,1
auf. Widerspricht dies nicht den obigen Angaben?
Gruß Jörg
Ja,
das ist mir dann auch aufgefallen. Trotzdem sollte diese Syntax doch in
irgendeinem Manual erklärt sein oder woher soll man von ihrer Existenz
wissen, wenn man sie nicht gerade aus bestehendem Code herausliest.
Zwar sind "+" oder "-" auch nicht erklärt, aber lo8 und hi8 haben
auch nicht diese Allgemeingültigkeit.
In einem DSP-Assembler-Dialekt mit dem ich auch mal gearbeitet habe,
gab es lo8 jedenfalls nicht.
Gruß Jörg