mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 2 Bytes (int) struct korrekt so?


Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ATmega8 / AVR Studio / C

ich habe eine "integer-Strukture" wie folgt initialisiert:



union
{
    int integer;
    struct
    {
    int SV0 :1;
    int SV1 :1;
    int SV2 :1;
    int SV3 :1;
    int SV4 :1;
    int SV5 :1;
    int SV6 :1;
    int SV7 :1;
    int SV8 :1;
    int SV9 :1;
    } bit;
} SentVector;

Und greife auf ihre Elemente so zu:

SentVector.integer = 0b1000000000000000;
SentVector.bit.SV2 = 1;

Der Compiler meldet nichts an dieser Stette, und die Fachmänner? Ist es 
alles ok?

Danke für Eure Meinung, Grüße

Michal

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michal schrieb:

> Der Compiler meldet nichts an dieser Stette, und die Fachmänner? Ist es
> alles ok?

Wie 'gesetzteskonform' willst du es wissen :-)

Ist schon ok.

( :-) Wobei ich mich frage, wie der Compiler eine 1-Bit Variable mit 
einem Vorzeichen realisiert )

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für die 1-bit-Werte würde ich unsigned int statt int nehmen.

Sonst wären die gültigen Werte genau genommen nur 0 und -1.

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, also besser uint? Mache gleich, danke!

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
unsigned int oder einfach unsigned

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

jetzt noch eine Frage:
wenn ich jetzt laut den einzelnen Bits dieser struct meine Portpins 
setzten will (leider geht es nicht Portweise sondern Hardware-angepasst) 
muss ich den Wahnsinn so treiben, oder habe ich eine einfachere 
Möglichkeit übersehen?

      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB1 ); else PORTB &= ~ ( 
1 << PB1 );
      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB2 ); else PORTB &= ~ ( 
1 << PB2 );
      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB3 ); else PORTB &= ~ ( 
1 << PB3 );
      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB4 ); else PORTB &= ~ ( 
1 << PB4 );
      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB5 ); else PORTB &= ~ ( 
1 << PB5 );
      if ( SentVector.bit.SV0 ) PORTC |= ( 1 << PC0 ); else PORTC &= ~ ( 
1 << PC0 );
      if ( SentVector.bit.SV0 ) PORTC |= ( 1 << PC1 ); else PORTC &= ~ ( 
1 << PC1 );
      if ( SentVector.bit.SV0 ) PORTC |= ( 1 << PC2 ); else PORTC &= ~ ( 
1 << PC2 );
      if ( SentVector.bit.SV0 ) PORTC |= ( 1 << PC3 ); else PORTC &= ~ ( 
1 << PC3 );
      if ( SentVector.bit.SV0 ) PORTC |= ( 1 << PC4 ); else PORTC &= ~ ( 
1 << PC4 );

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Entschuldige, natürlich sollte sein:

      if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB1 ); else PORTB &= ~ ( 
1 << PB1 );
      if ( SentVector.bit.SV1 ) PORTB |= ( 1 << PB2 ); else PORTB &= ~ ( 
1 << PB2 );
      if ( SentVector.bit.SV2 ) PORTB |= ( 1 << PB3 ); else PORTB &= ~ ( 
1 << PB3 );
      if ( SentVector.bit.SV3 ) PORTB |= ( 1 << PB4 ); else PORTB &= ~ ( 
1 << PB4 );
      if ( SentVector.bit.SV4 ) PORTB |= ( 1 << PB5 ); else PORTB &= ~ ( 
1 << PB5 );
      if ( SentVector.bit.SV5 ) PORTC |= ( 1 << PC0 ); else PORTC &= ~ ( 
1 << PC0 );
      if ( SentVector.bit.SV6 ) PORTC |= ( 1 << PC1 ); else PORTC &= ~ ( 
1 << PC1 );
      if ( SentVector.bit.SV7 ) PORTC |= ( 1 << PC2 ); else PORTC &= ~ ( 
1 << PC2 );
      if ( SentVector.bit.SV8 ) PORTC |= ( 1 << PC3 ); else PORTC &= ~ ( 
1 << PC3 );
      if ( SentVector.bit.SV9 ) PORTC |= ( 1 << PC4 ); else PORTC &= ~ ( 
1 << PC4 );

