Forum: Mikrocontroller und Digitale Elektronik Warum ist das so? (USART)


von GoZu (Gast)


Lesenswert?

Hi, ich zeig euch gleich mal 2 Codevarianten:

Variante 1:

uint8_t temp;
temp = USART_check();
if(temp == 0x01)
{
PORTB = 0x00;
}


Variante 2:

if(USART_check() == 0x01)
{
PORTB = 0x00;
}


Hier noch die dazugehörige Funktion USART_check():

uint8_t USART_check(void)
{
  // Sind neue Daten im Buffer? 1 = ja ; 0 = nein
  uint8_t temp = (UCSRA & ( 1<<RXC));

  if(0b10000000 == temp)
  {
    return 1;      // Daten vorhanden
  }
  return 0;        // Keine Date vorhanden
}

So mein Problem ist jetzt, dass Variante 1 funktioniert und Variante 2 
nicht. (unter Funktionieren meine ich, dass man die if Anweisungen 
ausführt, also die LEDs an Portb leuchten) Allerdings ist das doch 
eigentlich das total gleiche oder? Nur benötigt man bei der 1ten Version 
eine Variable mehr... wenn man if(USART_check()) schreibt funktioniert 
es auch nicht.

von Johannes M. (johnny-m)


Lesenswert?

Lass erstmal die ganzen Vergleiche mit "==" weg, das ist schlechter Stil 
und kann in die Hose gehen! Also nicht
1
if(USART_check() == 0x01)
sondern einfach
1
if(USART_check())
...und in USART_check die überflüssige lokale Variable weglassen und 
einfach
1
if(UCSRA & ( 1<<RXC))
Die Vergleiche sind hier überflüssig, weil hier nicht auf Gleichheit 
überprüft werden muss, sondern nur, ob der betreffende Wert von Null 
verschieden ist oder nicht.

Warum jetzt konkret die erste Version nicht funktioniert, kann ich 
allerdings so auch nicht sagen.

Außerdem solltest Du die Möglichkeiten nutzen, die das Forum zur 
Formatierung von Code bietet...

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

1
uint8_t USART_check(void)
2
{
3
   // Sind neue Daten im Buffer?
4
   return (UCSRA & ( 1<<RXC));
5
}
würde es aber auch tun ;)

von Johannes M. (johnny-m)


Lesenswert?

Läubi Mail@laeubi.de wrote:
>
1
uint8_t USART_check(void)
2
> {
3
>    // Sind neue Daten im Buffer?
4
>    return (UCSRA & ( 1<<RXC));
5
> }
> würde es aber auch tun ;)
Jo, das wäre die (fast) absolute Minimal-Methode. Noch minimaler wird 
es, wenn man das ganze nicht als Funktion mit dem dazugehörigen Overhead 
implementiert, sondern die Abfrage direkt ins Programm an die 
betreffenden Stellen einbaut...

von Niemand (Gast)


Lesenswert?

Und die Antwort auf die Frage von GoZu (Gast) lautet ?

von Severino R. (severino)


Lesenswert?

@Johannes M.
überflüssig ok, aber nicht falsch. Aus purer Neugier würde ich auch der 
Sache nachgehen.

@GoZu
Du kannst ja, um das Problem einzugrenzen, versuchhalber die
1
uint8_t USART_check(void)
so abändern:
1
uint8_t USART_check(void)
2
{
3
    return 1;
4
}

Wenn dann noch immer ein Unterschied ist, dann liegt es wohl nicht an 
der Abfrage des USART.
Kannst Du das Ding im Debugger laufen lassen und temp abfragen?

Logisch ist es in jedem Fall nicht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Läubi Mail@laeubi.de wrote:
> :
> :
> würde es aber auch tun ;)


1
#define USART_check (UCSRA & ( 1<<RXC))
würde es auch tun ;-)

von Severino R. (severino)


Lesenswert?

