Forum: Compiler & IDEs Was übersehe ich hier? If mit/ohne temporäre Variable


von Michael G. (mjgraf)


Lesenswert?

Hallo zusammen,

ich fange langsam an, an mir selbst zu zweifeln... In meiner 
Hauptschleife habe ich die Abfrage
1
if (serialReceivedDataWaiting()) {serialPutC (serialGetC ());}

(Ein kleines Echo zum Testen des USARTs beim ATmega 8, eigentlich nichts 
dolles...). serialReceivedDataWaiting() ist definiert als
1
 unsigned char serialReceivedDataWaiting () {
2
  return (rxBufferNextPut != rxBufferNextGet);
3
}

rxBufferNextPut und rxBufferNextGet sind Indizes in einen Ringpuffer.

Bevor serialReceivedDataWaiting () das erste mal eine 1 zurücmkgibt, 
funktioniert alles wie erwartet. War das Ergebnis jedoch einmal 1, wird 
der then-Block bei jedem Durchlauf ausgeführt. Behelfe ich mir jedoch 
mit einer temporären Variable
1
unsigned char temp2;
2
[...]
3
temp2 = serialReceivedDataWaiting();
4
if (temp2) {serialPutC (serialGetC ());}

funktioniert alles, wie es soll. Was übersehe ich hier?

Gruß & Dank,
Michael

von Olga (Gast)


Lesenswert?

Mit den kleinen Codeschnipseln wird es schwierig dir zu helfen. Zeig mal 
den ganze Code. In die Autowerkstatt geht man ja auch mit dem ganzen 
Wagen, und nicht nur mit dem Zündschlüssel.

von Michael G. (mjgraf)


Angehängte Dateien:

Lesenswert?

Wenn's hilft... mjgSerial.c bedient die serielle Schnittstelle, 
AVRGCC2.c ist der Testtreiber.

von Lutz H. (luhe)


Lesenswert?

versuche es mal mit if (Bedingung)

also in der Form


zahl = serialReceivedDataWaiting();
if (zahl<3) { ;};

Dann wird klarer was passiert, vor allem wenn das Funktionsergebnis 
vorher in eine Variable abgespeichert wird, kann besser der 
Programmablauf verfolgt werden.

von Michael G. (mjgraf)


Lesenswert?

Ähm... Wenn das Funktionsergebnis vorher in einer Variable abgespeichert 
wird, funktioniert es, wenn nicht, nicht. Das schrieb ich oben...

Dieses Verhalten tritt bei -O0 genauso auf wie bei -Os, ist also kein 
Optimierungsartefakt. In meiner Verzweiflung habe ich tatsächlich sogar 
mal mit
1
 if (serialReceivedDataWaiting() != 0)

probiert, dies macht keinen Unterchied (und sollte es ja auch nicht).

von (prx) A. K. (prx)


Lesenswert?

Hat nichts mit deinem Problem zu tun: Der Test auf die race condition in 
serialPutC funktioniert so nicht. Wenn UDR mit cli leer läuft, der 
Puffer aber nicht leer ist, dann sendest du in verkehrter Reihenfolge.

von Amateur (Gast)


Lesenswert?

Rein von der Logik her müsste es eigentlich gehen.
Möglicherweise solltest Du mal mit der Optimierung "spielen". Vielleicht 
haut die irgendwas raus.
Alternativ kannst Du auch eine .lss Datei erzeugen lassen und Dir die 
Stelle, die spinnt, aus der Nähe anschauen.
Das Wegoptimieren von: serialGetC () wäre so ein Kandidat. Dann geht’s 
dauernd rund.

von Michael G. (mjgraf)


Lesenswert?

A. K. schrieb:
> Hat nichts mit deinem Problem zu tun: Der Test auf die race condition in
> serialPutC funktioniert so nicht. Wenn UDR mit cli leer läuft, der
> Puffer aber nicht leer ist, dann sendest du in verkehrter Reihenfolge.

Stimmt, danke!
1
if (  (UCSRA & (1<<UDRE))                   // USART data register empty
2
   && (txBufferNextPut == txBufferNextGet)) // queue empty

müsste dann aber funktionieren - oder?

Gruß & Dank,
Michael

von (prx) A. K. (prx)


Lesenswert?

Michael Graf schrieb:
> müsste dann aber funktionieren - oder?

Spar dir den Zirkus mit der Optimierung darin, schreib den Kram stets in 
den Puffer und schalte den Interrupt ein. cli/sei ist dann völlig 
überflüssig. Worst case ist ein unnötiger Interrupt.

von Michael G. (mjgraf)


Angehängte Dateien:

Lesenswert?

Amateur schrieb:

> Alternativ kannst Du auch eine .lss Datei erzeugen lassen und Dir die
> Stelle, die spinnt, aus der Nähe anschauen.

Die wichtigen stellen in der LSS-Datei sehen so aus:

 288:  3b d1         rcall  .+630      ; 0x500 
<serialReceivedDataWaiting>
 28a:  00 97         sbiw  r24, 0x00  ; 0
 28c:  09 f4         brne  .+2        ; 0x290 <main+0x232>
 28e:  7f cf         rjmp  .-258      ; 0x18e <main+0x130>
 290:  13 d1         rcall  .+550      ; 0x4b8 <serialGetC>
 292:  23 d0         rcall  .+70       ; 0x2da <serialPutC>
 294:  7d cf         rjmp  .-262      ; 0x190 <main+0x132>
    }

