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_tRS:1;
3
uint8_tRW:1;
4
uint8_tENABLE:1;
5
uint8_tLedBacklight:1;
6
uint8_tDATA_4:1;
7
uint8_tDATA_5:1;
8
uint8_tDATA_6:1;
9
uint8_tDATA_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.
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.
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.
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.
>> 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
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;
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.
@ 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.
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.
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!
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.
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
structI2CData{
2
uint8_tRS:1;
3
uint8_tRW:1;
4
uint8_tENABLE:1;
5
uint8_tBacklight:1;
6
uint8_tDATA_4:1;
7
uint8_tDATA_5:1;
8
uint8_tDATA_6:1;
9
uint8_tDATA_7:1;
10
};
11
12
unionI2cDataSet{
13
structI2CDataBIT;
14
uint8_tasByte;
15
}I2cData;
Dank dieser Struktur kann man nun den Code, wie folgt, schreiben.
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.
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.
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.
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.
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.
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
@ 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.
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.
>> 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.
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.
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.
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.
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.
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.