Hallo zusammen,
momentan sitze über einem Assemblercode, den mir der GCC (Version 4.6.1)
aus meinem C-Code generiert. Mich treibt es fast in den Wahnsinn, weil
ich den Fehler nicht finden kann, wieso der Compiler einen Assemblercode
generiert, der dazu führt, dass meine Funktion nicht ordnungsgemäß
funktionieren kann. Die Lösung für das Problem habe ich zwar gefunden,
es wäre aber für mich und möglicherweise auch für andere Nutzer
interessant, die Gründe für ein solches Compilerverhalten zu kennen.
Mein Problem bezieht sich auf die "while-Schleife", die in Version 1
nicht mehr im Assemblercode auftaucht, in Version 2 hingegen schon. Zum
besseren Verständnis habe ich jeweils die komplette Funktion (Start
einer AD-Wandlung und warten bis diese erfolgt ist) aus dem lss-File
kopiert.
Zur Zeit wird von mir das Optimierungslevel -O1 verwendet.
1. Die "nicht funktionierende" Version:
1
uint16_t makeConversion(uint8_t channel)
2
{
3
uint16_t adcResult;
4
5
ADMUX &= 0xE0; /* Clear channel selection */
6
298: 97 b1 in r25, 0x07 ; 7
7
29a: 90 7e andi r25, 0xE0 ; 224
8
29c: 97 b9 out 0x07, r25 ; 7
9
ADMUX |= channel; /* Set channel */
10
29e: 97 b1 in r25, 0x07 ; 7
11
2a0: 89 2b or r24, r25
12
2a2: 87 b9 out 0x07, r24 ; 7
13
14
ADCSRA |= 0x40; /* Start conversion */
15
2a4: 36 9a sbi 0x06, 6 ; 6
16
17
while((ADCSRA & 0x40) == 1)
18
2a6: 86 b1 in r24, 0x06 ; 6
19
{
20
21
}
22
23
adcResult = ADC; /* Get result */
24
2a8: 84 b1 in r24, 0x04 ; 4
25
2aa: 95 b1 in r25, 0x05 ; 5
26
27
28
return(adcResult);
29
}
30
2ac: 08 95 ret
2. Die funktionierende Version.
1
uint16_t makeConversion(uint8_t channel)
2
{
3
uint16_t adcResult;
4
5
ADMUX &= 0xE0; /* Clear channel selection */
6
298: 97 b1 in r25, 0x07 ; 7
7
29a: 90 7e andi r25, 0xE0 ; 224
8
29c: 97 b9 out 0x07, r25 ; 7
9
ADMUX |= channel; /* Set channel */
10
29e: 97 b1 in r25, 0x07 ; 7
11
2a0: 89 2b or r24, r25
12
2a2: 87 b9 out 0x07, r24 ; 7
13
14
ADCSRA |= 0x40; /* Start conversion */
15
2a4: 36 9a sbi 0x06, 6 ; 6
16
17
while((ADCSRA & 0x40) != 0)
18
2a6: 36 99 sbic 0x06, 6 ; 6
19
2a8: fe cf rjmp .-4 ; 0x2a6 <thm5adc_makeConversion+0xe>
20
{
21
22
}
23
24
adcResult = ADC; /* Get result */
25
2aa: 84 b1 in r24, 0x04 ; 4
26
2ac: 95 b1 in r25, 0x05 ; 5
27
28
29
return(adcResult);
30
}
31
2ae: 08 95 ret
Erläuterung zur Funktion:
Es wird eine AD-Wandlung angestoßen und in einer while-Schleife (zur
Übersichtlichkeit noch ohne timeout) gewartet, bis diese abgeschlossen
wurde. Laut Datenblatt wird das Bit ADSC immer dann von 1 auf 0 gesetzt,
wenn eine Wandlung abgeschlossen wurde. Aus diesem Grund warte ich genau
auf dieses Event in der while-Schleife. Es gibt natürlich zwei
Möglichkeiten, wie ich auf den Statuswechsel warten kann.
Möglichkeit 1: Warten, solange ADSC auf 1 steht (siehe Version 1)
Möglichkeit 2: Warten, solange ADSC nicht auf 0 steht (siehe Version 2)
Prinzipiell sollte doch daraus derselbe Assemblercode (bis auf die
Verwendung von SBIS anstelle von SBIC) entstehen. Wo liegt mein
Denkfehler?
Viele Grüße,
Philipp
Hallo Spess, hallo Peter,
ihr habt völlig recht.
Hatte absolut Tomaten auf den Augen. Aber manchmal sieht man eben vor
lauter Bäumen den Wald nicht mehr.
Vielen Dank an euch!
Philipp S. schrieb:> Hatte absolut Tomaten auf den Augen. Aber manchmal sieht man eben vor> lauter Bäumen den Wald nicht mehr.
Deswegen schreibt man sowas ja auch nicht.
> while((ADCSRA & 0x40) == 1)> while((ADCSRA & 0x40) != 0)
Sondern: while(ADCSRA & 0x40)
mfg.
Hallo Thomas,
da hast du völlig recht. Allerdings füge ich einen Timeout in die
While-Schleife ein. Und weil daher ein weiteres Abbruchkriterium
dazukommt, dachte ich, dass es übersichtlicher gewesen wäre.
Um diejenigen, die zufällig auf diesen Beitrag stoßen, nicht im Unklaren
über den von mir gemachten Fehler zu lassen:
1
while((ADCSRA&0x40)==1);//Wird NIEMALS erfüllt
2
3
while((ADCSRA&0x40)==0x40);//Würde funktionieren besser ist aber die Lösung von Thomas Eckmann
Jörg Wunsch schrieb:> Macht man aber nur, wenn man einen Beitrag beim IOCCC einreichen> möchte. :-)
Nö.
Es kann sein, daß das 0x40 nicht direkt im Text steht, sondern ein
Define irgendwo ist.
Und nicht ein While den Ausdruck auswertet, sondern er als Returnwert
von einer anderen Funktion irgendwo auswertet wird.
Dann ist das !! schon sinnvoll, um immer den Wert TRUE zu erzwingen,
egal welche Bitposition geprüft wird.
Philipp S. schrieb:> Um diejenigen, die zufällig auf diesen Beitrag stoßen, nicht im Unklaren> über den von mir gemachten Fehler zu lassen:
Man kann deinen Fehler so zusammenfassen:
ungleich 0
und gleich 1
sind nicht dasselbe
Wie man sich leicht klar machen kann. 2 ist zwar ungleich 0 (diese
Fragstellung würde man als mit wahr beantworten), aber 2 ist nicht
gleich 1 (diese Fragestellung würde man also mit falsch beantworten).
In C ist weniger oft mehr. Da im while bzw. im if die Bedingung
'ungleich 0' schon implizit mit drinnen steckt(*), lässt man als
Programmierer den am besten weg (wenn es nicht um eine extreme Betonung
dieses Sachverhalts geht). Alles was man weglassen kann, kann auch nicht
zu einem Tippfehler führen.
(*) dies ist so zu verstehen, dass while (oder auch if) sich nicht dafür
interessieren, dass in ihren Klammern ein logischer Ausdruck mit einem
boolschen Ergebnis steckt. Sie nehmen einfach den angegebenen Ausdruck,
werten ihn aus und bewerten dieses Ergebnis nach den Regeln der Sprache
für logische Ausdrücke.
D.h. bei
while( x )
geht es nicht um den konkreten Aufbau dieses x. Das muss kein Ausdruck
sein, in dem ein Vergleichsoperator vorkommt. Das ist in C nicht
gefordert (in anderen Sprachen ist das anders!). Aus Sicht des while ist
das einfach nur ein arithmetischer Ausdruck, dessen Ergebnis auf "0 oder
nicht 0" untersucht wird. Ist er nicht 0, dann wird die Schleife
(erneut) betreten.
In diesem Sinne ist
while( x != 0 )
eigentlich doppelt gemoppelt. Denn das Ergebnis des Ausdrucks
x != 0
ist seinerseits wieder eine Zahl: 0 wenn der Vergleich nicht zutrifft
und 1 wenn der Vergleich zutrifft. Wenn das while daher das Ergebnis
dieses Vergleichs prüft, dann stellt man fest, dass dieses Ergebnis
immer mit dem Ergebnis des explizit hingeschriebenen Vergleiches
übereinstimmt. D.h. diese Schreibweise mit dem expliziten != Vergleich
ist in allen Belangen identisch zu
while( x )
Nun kann es natürlich sein, dass man als Programmierer hier eine
spezielle Betonung auf den Umstand des 'ungleich 0' legen will und dann
schreibt man den Vergleich auch explizit an. Aber im Grunde ist er nicht
notwendig, so wie bei
i = 1 * k;
die Multiplikation mit 1 nicht notwendig ist. Und die schreibt man ja
auch nicht explizit an, es sei denn man will da eine Betonung drauf
legen.
Also keine Scheu
if( PINC & ( 1 << PB4 ) )
ist perfektes C, an dem es erst mal nichts auszusetzen gibt. Es ist
genauso logisch einwandfrei, wie
if( isCursorVisible() )
...
einwandfrei ist. Denn in dieser Situation würde wohl kaum jemand
if( isCursorVisible() != FALSE )
schreiben. Ganz im Gegenteil, würde wohl hier der zusätzliche Vergleich
die meisten Programmierer stutzig machen und sie würden sich fragen,
warum der da steht und ob es da wohl irgendwelche Sonderfälle gibt, die
sie nur einfach nicht durchschauen.
In diesem Sinn:
* Vergleichsoperatoren sind in C auch nichts anderes als arithmetisch
logische Operatoren. Sie bewerten ihr linkes Argument, bewerten ihr
rechtes Argument, führen die Aktion durch und haben ein numerisches
Ergebnis. Konzeptionell identisch zu den Operatoren +, -, *, /, |, ....
und wie sie sonst alle heißen.
* In C ist weniger oft mehr.
Philipp S. schrieb:> Und weil daher ein weiteres Abbruchkriterium> dazukommt, dachte ich, dass es übersichtlicher gewesen wäre.
Finde ich nicht.
while((ADCSRA & 0x40) & !timeout);
Peter Dannegger schrieb:> Das !! macht aus Wahr eine 1.
Nö.
Wahr, also true, ist immer 1. Das !! macht aus !0 eine 1.
Karl Heinz Buchegger schrieb:> ist perfektes C, an dem es erst mal nichts auszusetzen gibt.
MISRAtene Normungspopanze regen sich da aber schon drüber auf.
mfg.
Thomas Eckmann schrieb:>> ist perfektes C, an dem es erst mal nichts auszusetzen gibt.> MISRAtene Normungspopanze regen sich da aber schon drüber auf.
:-)
Zum Thema MISRA: Blödeln wir rum oder programmieren wir?
(Du kennst sicherlich meine Einstellung zu MISRA. :-) )
Thomas Eckmann schrieb:> while((ADCSRA & 0x40) & !timeout);
Oi oi oi. :-)
Peter Dannegger schrieb:> Dann ist das !! schon sinnvoll, um immer den Wert TRUE zu erzwingen,> egal welche Bitposition geprüft wird.
Da finde ich aber ((ADCSRA & 0x40) != 0) allemal besser verständlich.
Das erzwingt auch den Wert 1, unabhängig von der Bitposition, und
man kann es lesen, ohne erst drüber nachdenken zu müssen. ;-)