Forum: Mikrocontroller und Digitale Elektronik BITs Bytes struct


von c beginner (Gast)


Lesenswert?

Hallo zusammen,

folgende Aufgabe würde ich gerne lösen aber ich komme nicht auf die 
Lösung. Ich möchte einen "uint8_t" BITweise befüllen. Dazu habe ich mir 
BIT Felder angesehen wie im Tutorial beschrieben.
1
    struct {
2
        uint8_t RS:1;
3
        uint8_t RW:1;
4
        uint8_t ENABLE:1;
5
        uint8_t LedBacklight:1;
6
        uint8_t DATA_4:1;
7
        uint8_t DATA_5:1;
8
        uint8_t DATA_6:1;
9
        uint8_t DATA_7:1;
10
    } I2C_Data;
11
12
    I2C_Data.RS = 1;
13
    I2C_Data.RW = 0;
14
    I2C_Data.DATA_7 = 1;

I2C_Data hat nun auch den Wert 81H aber ist nicht vom Typ uint8_t. Will 
ich also damit weiterarbeiten dann scheint es ein int zu sein. Frage: 
Wie kann ich das so hinbekommen das I2C_Data vom Typ uint8_t ist ?

2te Frage, wie kann ich I2C_Data direkt befüllen ? eine Zuweisung wie...
I2C_Data = 0xFF;
ist ja nicht zulässig. Ich kann hier nur BITweise befüllen. Gibt es eine 
Möglichkeit auch noch direkt auf I2C_Data eine Zuweisung zu machen ?

Ich hoffe man versteht die Idee aber ich kann es nicht besser 
formulieren.

von Einer K. (Gast)


Lesenswert?

union ? ! ?

von c beginner (Gast)


Lesenswert?

>> union ? ! ?
Ist das eine Antwort oder Frage ? Lese mich da gerade ein aber ein 
Beispiel wäre da hilfreich.

von Cyblord -. (cyblord)


Lesenswert?

Arduino Fanboy D. schrieb:
> union ? ! ?

Nein. Eine Union ist eben gerade NICHT zur Typumwandlung gedacht. Dafür 
nimmt man einfach einen Cast.
Eine Union soll verschiedenen Datentypen ermöglichen NACHEINANDER 
denselben Speicher zu belegen.

von Cyblord -. (cyblord)


Lesenswert?

1
 I2C_Data.RS = 1;
Erst mal muss man eine Variable vom Typ des Structs erzeugen. So wie du 
das machst ist das natürlich falsch.
Wäre Äquivalent zu int=8. Macht keinen Sinn.
struct I2C_Data ist der Datentyp und noch keine Variable.

Mach I2C_Data mal zum Typen mit typedef. Macht alles leichter.
1
I2C_Data i2c;
2
3
i2c.RS=1;
4
5
uint8_t x=(uint8_t)i2c;

von Einer K. (Gast)


Lesenswert?

zum Glück sagt die AVR-Gcc Doku, dass dieses unerlaubte Verfahren 
funktioniert.

Andere Kompiler können sich anders verhalten.
1
    struct I2CData{
2
        uint8_t RS:1;
3
        uint8_t RW:1;
4
        uint8_t ENABLE:1;
5
        uint8_t LedBacklight:1;
6
        uint8_t DATA_4:1;
7
        uint8_t DATA_5:1;
8
        uint8_t DATA_6:1;
9
        uint8_t DATA_7:1;
10
    };
11
12
    
13
    union Datensatz
14
    {
15
      I2CData asI2cData;
16
      uint8_t asByte;
17
    }datensatz;
18
19
   
20
    datensatz.asI2cData.RS = 1;
21
    datensatz.asI2cData.RW = 0;
22
    datensatz.asI2cData.DATA_7 = 1;
23
    datensatz.asByte = 0x42;

von Cyblord -. (cyblord)


Lesenswert?

Arduino Fanboy D. schrieb:
> zum Glück sagt die AVR-Gcc Doku, dass dieses unerlaubte Verfahren
> funktioniert.

Ich weiß. Aber man sollte sich sowas erst gar nicht angewöhnen.

von c beginner (Gast)


Lesenswert?

>> zum Glück sagt die AVR-Gcc Doku, dass dieses unerlaubte Verfahre
>> funktioniert.