00000500 <serialReceivedDataWaiting>:
 500:  df 93         push  r29
 502:  cf 93         push  r28
 504:  cd b7         in  r28, 0x3d  ; 61
 506:  de b7         in  r29, 0x3e  ; 62
 508:  20 91 76 00   lds  r18, 0x0076
 50c:  90 91 77 00   lds  r25, 0x0077
 510:  81 e0         ldi  r24, 0x01  ; 1
 512:  29 17         cp  r18, r25
 514:  09 f4         brne  .+2        ; 0x518 
<serialReceivedDataWaiting+0x18>
 516:  80 e0         ldi  r24, 0x00  ; 0
 518:  cf 91         pop  r28
 51a:  df 91         pop  r29
 51c:  08 95         ret


Blöderweise habe ich in Assembler das letzte Mal vor 20+ Jahren auf 
einem C-64 programmiert. Zum groben Lesen reicht's noch, aber den Fehler 
kann ich hier nicht entdecken.

Anyone?

> Das Wegoptimieren von: serialGetC () wäre so ein Kandidat. Dann geht’s
> dauernd rund.

Inwiefern?

von Michael G. (mjgraf)


Lesenswert?

A. K. schrieb:

> Spar dir den Zirkus mit der Optimierung darin, schreib den Kram stets in
> den Puffer und schalte den Interrupt ein. cli/sei ist dann völlig
> überflüssig. Worst case ist ein unnötiger Interrupt.

Hast Recht. Als ich den Mechanismus eingebaut habe, dachte ich noch, der 
UDRE-Interrupt würde durch den Übergang von 0 nach 1 ausgelöst, nicht 
davon, dass UDRE 1 ist, so dass ich das erste Byte immer manuell 
schreiben müsste... Danach habe ich ihn nicht mehr ausgebaut.

von Amateur (Gast)


Lesenswert?

> Das Wegoptimieren von: serialGetC () wäre so ein Kandidat. Dann geht’s
> dauernd rund.

Ich habe mir den Sourcecode noch nicht reingezogen, aber zu einem 
Empfang gehört auch das Löschen (wie auch immer) des abgeholten 
Zeichens.
Sagt die Routine serialReceivedDataWaiting(): jemand ist zuhause - so 
lange das nicht der Fall ist, geht's ja gut und erfolgt ein Aufruf von: 
serialPutC () ohne das eingefügte serialGetC (), so wird auch der 
Empfangspuffer nicht aktualisiert und die Abfrage liefert somit auch 
beim nächsten Durchlauf true. Usw.

von Lutz H. (luhe)


Lesenswert?

Hier wird die Funktion   serialReceivedDataWaiting() 2x aufgerufen
    temp2 = serialReceivedDataWaiting();
//    serialPutC (temp2+0x30);
    if (serialReceivedDataWaiting()




Möglicherweise sind beim 2. mal die Daten schon als nicht mehr als 
Waiting
registriert, weil keine neuen Daten empfangen wurden, und angenommen 
wird das die Daten abgeholt werden, wenn die Funktion 
serialReceivedDataWaiting() aufgerufen wird ? Deshalb liefert diese ein 
anderes Ergebnis.

Kenne leider die Funktionsbeschreibung nicht.

von Michael G. (mjgraf)


Lesenswert?

lutz h. schrieb:
> Hier wird die Funktion   serialReceivedDataWaiting() 2x aufgerufen

> Möglicherweise sind beim 2. mal die Daten schon als nicht mehr als
> Waiting
> registriert, weil keine neuen Daten empfangen wurden, und angenommen
> wird das die Daten abgeholt werden, wenn die Funktion
> serialReceivedDataWaiting() aufgerufen wird ? Deshalb liefert diese ein
> anderes Ergebnis.
>
> Kenne leider die Funktionsbeschreibung nicht.

Die steht im ersten Post (und später im angehängten Quelltext). 
serialReceivedDataWaiting() greift nur lesend auf die Indizes zu.

von (prx) A. K. (prx)


Lesenswert?

Hast du mal die Warnungen eingeschaltet oder gar gelesen? Mir fehlt in 
AVRGCC2.c ein #include "mjgSerial.h" oder so, mit der Deklaration der 
entsprechenden Funktionen. Weshalb der Return-Typ von 
serialReceivedDataWaiting in alter K&R-Tradition als int angenommen 
wird.

Ist er aber nicht, und so steht in der oberen Hälfte des vom Aufrufer 
angenommenen 16-Bit Werts zufällig einer der Indizes drin. Und der ist 
nach dem ersten Byte erst einmal ein Weilchen != 0.

von Michael G. (mjgraf)


Lesenswert?

A. K. schrieb:

Danke, das war's!

> Hast du mal die Warnungen eingeschaltet oder gar gelesen?

Doch, aber ich hatte die "implicit declaration" nur für einen 
Schönheitsfehler gehalten, und angenommen, dass wenn der Linker die 
Funktion findet, es schon irgendwie passen wird.

> Weshalb der Return-Typ von
> serialReceivedDataWaiting in alter K&R-Tradition als int angenommen
> wird.

> Ist er aber nicht, und so steht in der oberen Hälfte des vom Aufrufer
> angenommenen 16-Bit Werts zufällig einer der Indizes drin. Und der ist
> nach dem ersten Byte erst einmal ein Weilchen != 0.

Danke, dass Du mir das Brett vom Kopf genommen hast. Rückblickend hätte 
es mir klar sein sollen...

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
Noch kein Account? Hier anmelden.