Hallo Ihr, ich hab da mal wider ein kleines Problem. Ich hab ein 16 bit großes struct: struct { unsigned char AB : 1 ; // Kanal selektion unsigned char DC : 1 ; // don't care unsigned char GA : 1 ; // Gain unsigned char SHDN : 1 ; // shutdown unsigned int dac_val : 10 ; // dac value unsigned char empty : 2 ; // don't care } mcp4812; Die ersten 4 bit sind fest: mcp4812.AB = 0; mcp4812.DC = 0; mcp4812.GA = 1; mcp4812.SHDN = 1; "dac_val" steht der 10 bit Wert den der ADC ausgeben soll. Diese 16 bit möchte ich so zerlegen das ich sie über die SPI Schnittstelle ausgeben kann. Beim MEGA 328 sind die Register bekanntlich 8 bit breit. Von daher meine Frage wie zerlege ich den 16 bit "struct" in 2 8 bit Häpchen die ich augeben kann. Gruß Andy
1 | struct { |
2 | unsigned char AB : 1 ; // Kanal selektion |
3 | unsigned char DC : 1 ; // don't care |
4 | unsigned char GA : 1 ; // Gain |
5 | unsigned char SHDN : 1 ; // shutdown |
6 | union{ |
7 | unsigned char dac_bytes[2]; // dac value bytes |
8 | unsigned int dac_val : 10; // dac value |
9 | }
|
10 | unsigned char empty : 2 ; // don't care |
11 | } mcp4812; |
je nach dem welchen C dialekt du verwendest musst du dem union ggf. nen namen verpassen. lg
Andreas Geissler schrieb: > Von daher meine Frage wie zerlege ich den 16 bit "struct" in 2 8 bit > Häpchen die ich augeben kann. Die Frage taucht alle 2 Wochen erneut im Forum auf. Kleines Vorgepänkel. So etwas:
1 | struct { |
2 | unsigned char AB : 1 ; // Kanal selektion |
3 | unsigned char DC : 1 ; // don't care |
4 | unsigned char GA : 1 ; // Gain |
5 | unsigned char SHDN : 1 ; // shutdown |
6 | unsigned int dac_val : 10 ; // dac value |
7 | unsigned char empty : 2 ; // don't care |
8 | } mcp4812; |
ist meistens EXTREM unklug. Man macht keine anonyme struct! Denn dann kannst du diesen Datentyp zu nichts anderem mehr verwenden und sich im restlichen Code nicht mehr darauf beziehen. Es gibt zwar Ausnahmen, aber die sind rar. Also gib deiner struct einen Namen, und alles ist gut.
1 | struct myChanelData { |
2 | unsigned char AB : 1 ; // Kanal selektion |
3 | unsigned char DC : 1 ; // don't care |
4 | unsigned char GA : 1 ; // Gain |
5 | unsigned char SHDN : 1 ; // shutdown |
6 | unsigned int dac_val : 10 ; // dac value |
7 | unsigned char empty : 2 ; // don't care |
8 | } mcp4812; |
Zb kannst du mit einer union das Gewünschte erreichen
1 | union convert |
2 | {
|
3 | struct myChanelData Data; |
4 | unsigned char Bytes[2]; |
5 | };
|
In einer union liegen die Member quasi üereinander, belegen also denselben Speicherbereich. D.h. du kannst über Data den Speicher beschreiben und dir über Bytes denselben Speicher in Byte-Form auslesen.
1 | union convert conv; |
2 | |
3 | conv.Data = mcp4812; |
4 | spi_out( conv.Bytes[0] ); |
5 | spi_out( conv.Bytes[1] ); |
:
Bearbeitet durch User
Eine andere Möglichkeit wäre es, einen entsprechenden Pointer zurecht zu casten.
1 | struct myChanelData { |
2 | ...
|
3 | } mcp4812; |
4 | |
5 | |
6 | unsigned char* pData = (unsigned char*)&mcp4812; |
7 | |
8 | spi_out( pData[0] ); |
9 | spi_out( pData[1] ); |
10 | ...
|
kleine korrektur zu meinem posting oben... dort musst du noch die empty bits aus dem struct streichen, sonst ist der struct 18bit gross lg
Karl Heinz schrieb: > Eine andere Möglichkeit wäre es, einen entsprechenden Pointer zurecht zu > casten. Ich würde genau diese Pointer-Cast-Variante immer dem union-Trick vorziehen. Das mit der union ist ausdrücklich lt. C-Standard nicht garantiert. Mein Rat: Den Tipp mit der union hier im Forum nicht mehr zu geben, sondern stattdessen ausschließlich die Pointer-Variante zu propagieren. Da kommt der Compiler nicht dran vorbei ;-)
Frank M. schrieb: > Karl Heinz schrieb: >> Eine andere Möglichkeit wäre es, einen entsprechenden Pointer zurecht zu >> casten. > > Ich würde genau diese Pointer-Cast-Variante immer dem union-Trick > vorziehen. Das mit der union ist ausdrücklich lt. C-Standard nicht > garantiert. Ich denke es war der C99 Standard (weiss nicht mehr genau), welcher diesen union Trick für chars (und damit auch unsigned chars) legitimiert hat. Zuvor war die Situation so, dass es keinen gerantierten Weg gab, weder durch union, noch durch umcasten von Zeiger-Typen. Denn auch in dem Moment, in dem du einen Pointer Typ umcastest, landest du automatisch in 'undefined behaviour' land. Und wieder (konsequenterweise): einzige Ausnahme: umcasten zu einem char-Pointer. > Mein Rat: Den Tipp mit der union hier im Forum nicht mehr zu geben, Mein Tipp: die neueren C-Standards beherzigen. Musste ich auch.
:
Bearbeitet durch User
Karl Heinz schrieb: > Frank M. schrieb: >> Karl Heinz schrieb: >>> Eine andere Möglichkeit wäre es, einen entsprechenden Pointer zurecht zu >>> casten. >> >> Ich würde genau diese Pointer-Cast-Variante immer dem union-Trick >> vorziehen. Das mit der union ist ausdrücklich lt. C-Standard nicht >> garantiert. > > Ich denke es war der C99 Standard (weiss nicht mehr genau), welcher > diesen union Trick für chars (und damit auch unsigned chars) legitimiert > hat. Meinst du N1256 (C99) 6.2.6.1(5)?
1 | Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. If such a representation is produced by a side effect that modifies all or any part of the object by an lvalue expression that does not have character type, the behavior is undefined. [...] |
Den Abschnitt gibt es auch in N1570 (C11). > Zuvor war die Situation so, dass es keinen gerantierten Weg gab, weder > durch union, noch durch umcasten von Zeiger-Typen. Doch. 6.2.6.1(4):
1 | Values stored in non-bit-field objects of any other object type consist of n × CHAR_BIT bits, where n is the size of an object of that type, in bytes. The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); |
Also:
1 | struct foo f; |
2 | char tmp[sizeof(struct foo)]; |
3 | memcpy(&tmp, &f, sizeof(f)); |
Und ja, das ist effizient. Sowohl Clang und gcc machen (zumindest auf x86_64) daraus entsprechende mov-Instruktionen da memcpy ein Builtin ist. avr-gcc, msp430-gcc & Co sollten das auch können, hängt ja der gleiche Optimizer dahinter. > Denn auch in dem Moment, in dem du einen Pointer Typ umcastest, landest > du automatisch in 'undefined behaviour' land. Und wieder > (konsequenterweise): einzige Ausnahme: umcasten zu einem char-Pointer. Fast richtig. char und void + cv-qualifiers. (memcpy nimmt void)
Danke für das Raussuchen, ghl. So hatte ich das auch in Erinnerung. Johann hatte das bzgl. union schon mehrfach hier im Forum angemerkt. Also halten wir fest: union ist bäh, Pointer ist gut. :-)
Frank M. schrieb: > Danke für das Raussuchen, ghl. So hatte ich das auch in Erinnerung. > Johann hatte das bzgl. union schon mehrfach hier im Forum angemerkt. Ist aber trotzdem Quatsch. Ich schätze Johann sehr. Aber in Sachen Standard Auslegung ist er für mich keine Autorität. Welcher Teil der ständig vprkommenden Phrase "except character type" ist unklar?
:
Bearbeitet durch User
Karl Heinz schrieb: > Welcher Teil der ständig vprkommenden Phrase "except character type" ist > unklar? Du meinst offenbar dies in der Formulierung: "... is read by an lvalue expression that does not have character type, the behavior is undefined." Okay, das habe ich bisher auch überlesen. Gebe ich zu. Ich kann also offenbar in unions immer character-weise rumstochern. Dann hast Du recht: Ob ich char-Pointer oder (unsigned) char union member nehme, ist schnurz.
Frank M. schrieb: > offenbar in unions immer character-weise rumstochern. In alle Kürze: Worum gehts denn. es geht um die strict-Aliasing Sache. Der Compiler darf immer (mit Ausnahmen) davon ausgehen, dass es keine 2 Wege zum selben Objekt gibt, über die das Objekt modifiziert wird. Eine dieser Ausnahmen ist ein char-Typ. Sobald ein char-Typ im Spiel ist, ist die strict-Aliasing Regelung mehr oder weniger abgeschaltet und der Compiler muss von Aliasing ausgehen. Es gibt gute Artikel zu dem Thema im Web. Ein guter Anlaufpunkt sind fast immer die Artikel, die auf http://stackoverflow.com/ zu finden sind. Vor allen Dingen, seit die klassischen Newsgroups massiv an Bedeutung verloren haben - leider. Denn in den Newsgroups war es nicht ungewöhnlich, dass man dort auch auf die Leute traf, die am jeweiligen Standard mitgearbeitet haben und die Standard-Schreiber selbst um eine Klarstellung bitten konnte. http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule http://dbp-consulting.com/tutorials/StrictAliasing.html
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.