Bei mir leider nicht, da hagelt es Fehlermeldungen.
request for member 'RW' in something not a structure or union
und so weiter.

@ Cyblord, kannst du deinen Ansatz mal so formulieren das ich damit was 
anfangen kann ? wie muß das mit dem typedef aussehen ? vielen Dank

von zitter_ned_aso (Gast)


Lesenswert?

c beginner schrieb:
> Bei mir leider nicht

weil da ein "struct" vor I2CData fehlt. Aber es ist ja nur die Syntax. 
Die Idee ist doch super.


Arduino Fanboy D. schrieb:
> union Datensatz
>     {
>      struct I2CData asI2cData;
>       uint8_t asByte;
>     }datensatz;

von Niklas Gürtler (Gast)


Lesenswert?

Hier sind die diversen Tücken und Lösungen dieses Standardproblems 
erläutert: Serialisierung

von Einer K. (Gast)


Lesenswert?

c beginner schrieb:
> Bei mir leider nicht, da hagelt es Fehlermeldungen.

Habs mit C++ getestet.
Für C muss man das ein winziges bisschen umbauen.(typedef)
Das Prinzip kann erhalten bleiben.

von c beginner (Gast)


Lesenswert?

@ zitter_ned_aso, danke für den Hinweis. Damit funkioniert die Version 
von Arduino Fanboy D schon einmal. Auch dir ein Danke.

Ich will den Hinweis von Cyblord dennoch ernst nehmen und würde mich 
über ein portierbares (C konformes) Beispiel freuen. Nochmals vielen 
Dank an Alle.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

c beginner schrieb:
> würde mich
> über ein portierbares (C konformes) Beispiel freuen.
1
i2cdata = 0x81; /* set rs=1; data_7=1; others=0 */

Muss nicht immer alles komplett overengineered werden und auf zig 
headerfiles und definitionen verteilt sein.

Gruss
WK

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dergute W. schrieb:
> Muss nicht immer alles komplett overengineered werden

Daß das die schlechtestmögliche Lösung ist, ist hinreichend bekannt und 
ausdiskutiert worden. "Magic Numbers" im Code gelten außerhalb Deines 
spezifischen persönlichen Standards als fehleranfällig und schlecht 
wartbar.

Dein Argument, man müsse ja eh' im Datenblatt nachsehen, macht das jetzt 
nicht besser.

von Cyblord -. (cyblord)


Lesenswert?

c beginner schrieb:
> Ich will den Hinweis von Cyblord dennoch ernst nehmen und würde mich
> über ein portierbares (C konformes) Beispiel freuen. Nochmals vielen
> Dank an Alle.

Ich hab dir praktisch den kompletten Code hingeschrieben. Was willst du 
noch?

Wenn man structs ohne typedef verwendet, muss man immer (u.a.) immer das 
Schlüsselwort struct voranstellen. Das habe ich mir geschenkt.

Das Prinzip sollte aber wohl klar sein, zusammen mit meiner Erklärung: 
Cast statt Union.
Wenn du nicht weißt was ein Cast ist, schlags nach!

von Eric B. (beric)


Lesenswert?

Rufus Τ. F. schrieb:
> Dergute W. schrieb:
>> Muss nicht immer alles komplett overengineered werden
>
> Daß das die schlechtestmögliche Lösung ist, ist hinreichend bekannt und
> ausdiskutiert worden. "Magic Numbers" im Code gelten außerhalb Deines
> spezifischen persönlichen Standards als fehleranfällig und schlecht
> wartbar.

Abgesehen von den Magic Numbers, die man, je nach Sprachvariante, in 
einem #define oder constexpr packen kann, ist die Lösung einfach und 
portabel und stützt nicht auf Undefined Behaviour, wie das die Lösungen 
mit structs und unions tun.
1
#define I2C_RS  0x01
2
#define I2C_RW  0x02
3
#define I2C_ENA 0x04
4
#define I2C_LED 0x08
5
#define I2C_DATA(x) ((x) & 0x0F) << 4)
6
7
i2c_data = I2C_RS | I2C_DATA(8);

von Tüt (Gast)


Lesenswert?

Klammerfehler, richtig:
1
#define I2C_DATA(x)   (((x) & 0x0F) << 4)

von Tüt (Gast)


Lesenswert?

#define I2C_DATA(x)   (x << 4)