Johannes M. wrote:
> Noch minimaler wird
> es, wenn man das ganze nicht als Funktion mit dem dazugehörigen Overhead
> implementiert, sondern die Abfrage direkt ins Programm an die
> betreffenden Stellen einbaut...

Darunter leidet aber die Portabilität des Codes. Vielleicht bringt man 
den Compiler dazu, die Funktion inline zu übersetzen.

von Karl-heinz S. (cletus)


Lesenswert?

Severino R. wrote:
> Johannes M. wrote:
>> Noch minimaler wird
>> es, wenn man das ganze nicht als Funktion mit dem dazugehörigen Overhead
>> implementiert, sondern die Abfrage direkt ins Programm an die
>> betreffenden Stellen einbaut...
>
> Darunter leidet aber die Portabilität des Codes. Vielleicht bringt man
> den Compiler dazu, die Funktion inline zu übersetzen.

Gängige Compiler (gcc) sollten das sowieso machen, wenn es Sinn macht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Und die Antwort auf die Frage von GoZu (Gast) lautet ?
Assembler-Listing ansehen  :-)

von Jean P. (fubu1000)


Lesenswert?

Hi,

>if(USART_check() == 0x01)
>{
>   PORTB = 0x00;
>}

Funktioniert nicht , da der return Wert nicht zugewiesen wird.
Gehen müsste:

unsigned char temp;
if((temp = USAT_check()))
{
   ...
}

Gruß

von Johannes M. (johnny-m)


Lesenswert?

Fabian Ostner wrote:
> Funktioniert nicht , da der return Wert nicht zugewiesen wird.
Das muss der auch nicht. USART_check ist vom Typ uint8_t und hat als 
Wert entweder 1 oder 0...

> unsigned char temp;
> if((temp = USAT_check()))
Das ist Unsinn.

von Johannes M. (johnny-m)


Lesenswert?

Severino R. wrote:
> @Johannes M.
> überflüssig ok, aber nicht falsch. Aus purer Neugier würde ich auch der
> Sache nachgehen.
Nicht direkt falsch, aber fehleranfällig. Wenn ich einen Wahrheitswert 
brauche, dann mache ich keine großen Verrenkungen mit irgendwelchen 
unnötigen Vergleichen.

Man sollte sich auch eine konsistente Schreibweise angewöhnen (und auch 
die Binärschreibweise, die noch zusätzlich die Gefahr mitbringt, dass 
man sich bei den 0en und 1en irgendwo verzählt, nach Möglichkeit nicht 
verwenden, auch wenn der Compiler sie zur Verfügung stellt).

von GoZu (Gast)


Lesenswert?

OK

Also der Code ist deshalb so komisch, weil ich jetzt schon ca. 1 Stunde 
daran herumteste. Hatte vorher natürlich auch die minimalvariante von

return (UCSRA & ( 1<<RXC));

Allerdings gibt das 0b10000000 zurück und nicht 0x01.... Das mit 
if(USART_check()) hatte ich natürlich auch vorher so, ist ja viel besser 
zu lesen so =)
Aber warum es immer noch nicht funktioniert weiß ich selbst nicht :/ 
Egal, ich lass es jetzt halt einmal unständlich und wenn ich mal wieder 
mehr Zeit habe, teste ich es genauer aus

von Johannes M. (johnny-m)


Lesenswert?

GoZu wrote:
> Aber warum es immer noch nicht funktioniert weiß ich selbst nicht :/
> Egal, ich lass es jetzt halt einmal unständlich und wenn ich mal wieder
> mehr Zeit habe, teste ich es genauer aus
Hast Du die anderen Varianten, die genannt wurden, wenigstens mal 
ausprobiert? Wie schon gesagt, es ist eigentlich unnötig, das als 
Funktion zu implementieren. Die Abfrage kann auch direkt in die 
if-Bedingung geschrieben werden. Und welchen Wert die Abfrage liefert, 
spielt überhaupt keine Rolle, wenn man eben die direkten Vergleiche mit 
einem bestimmten Wert weglässt und den Wert einfach als Wahrheitswert 
interpretiert.