Autor: Krishna (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
 if ( SentVector.bit.SV0 ) {
   PORTB |= ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) | ( 1 << PB4 ) | ( 1 << PB4 );
   PORTC |= ( 1 << PC0 ) | ( 1 << PC1 ) | ( 1 << PC2 ) | ( 1 << PC3 ) | ( 1 << PC4 );
 } else {
   PORTB &= ~(( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) | ( 1 << PB4 ) | ( 1 << PB4 ));
   PORTC &= ~(( 1 << PC0 ) | ( 1 << PC1 ) | ( 1 << PC2 ) | ( 1 << PC3 ) | ( 1 << PC4 ));
 }

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(man darf seinen Quelltext hier auch gerne als C formatiert
reinkopieren, dann ist es auch lesbar...)

Das Thema taucht hier öfter auf.
Viele finden es so toll, mir gefällt es auch nicht.

Leider muß man zur Vermeidung etwas tricksen.

Die eine Variante ist, sich passende Makros zu definieren in der
Art SETBIT( PORTB, 1 ) o.s.ä..

Ich fände es eleganter, über den jeweiligen Port eine struct
mit Bitfeldern zu legen (ähnlich deiner obigen) und dann über
die Bitfelder die Bits anzufassen.

Jede Methode hat Vor- und Nachteile, und keine wird allen gefallen.
Richtig elegant geht es in C mit AVR leider nicht.

(Etwas schicker könnte man es in C++ machen, aber das ist
sicher auch nicht mehrheitsfähig.)

Autor: Krishna (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Entschuldige, natürlich sollte sein:

Dann hat sich obiges erledigt, und es geht ganz anders.

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Entschuldige Krishna,

bei dem ersten Copy und Paste war der Code noch nicht fertig.
Natürlich kriegt jeder Pin einen anderen Bt vom struct.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier bricht ganz einfach diese bitweise Betrachtung zusammen und ist 
umständlicher, als wenn man die Betrachtung anstellt:
 Hier ein Byte, dort 2 Empfägerbytes.
 Wie muss ich schieben und maskieren um die gewünschten Bits
 auf die Empfänger zu verteilen.

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, dann glaube ich doch dass meine Lösung doch übersichtlich ist und so 
bleibt...

Autor: Krishna (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.. Ih habe kürzlich auch gelernt, daß man bei einem Union nicht eine 
Variable schreiben, und anschliessend eine andere, überlappende, lesen 
sollte. Das ist nämlich undefiniert. Jedenfalls in C (ich liebe Pascal 
;-) ). Und es gibt tatsächlich derzeit einen BUG in AVR-GCC der 
verhindert, daß es unter allen Umständen funktioniert.
Kurz gesagt, falls du das vorhattest:  Vermeide es. Wirf die ganze Union 
weg, die brauchst du sowieso nicht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michal schrieb:
> Ok, dann glaube ich doch dass meine Lösung doch übersichtlich ist und so
> bleibt...

Und ich glaube, dass du den Aufwand scheust, dich in Bitmanipulationen, 
Schiebereien, Maskierungen etc. einzuarbeiten :-)
  tmp = PORTB & ~( 0x3E );
  tmp |= ( SentVector.integer & 0x1F ) << 1;
  PORTB = tmp;

  tmp = PORTC & ~( 0x1F );
  tmp |= ( SentVector.integer >> 5 ) & 0x1F;
  PORTC = tmp;

Das hat dann auch den Vorteil, dass zumindest die Pins an einem Port 
(nahezu) gleichzeitig neu gesetzt werden. Wenn an den Pins Hardware 
hängt, die auf Flanken reagiert, kann das wichtig sein.

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, geschoben und maskiert habe ich schon, dabei aber:
man kriegt schnell dieselbe Zeilenanzahl,
die aber nicht so regulär und leicht zu lesen sind wie diese 10 fast 
identische.
Und ich kann die Pins beim Device-Wechsel schneller anpassen.

@Krishna: komisch, in einem Paper auf Atmel-Website werden struct 
empfohlen!