ist einfacher zu lesen

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Tüt schrieb:
> ist einfacher zu lesen

aber je nach Kontext im Vergleich zu Deiner vorherigen Variante falsch.

von c beginner (Gast)


Lesenswert?

Nochmals meinen Dank für die Rückmeldungen. Es geht mir hier gewiss 
nicht um Abstraktion aus langeweile. Die Idee ist hier natürlich 
lesbaren Code zu schreiben und hier auch nochmal der Hinweis das ich 
gelegentlich C Programme schreibe aber das ist am Ende immer noch ein 
Hobby.

Wie in der Struktur hoffentlich zu erkennen war ging es darum ein Byte 
mittels I2C Bus zu übertragen. Dieses Byte sollte BITweise beschrieben 
werden können um dann anschließend als Byte an den I2C Layer übergeben 
zu werden.

Arduino Fanboy D hat das direkt erkannt und ich habe seine Idee auch so 
übernommen.
1
struct I2CData {
2
    uint8_t RS:1;
3
    uint8_t RW:1;
4
    uint8_t ENABLE:1;
5
    uint8_t Backlight:1;
6
    uint8_t DATA_4:1;
7
    uint8_t DATA_5:1;
8
    uint8_t DATA_6:1;
9
    uint8_t DATA_7:1;
10
};
11
12
union I2cDataSet {
13
    struct I2CData BIT;
14
    uint8_t asByte;
15
} I2cData;

Dank dieser Struktur kann man nun den Code, wie folgt, schreiben.
1
/* ---- Display Ausgabe Steuersignale ------------------------------------------------------------------- */
2
/*                                                                                                        */
3
void WriteCommandToLCD (uint8_t displayCommand)
4
{
5
    I2cData.BIT.RS = 0;                                             // Steuersignal ausgeben (RS BIT=0)
6
    I2cData.BIT.RW = 0;
7
    LCDputC (displayCommand);
8
    _delay_ms (5);
9
    I2cData.BIT.RS = 1;                                             // Zurückstellen auf Datenausgabe
10
}
11
12
/* ---- Display Ausgabe Zeichen ------------------------------------------------------------------------- */
13
/*                                                                                                        */
14
void LCDputC (uint8_t displayData)
15
{
16
    uint8_t n=2;                                                    // Sende 2x 4 BIT (oberes Nibble zuerst)
17
18
    do  {                                                           // oberes Nibble, Display Daten anordnen
19
        if (displayData & (1 << 4)) I2cData.BIT.DATA_4 = 1;
20
        else I2cData.BIT.DATA_4 = 0;
21
        if (displayData & (1 << 5)) I2cData.BIT.DATA_5 = 1;
22
        else I2cData.BIT.DATA_5 = 0;
23
        if (displayData & (1 << 6)) I2cData.BIT.DATA_6 = 1;
24
        else I2cData.BIT.DATA_6 = 0;
25
        if (displayData & (1 << 7)) I2cData.BIT.DATA_7 = 1;
26
        else I2cData.BIT.DATA_7 = 0;
27
28
        I2cData.BIT.RW = 0;
29
30
        I2cData.BIT.ENABLE = 1;
31
        I2cStart (PCF8574_Write_Adr);                               // I2C Device Adresse PCF 8574
32
        I2cWrite (I2cData.asByte);                                  // Daten an PCF 8574 senden
33
        I2cStop ();
34
35
        I2cData.BIT.ENABLE = 0;
36
        I2cStart (PCF8574_Write_Adr);                               // I2C Device Adresse PCF 8574
37
        I2cWrite (I2cData.asByte);                                  // Daten an PCF 8574 senden
38
        I2cStop ();
39
40
        displayData <<= 4;
41
    } while (--n);
42
    _delay_us (42);
43
}
Aus meiner Sicht wird der Code gut lesbar. Es hat den Vorteil das man 
nun mit einzelnen BITs (Steuerbits für das Display) die Logikpegel 
eindeutig steuern kann. Die Display Ansteuerung ist somit vom I2C Layer 
entkoppelt und das I2C Byte wird an den I2C Layer übergeben.

Folgendes würde ich gerne noch verstehen:
1. Wo findet man so einen Konstrukt in der GCC Doku ?
2. Was ist an dieser Kombi Struct / Union nicht C konform und warum ?
3. Wie kann man bei gleicher Schreibweise des Codes diese Strukur C 
konform anders formulieren ?