Vielleicht ist Dir die Funktionsweise nicht ganz klar. Wenn man eine 
if-Abfrage macht, z.B.
1
if(Bedingung)
2
    Anweisung;
Dann wird Anweisung genau dann ausgeführt, wenn Bedingung wahr (also 
nach C-Standard nicht Null) ist. Wenn Du für Bedingung jetzt Dein 
"UCSRA & ( 1<<RXC)" einsetzt, dann ist es pupsegal, ob der Wert dieses 
Ausdrucks 1, 100, 1245.678 oder -65 ist, Anweisung wird trotzdem 
ausgeführt. Nur, wenn der Ausdruck exakt 0 ergibt, wird Anweisung 
nicht bearbeitet.

In Deinem Fall wird ein einzelnes Bit maskiert. Der Ausdruck UCSRA & 
(1<<RXC) kann also nur genau zwei Zustände annehmen, nämlich 0 (wenn das 
Bit nicht gesetzt ist) und (1 << RXC), was aufgelöst 0x80 oder 
0b10000000 entspricht. Dieser zweite Zustand ist ungleich 0, weshalb es 
nicht erforderlich ist, hier noch mal einen Vergleich zu machen.

Ich weiß auch, dass die Sache mit den Wahrheitswerten für Anfänger nicht 
ganz leicht zu verstehen ist, aber wenn man den Dreh einmal raus hat, 
merkt man schnell, was dieses Verständnis für Vorteile mitbringt.

von P. S. (Gast)


Lesenswert?

Johannes M. wrote:
> Lass erstmal die ganzen Vergleiche mit "==" weg, das ist schlechter Stil
> und kann in die Hose gehen! Also nicht
>
> if(USART_check() == 0x01)
>
> sondern einfach
>
> if(USART_check())
>

Wenn USART_check als bool definiert worden waere, wuerde ich dir recht 
geben. Ist es aber nicht. Also ist es voellig richtig mit dem 
definierten Wert zu vergleichen. Nicht umsonst gibt es die implizite 
Konvertierung in boolean z.B. in Java nicht...

von Johannes M. (johnny-m)


Lesenswert?

Peter Stegemann wrote:
> Wenn USART_check als bool definiert worden waere, wuerde ich dir recht
> geben. Ist es aber nicht. Also ist es voellig richtig mit dem
> definierten Wert zu vergleichen. Nicht umsonst gibt es die implizite
> Konvertierung in boolean z.B. in Java nicht...
Wir sind hier aber nicht in Java, sondern in C, und in C gibt es nunmal 
standardmäßig keinen Datentyp bool.

Und mit solchen Vergleichen kann man sich auch schnell mal ins Knie 
schießen und Fehler verursachen, nach denen man sich dann nen Wolf 
sucht. In diesem Zusammenhang ist die Tatsache, dass in C schlicht alles 
wahr ist, was nicht falsch (also 0) ist, vorteilhaft verwendbar.

von GoZu (Gast)


Lesenswert?

Ich hab ja eh schon 2 mal geschrieben, dass ich normalerweise es eh mit 
if(USART_check()) schreibe, allerdings habe ich es später geändert um es 
kurz zu testen. Ich programmiere schon länger C/C++ am PC, als nix mit 
Anfang, und die genaue Funktionsweise ist mir auch klar =)

Allerdings habe ich jetzt gerade nicht weiter Zeit um das genauer 
auszutesten, wenn ich es heute später am Abend austesten werde, werde 
ich meine Ergebnisse hier natürlich posten.

Danke schonmal an alle!

von P. S. (Gast)


Lesenswert?

Johannes M. wrote:
> Peter Stegemann wrote:
>> Wenn USART_check als bool definiert worden waere, wuerde ich dir recht
>> geben. Ist es aber nicht. Also ist es voellig richtig mit dem
>> definierten Wert zu vergleichen. Nicht umsonst gibt es die implizite
>> Konvertierung in boolean z.B. in Java nicht...
> Wir sind hier aber nicht in Java, sondern in C,