Autor: Krishna (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
UNIONs auch ?
Für diesen Zweck ?

Egal, Fakt ist, es gibt diesbezüglich einen BUG AVR-GCC.
Ob der bei Deinem speziellem Fall zum Tragen kommen würde, weiß ich aber 
nicht.

Aber google mal nach "union" und undefiniertes Verhalten.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michal schrieb:

> Und ich kann die Pins beim Device-Wechsel schneller anpassen.

Gut, das ist ein Argument.
Aber dann schreibs wenigstens so
  PORTB &= ~( ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) | ( 1 << PB4 ) | ( 1 << PB5 ) );

  if ( SentVector.bit.SV0 ) PORTB |= ( 1 << PB1 );
  if ( SentVector.bit.SV1 ) PORTB |= ( 1 << PB2 );
  if ( SentVector.bit.SV2 ) PORTB |= ( 1 << PB3 );
  if ( SentVector.bit.SV3 ) PORTB |= ( 1 << PB4 );
  if ( SentVector.bit.SV4 ) PORTB |= ( 1 << PB5 );

  PORTC &= ~( ( 1 << PC0 ) | ( 1 << PC1 ) | ( 1 << PC2 ) | ( 1 << PC3 ) | ( 1 << PC4 ) );

  if ( SentVector.bit.SV5 ) PORTC |= ( 1 << PC0 );
  if ( SentVector.bit.SV6 ) PORTC |= ( 1 << PC1 );
  if ( SentVector.bit.SV7 ) PORTC |= ( 1 << PC2 );
  if ( SentVector.bit.SV8 ) PORTC |= ( 1 << PC3 );
  if ( SentVector.bit.SV9 ) PORTC |= ( 1 << PC4 );

Dann ist wenigstens nicht die ganze Performance beim Teufel

> @Krishna: komisch, in einem Paper auf Atmel-Website werden struct
> empfohlen!

Structs mag sein.
Aber unions haben laut Sprachstandard mit einer derartigen Verwendung 
meistens undefiniertes Verhalten (drum habe ich auch am Anfang gefragt 
"wie genau du es wissen willst" :-)

Autor: Krishna (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was man sich bei der struct-in-union-Geschichte bewusst machen
sollte, ist daß es nicht geregelt ist, in welcher Reihenfolge die
:1-Bits den Stellen in der int zugeordnet werden.
Das kann der Compiler machen wie er will, und insofern ist
das Programm nicht portabel.

Ich würde mich aber davon nicht irritieren lassen; der gcc
macht es in der Reihenfolge wie erwartet und portabel ist das
Programm sowieso nicht, wenn eine Zeile weiter auf AVR-Register
Bezug genommen wird.

In diesem Fall würde ich also nicht davon abraten.
Der Hinweis auf "undefiniertes Verhalten" ist aber berechtigt,
wenn man den Quelltext mal auf einem anderen System verwenden
will.

Autor: Michal (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uups...


na dann mein Vorschlag wie ich meine Portpins OHNE Unions und Structs 
von einer 16-Bits-Varlablen setzte und in eine 16-Bits-Variable einlese:


Setzen:

/c

unsigned SentVector;

PORTB &= ~( ( 1 << PB1 ) | ( 1 << PB2 ) | ( 1 << PB3 ) | ( 1 << PB4 ) | 
( 1 << PB5 ) );
if ( SentVector & 0b1000000000000000 ) PORTB |= ( 1 << PB1 );
if ( SentVector & 0b0100000000000000 ) PORTB |= ( 1 << PB2 );
...
...


Und einlesen:

/c
unsigned ResponseVector;

ResponseVector = 0b0000000000000000;
if ( PIND & ( 1 << PIND7 ) ) ResponseVector |= 0b1000000000000000;
if ( PIND & ( 1 << PIND6 ) ) ResponseVector |= 0b0100000000000000;
...
...

Bin ich so an der sicheren Seite?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michal schrieb:

> Bin ich so an der sicheren Seite?

Ja.
Mach dir ein paar Makros (inline Funktionen wären besser)

#define SET_BIT(where,which)    ((where) |= (1<<(which)))
#define CLR_BIT(where,which)    ((where) &= ~(1<<(which)))
#define IS_SET(where,which)     ((where) & (1<<(which)))

was anderes macht der Compiler auch nicht.
Auch der Compiler muss letztendes den Einzelbitzugriff mit Maskieren und 
Schieben implementieren

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.