Im Prinzip ist die Sache aber für mich derzeit gelöst und ich bedanke 
mich nochmal hier besonders bei Arduino Fanboy D.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

c beginner schrieb:
> 1. Wo findet man so einen Konstrukt in der GCC Doku ?

Das findet man im C Standard.

c beginner schrieb:
> 2. Was ist an dieser Kombi Struct / Union nicht C konform und warum ?

Da das Ergebnis stark von der Plattform abhängt, ist es nicht portabel. 
C definiert es daher als "implementation defined", d.h. du kannst dich 
nicht auf das Ergebnis verlassen. Mit einem anderen Compiler kann etwas 
anderes herauskommen. In C++ ist es ganz verboten. "union" ist nur zum 
Speicher sparen da, nicht zum Daten konvertieren - man darf nicht einen 
Member beschreiben und dann einen anderen lesen.

c beginner schrieb:
> 3. Wie kann man bei gleicher Schreibweise des Codes diese Strukur C
> konform anders formulieren ?

Gleiche Schreibweise geht in C nicht. Korrekte Schreibweisen wurden 
genannt. Mithilfe von Bibliotheken lässt sich in C++ eine ähnliche, aber 
korrekte Schreibweise erreichen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ich hätte da noch einen Änderungsvorschlag:
1
struct I2CData {
2
    uint8_t RS:1;
3
    uint8_t RW:1;
4
    uint8_t ENABLE:1;
5
    uint8_t Backlight:1;
6
    uint8_t DATA:4;
7
};
8
9
union I2cDataSet {
10
    struct I2CData BIT;
11
    uint8_t asByte;
12
} I2cData;

und
1
void LCDputC (uint8_t displayData)
2
{
3
    uint8_t n=2;                                                    // Sende 2x 4 BIT (oberes Nibble zuerst)
4
5
    do  {                                                           // oberes Nibble, Display Daten anordnen
6
        I2cData.BIT.DATA = displayData >> 4;
7
8
        I2cData.BIT.RW = 0;
9
10
        I2cData.BIT.ENABLE = 1;
11
        I2cStart (PCF8574_Write_Adr);                               // I2C Device Adresse PCF 8574
12
        I2cWrite (I2cData.asByte);                                  // Daten an PCF 8574 senden
13
        I2cStop ();
14
15
        I2cData.BIT.ENABLE = 0;
16
        I2cStart (PCF8574_Write_Adr);                               // I2C Device Adresse PCF 8574
17
        I2cWrite (I2cData.asByte);                                  // Daten an PCF 8574 senden
18
        I2cStop ();
19
20
        displayData <<= 4;
21
    } while (--n);
22
    _delay_us (42);
23
}

von zitter_ned_aso (Gast)


Lesenswert?

c beginner schrieb:
> 2. Was ist an dieser Kombi Struct / Union nicht C konform und warum ?

Ich glaube das hat was mit der Speicherbelegung zu tun. Die 
member-Variablen einer Struktur werden nicht zwangsläufig hintereinander 
im Speicher abgelegt. Es gibt Lücken, die mit padding bits gefühlt 
werden.

Wenn du so eine Struktur in eine Union packst (wie oben), dann kannst du 
nicht den gesamten struct-Block mit einer Zahl überschreiben (wie oben) 
und damit alle member-Variablen initialiseren.

von c beginner (Gast)


Lesenswert?

>>  Rufus Τ. F Ich hätte da noch einen Änderungsvorschlag:

Sehr schön, warum seh ich sowas nicht selbst ? Vielen Dank.

von Markus F. (mfro)


Lesenswert?

Eric B. schrieb:
> stützt nicht auf Undefined Behaviour, wie das die Lösungen
> mit structs und unions tun

Das tun sie nicht.

Bitfelder sind implementation defined (muss jeder selbst wissen, ob das 
für ihn ein k.o.-Kriterium ist oder nicht).

"Unions zur Typumwandlung" mittlerweile offiziell erlaubt.

von Eric B. (beric)


Lesenswert?