Ob C oder C++ kann man so nicht sehen und in C++ gibt es bool. Einer der 
Gruende, warum man auch fuer C heute den C++-Compiler benutzen kann und 
sollte.

> Und mit solchen Vergleichen kann man sich auch schnell mal ins Knie
> schießen und Fehler verursachen, nach denen man sich dann nen Wolf
> sucht.

Wann? Wenn eine Funktion, fuer die nur die Werte 0 und 1 definiert sind, 
andere Werte zurueckgegeben werden?

Viel eher schiesst man sich in den Fuss, wenn man sich auch implizite 
Konvertierungen verlaesst. Z.B., wenn als Fehler Werte <= 0 verwendet 
werden und der Entwickler aus gewohnheit wieder mal if( doSomething()) 
schreibt...

von Johannes M. (johnny-m)


Lesenswert?

Peter Stegemann wrote:
> Ob C oder C++ kann man so nicht sehen und in C++ gibt es bool. Einer der
> Gruende, warum man auch fuer C heute den C++-Compiler benutzen kann und
> sollte.
Anhand der Register- und Bitnamen handelt es sich vermutlich um einen 
AVR, und für AVRs gibt es afaik noch keine "vernünftigen" C++-Compiler.

>> Und mit solchen Vergleichen kann man sich auch schnell mal ins Knie
>> schießen und Fehler verursachen, nach denen man sich dann nen Wolf
>> sucht.
>
> Wann? Wenn eine Funktion, fuer die nur die Werte 0 und 1 definiert sind,
> andere Werte zurueckgegeben werden?
Es lässt sich aber in diesem Fall auch ohne Funktionen und irgendwelche 
Rückgabewerte lösen. Wenn der OP in dem einen Fall anstelle der 
Binärzahl wenigstens den Ausdruck mit dem Bitnamen hingeschrieben hätte, 
wäre das ja auch schon einen Schritt besser.

Und wenn es wirklich nur zwei Zustände gibt, von denen einer Null ist 
(und genau darum geht es hier), dann kann man sich den ganzen Rotz 
tatsächlich sparen. Das verwirrt nur.

> Viel eher schiesst man sich in den Fuss, wenn man sich auch implizite
> Konvertierungen verlaesst. Z.B., wenn als Fehler Werte <= 0 verwendet
> werden und der Entwickler aus gewohnheit wieder mal if( doSomething())
> schreibt...
Das ist ein ganz anderes Kapitel, und in dieser Hinsicht gebe ich Dir 
Recht. Es geht hier nicht um eine Konvertierung, sondern lediglich um 
eine Interpretation eines Wertes. In C wird eben nichts konvertiert, 
da es eben auch keinen Datentyp bool gibt, in den man konvertieren 
könnte.

von P. S. (Gast)


Lesenswert?

Johannes M. wrote:
> Peter Stegemann wrote:

>> Ob C oder C++ kann man so nicht sehen und in C++ gibt es bool. Einer der
>> Gruende, warum man auch fuer C heute den C++-Compiler benutzen kann und
>> sollte.
> Anhand der Register- und Bitnamen handelt es sich vermutlich um einen
> AVR, und für AVRs gibt es afaik noch keine "vernünftigen" C++-Compiler.

Bei gcc funktioniert zwar noch nicht alles, aber er ist durchaus 
vernuenftig - und bool gehoert zu den Teilen, die funktionieren. Von 
dem, was nicht funktioniert, vermisse ich nur virtuelle Methoden.

> Es lässt sich aber in diesem Fall auch ohne Funktionen und irgendwelche
> Rückgabewerte lösen. Wenn der OP in dem einen Fall anstelle der
> Binärzahl wenigstens den Ausdruck mit dem Bitnamen hingeschrieben hätte,
> wäre das ja auch schon einen Schritt besser.

Welcher Bitname? Es geht hier um eine Funktion, die 1 oder 0 zurueck 
gibt.

von Johannes M. (johnny-m)


Lesenswert?

Peter Stegemann wrote:
> Welcher Bitname? Es geht hier um eine Funktion, die 1 oder 0 zurueck
> gibt.
Es geht um eine Funktion, die man sich sparen kann! Und wenn man anstatt 
0b10000000 einfach (1 << RXC) hinschreibt, dann ist das auch schon ein 
Stück übersichtlicher...

Und selbst, wenn man die Funktion unbedingt haben und so schreiben will, 
dass sie tatsächlich 0 oder 1 zurückgibt, kann man den Vergleich mit 
"==" in der Funktion weglassen.

von Jean P. (fubu1000)


Lesenswert?

@Johannes :
> unsigned char temp;
> if((temp = USAT_check()))
>Das ist Unsinn.

Warum ?

von Karl H. (kbuchegg)


Lesenswert?

Fabian Ostner wrote:
> @Johannes :
>> unsigned char temp;
>> if((temp = USAT_check()))
>>Das ist Unsinn.
>
> Warum ?

Die Zuweisung an sich ist kein Unsinn, das kann man in C durchaus so 
machen, aber die Konklusio die du gezogen hast, das man das so machen 
muss ist Unsinn.

So eine Zuweisung macht man nur dann, wenn man den Wert von temp in 
weiterer Folge nochmal benötigt.

zb. sind Einleseschleifen oft so aufgebaut
1
   while( ( c = readchar() ) != EOF )
2
     mach_irgendwas_mit_c;

die Zuweisung an der Stelle hat einfach nur den Zweck, das Zeichen
gleichzeitig in einer Variablen zu speichern UND
mit EOF zu vergleichen.

Würde man das nicht so machen, dann wäre die (gleichwertige) Alternative
1
    c = readchar();
2
    while( c != EOF )
3
    {
4
      mach_irgendwas_mit_c;
5
      c = readchar();
6
    }

also deutlich länger UND der Aufruf der eigentlichen Lesefunktion muss 
dupliziert werden.

Auch
1
    do
2
    {
3
      c = readchar();
4
      if( c != EOF )
5
      { 
6
        mach_irgendwas_mit_c;
7
      }
8
    } while( c != EOF );

hat den Schönheitsfehler, dass hier die Abbruchbedingung doppelt 
aufgeführt werden muss.

Warum ist 'doppelt' schlecht?
Weil man Gefahr läuft, dass man bei einer ev. Änderung nur 1 Änderung 
macht und die andere Codestelle übersieht.

Eventuell könnte man noch
1
    while( 1 )
2
    {
3
      c = readchar();
4
      if( c == EOF )
5
        break;
6
7
      mach_irgendwas_mit_c;
8
    }

machen. Puristen werden sich jedoch an der vermeintlichen Endlosschleife 
stören bzw. daran, dass die Abbruchbedingung der Schleife mitten im 
Schleifenrumpf versteckt ist.

von Jean P. (fubu1000)


Lesenswert?

Jo,
mit machen muß war auch nit so gemeint. Das mit der Rückgabe ohne eine 
Zuweisung war mein Denkfehler.
Allerdings mache ich es grundsätzlich so (Angewohnheit), falls man mal 
mehr als 1 oder 0 zurückgeben will (z.B bei else if oder switch 
verweisen). Code bleibt eh der selbe.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Fabian Ostner wrote:
> Jo,
> mit machen muß war auch nit so gemeint. Das mit der Rückgabe ohne eine
> Zuweisung war mein Denkfehler.
> Allerdings mache ich es grundsätzlich so (Angewohnheit),

Was machst du grundsätzlich so?

Ich hoffe jetzt mal, du meinst damit, dass du den expliziten Vergleich 
weglässt und nicht dass du Funktionsergebnisse in Vergleichen in 
Variablen auffängst.

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.