Noch ein Änderungsvorschlag:
1
struct I2CData {
2
    uint8_t RS:1;
3
    uint8_t RW:1;
4
    uint8_t ENABLE:1;
5
    uint8_t Backlight:1;
6
    uint8_t Data: 4;
7
};
8
9
union I2cDataSet {
10
    struct I2CData BIT;
11
    uint8_t asByte;
12
} I2cData;
13
14
/* ---- Display Ausgabe Steuersignale - */
15
void WriteCommandToLCD (uint8_t displayCommand)
16
{
17
    I2cData.BIT.RS = 0;   // Steuersignal ausgeben (RS BIT=0)
18
    I2cData.BIT.RW = 0;
19
    LCDputNibble (displayCommand >> 4);
20
    LCDputNibble (displayCommand);
21
    _delay_ms (5);
22
    I2cData.BIT.RS = 1;   // Zurückstellen auf Datenausgabe
23
}
24
25
/* ---- Display Ausgabe Zeichen ------- */
26
27
void LCDputNibble (uint8_t displayData)
28
{
29
  I2cData.BIT.Data = displayData;
30
31
  I2cData.BIT.ENABLE = 1;
32
  I2cStart (PCF8574_Write_Adr); // I2C Device Adresse PCF 8574
33
  I2cWrite (I2cData.asByte);    // Daten an PCF 8574 senden
34
  I2cStop ();
35
36
  I2cData.BIT.ENABLE = 0;
37
  I2cStart (PCF8574_Write_Adr); // I2C Device Adresse PCF 8574
38
  I2cWrite (I2cData.asByte);    // Daten an PCF 8574 senden
39
  I2cStop ();
40
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Markus F. schrieb:
> "Unions zur Typumwandlung" mittlerweile offiziell erlaubt.

Nur in C, und das Ergebnis ist immer noch plattform-abhängig. Wenn man 
den Code auf eine andere Plattform kopiert, kann das Ergebnis komplett 
anders sein. Die "umständliche" Schreibweise mit bitweisen Operationen 
ist übrigens auch nicht weniger effizient, die Compiler können das gut 
optimieren. "union" zur Konvertierung zu verwenden ist also in 90% der 
Fälle nur der Schreibfaulheit geschuldet und resultiert in schlecht 
portier- und wartbarem Code.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Hier ein Beispiel, wie es mit meiner µSer Bibliothek in C++ gehen kann:
1
#include <iostream>
2
#include <iomanip>
3
#include <cstdint>
4
#include <uSer.hh>
5
6
struct I2CData {
7
    // Begin declaration
8
    USER_STRUCT (I2CData, uSer::AttrNone)
9
    
10
    USER_MEM(std::uint8_t, RS, uSer::Width<1>)
11
    USER_MEM(std::uint8_t, RW, uSer::Width<1>)
12
    USER_MEM(std::uint8_t, ENABLE, uSer::Width<1>)
13
    USER_MEM(std::uint8_t, Backlight, uSer::Width<1>)
14
    USER_MEM(std::uint8_t, Data, uSer::Width<4>)
15
    
16
    // Make members known to µSer
17
    USER_ENUM_MEM (RS, RW, ENABLE, Backlight, Data)
18
} I2cData;
19
20
// Dummy
21
static constexpr std::uint8_t PCF8574_Write_Adr = 0x12;
22
void I2cStart (std::uint8_t adr) {
23
  std::cout << "I2cStart(0x" << std::hex << std::setw(2) << std::setfill('0') << int { adr } << ");\n";
24
}
25
void I2cWrite (std::uint8_t data) {
26
  std::cout << "I2cWrite(0x" << std::hex << std::setw(2) << std::setfill('0') << int { data } << ");\n";
27
}
28
void I2cStop () {
29
  std::cout << "I2cStop();\n";
30
}
31
32
void _delay_ms (...) {}
33
34
void LCDputNibble (uint8_t displayData)
35
{
36
  I2cData.Data = displayData;
37
  I2cData.ENABLE = 1;
38
  
39
  std::uint8_t asByte [1];
40
  uSer::serialize (asByte, I2cData);
41
42
  I2cStart (PCF8574_Write_Adr); // I2C Device Adresse PCF 8574
43
  I2cWrite (asByte [0]);    // Daten an PCF 8574 senden
44
  I2cStop ();
45
46
  I2cData.ENABLE = 0;
47
  uSer::serialize (asByte, I2cData);
48
  
49
  I2cStart (PCF8574_Write_Adr); // I2C Device Adresse PCF 8574
50
  I2cWrite (asByte [0]);    // Daten an PCF 8574 senden
51
  I2cStop ();
52
}
53
54
55
/* ---- Display Ausgabe Steuersignale - */
56
void WriteCommandToLCD (uint8_t displayCommand)
57
{
58
    I2cData.RS = 0;   // Steuersignal ausgeben (RS BIT=0)
59
    I2cData.RW = 0;
60
    LCDputNibble (static_cast<std::uint8_t> (displayCommand >> 4));
61
    LCDputNibble (static_cast<std::uint8_t> (displayCommand));
62
    _delay_ms (5);
63
    I2cData.RS = 1;   // Zurückstellen auf Datenausgabe
64
}
65
66
67
int main () {
68
  WriteCommandToLCD (0x34);
69
}

Mit Dummy-Funktionen, damit es am PC testbar ist. Es ist etwas 
umständlicher als die union-Variante, aber man kann immer noch recht 
übersichtlich die Aufteilung der Bit definieren. Dafür ist es portabel 
auf alles, was C++17 unterstützt bei exakt identischem Ergebnis, 
unabhängig von Padding-Bytes, Endianness, Alignment...

Die Bibliothek: https://github.com/Erlkoenig90/uSer

von c beginner (Gast)


Lesenswert?

@ Eric B.
Das verändert die Funktion und ist daher keine Optimierung aber dennoch 
danke für den Versuch.

@ Niklas G.
c++ ist leider nichts für mich.

>> Nur in C, und das Ergebnis ist immer noch plattform-abhängig.
Ich habs gestern mal auf meinen diversen Compilern getestet und es 
funktioniert soweit auf diesen korrekt.

@ Markus F.
>> "Unions zur Typumwandlung" mittlerweile offiziell erlaubt.

Das ist wirklich gut zu wissen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

c beginner schrieb:
> Ich habs gestern mal auf meinen diversen Compilern getestet und es
> funktioniert soweit auf diesen korrekt.

Solange es nur ein einzelnes Byte ist funktioniert es "vermutlich" 
überall gleich. Aber möchte man für maximal 8 Einträge so etwas 
unschönes nutzen? Unterschiedliche Compiler für den selben Prozessor 
haben wahrscheinlich oft das gleiche Ergebnis. Wenn man andere 
Prozessoren einbringt (AVR, ARM, x86, x86-64, PowerPC, DSP-Plattformen) 
sieht das vermutlich anders aus. Es funktioniert halt nur "zufällig" und 
"mit Glück".

c beginner schrieb:
> c++ ist leider nichts für mich.

Dann mach es so wie vorgeschlagen:

Eric B. schrieb:
> #define I2C_RS  0x01
> #define I2C_RW  0x02
> #define I2C_ENA 0x04
> #define I2C_LED 0x08
> #define I2C_DATA(x) ((x) & 0x0F) << 4)
>
> i2c_data = I2C_RS | I2C_DATA(8);

Lieber C als C++ verwenden zu wollen, es dann aber zu umständlich finden 
und daher lieber einen unportablen Hack verwenden ist vielleicht nicht 
die beste Herangehensweise.

von c beginner (Gast)


Lesenswert?

>> Aber möchte man für maximal 8 Einträge so etwas unschönes nutzen?
Wie gesagt, für mich in diesem Fall ok, ich weiß ja das ich beim 
portieren dann aufpassen muß.

>> Wenn man andere Prozessoren einbringt (AVR, ARM...
Ich habs für ARM, AVR, 8x51 getestet und es passt soweit.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

c beginner schrieb:
> Ich habs für ARM, AVR, 8x51 getestet und es passt soweit.

Mach das mal mit bis zu 64bit großen Typen auf ARM und AVR. Da kommen 
dann die Unterschiede (ARM braucht Padding, AVR nicht). Hast du 
Unit-Tests geschrieben, um alle Fälle automatisiert testen zu können? 
Das ist nötig, damit du auch nach jeder kleinsten Änderung 
(Compiler-Optionen, Compiler-Version, Prozessor-Version, 
Plattform-Wechsel) prüfen kannst ob es noch stimmt.
Das gemeine an C (und C++) ist ja dass viele Dinge den Anschein haben zu 
funktionieren, aber dennoch falsch sind und früher oder später schief 
gehen können. Da kann man entweder mit viel Aufwand alles testen, oder 
es direkt richtig machen.

von Cyblord -. (cyblord)


Lesenswert?

Aber was genau spricht denn jetzt dagegen, die Struktur einfach auf ein 
uint8 zu Casten?

von W.S. (Gast)


Lesenswert?

Dergute W. schrieb:
> i2cdata = 0x81; /* set rs=1; data_7=1; others=0 */

Rufus Τ. F. schrieb:
> Daß das die schlechtestmögliche Lösung ist, ist hinreichend bekannt und
> ausdiskutiert worden. "Magic Numbers" im Code gelten außerhalb Deines
> spezifischen persönlichen Standards als fehleranfällig und schlecht
> wartbar.

Daß das keineswegs die schlechtestmögliche Lösung ist, sondern 
schlichtweg die pragmatischste und fehler-UN-anfälligste, ist 
hinreichend bekannt.

Die meisten Fehler kommen nämlich genau dadurch zustande, daß jemand 
überkomplizierte Konstrunkte verwendet, die oftmals auch noch über 
mehrere Quelldateien verstreut sind.

Nur die Leute, die eifrigst bemüht sind, zusätzlich zu den Regeln der 
Sprache C noch weitergehende möglichst einschränkende Regeln zu 
erfinden, pflichten deiner Ansicht bei. Ich nicht. Mein Horizont ist 
größer als nur C.

Mal zur Richtigstellung des Ganzen:
Es geht - so wie sich mir das darstellt - um Hardware-Angelegenheiten, 
also um ganz spezielle Registerbelegungen, die nur für eine einzige 
Hardware (hier wohl AVR) zutreffen.

Deshalb sind erstens alle Argumente bezüglich einer Portierbarkeit 
gegenstandslos. Einen speziellen Lowlevel-Hardwaretreiber kann man 
nirgendwohin portieren.

Zweitens gehören solche Dinge wie die Belegung eines HW-Registers in 
einen genau dafür zuständigen Treiber und haben gefälligst dort drin 
gekapselt zu sein, so daß man außerhalb eben dieses Treibers auf gar 
keinen Fall mit derartigen Details in Berührung kommt.

Und deshalb ist die allereinfachste und pragmatischste Ausführung 
dessen, was tatsächlich dort drin getan werden muß, auch die beste. Ohne 
irgendwelche Verrenkungen. Und wer dort drin herumwursteln will, hat 
gefälligst das betreffende Kapitel im Manual zu studieren.

Kurzum, Rufus, du liegst mit deiner hier vorliegenden Verallgemeinerung 
mal wieder falsch.

W.S.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Cyblord -. schrieb:
> Aber was genau spricht denn jetzt dagegen, die Struktur einfach auf ein
> uint8 zu Casten?

Das Gleiche wie bei union. Das Ergebnis ist plattform-abhängig. 
Bitfelder sind nicht portabel.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

W.S. schrieb:
> Es geht - so wie sich mir das darstellt - um Hardware-Angelegenheiten,
> also um ganz spezielle Registerbelegungen, die nur für eine einzige
> Hardware (hier wohl AVR) zutreffen.

Es ist die Registerbelegung eines Displays. Das könnte man durchaus auch 
mal an einem ARM anschließen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

W.S. schrieb:
> Kurzum, Rufus, du liegst mit deiner hier vorliegenden Verallgemeinerung
> mal wieder falsch.

Geisterfahrer, tausende.

Ich bewundere zwar Deinen Ehrgeiz, anderen Leuten das C-Programmieren 
nahebringen zu wollen ("Lernbetty" etc.), kann mich aber mit Deinen 
elementaren Einstellungen zu Sprachdetails überhaupt nicht anfreunden.

Ich verdiene seit mehreren Jahrzehnten meinen Lebensunterhalt damit, daß 
ich mit dieser Sprache arbeite, und ich sehe recht viel professionellen, 
von anderen Leuten geschriebenen Code.

"Magic numbers" im Quelltext verwendet praktisch niemand davon - aus 
gutem Grund: Das ist wartungsfeindlich und fehlerträchtig.

> Deshalb sind erstens alle Argumente bezüglich einer Portierbarkeit
> gegenstandslos. Einen speziellen Lowlevel-Hardwaretreiber kann man
> nirgendwohin portieren.

Genau das kann man. Wenn man keine "magic numbers" verwendet.

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.