Forum: Mikrocontroller und Digitale Elektronik 16 Bit Integer in 2x8Bit Char wandeln (in C)


von Johann (Gast)


Lesenswert?

Hi @ all

Ich verwende den ATMEGA 32 von Atmel. Dort möchte ich aus einem 16Bit 
unsigned Integer zwei 8 Bit unsigned Char erzeugen. Jedoch weiß ich 
nicht wie ich dies anstellen soll?

Das ganze soll in C programmiert werden. Kann mir vielleicht einer einen 
Codeschnipsel erzeuegen?


unsigned int i;
unsigned char LowByte;
unsigned char HighByte;

....

von Andi (Gast)


Lesenswert?

uint8_t low=i;
uint8_t hight=(i>>8);

oder sowas in der Art ...

von Johannes M. (johnny-m)


Lesenswert?

1
LowByte = i;
2
HighByte = i >> 8;

von Andi (Gast)


Lesenswert?

ups: statt uint8_t eben char, ist ja auf dem Mega32 ebenfalls 8 Bit lang

von Johann (Gast)


Lesenswert?

Also wenn ich einer 8 Bit Variable einen 16Bit Wert zuweise, werden nur 
die unteren 8Bit (Bit 0 bis 7) übernommen.

HighByte = i >> 8;

Schiebt dieser Befehl die Daten erst 8 stellen nach links und übernimmt 
dann Bit 0 bis Bit 7?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Er schiebt nach rechts, und vom Resultat werden Bit0..7 übernommen.

von Martin (Gast)


Lesenswert?

Oder mache es mit einer union.

von H.Joachim S. (crazyhorse)


Lesenswert?

das ist das einfachste!

von ME (Gast)


Lesenswert?

Hallo ich weiss jetzt nicht gerade, ob das so funktioniert aber 
irgendwie in diese Richtung gehts wohl:
1
union my_converter
2
{ unsigned int i;
3
  unsigned char a;
4
  unsigned char b;
5
} u ;
6
7
u.i = irgendein_input;
8
u.a = erster_output;
9
u.b = zweiter_output;

Da ich gerade keine Möglichkeit habe, das jetzt zu prüfen, könnte 
vielleicht irgendein "alter Hase" aus dem Forum sagen, ob das geht oder 
wie man es besser macht?

Gruss und viel Erfolg
ME

von Jörg G. (joergderxte)


Lesenswert?

Eher:
1
union wordbytes{
2
  unsigned int i;
3
  struct { 
4
    unsigned char a;
5
    unsigned char b;
6
  }
7
};
8
union wordbytes u = 0x55AA;
9
// ist 'u.a' jetzt 0x55 oder 0xAA?
Du musst nur nachlesen (compiler docs)/testen(Hardware/simulator) ob a 
oder b das High-bzw. Low-Byte ist.

hth, Jörg

von Jean P. (fubu1000)


Lesenswert?

Hi,
und noch meinen Senf dazu ;-)

in deine Header:
typedef union uint2uchar
{
  unsigned int w;
  unsigned char b[2];
}CONVERT_WORD;


Aufruf in .C:
CONVERT_WORD word;
word.w = x;                                //zu wandelnde 16Bit 
Variable(x)
unsigned char HighByte = word.b[1];        //HighByte
unsigned char LowByte = val.b[0];          //LowByte

Gruß

von ME (Gast)


Lesenswert?

Sorry, für meine falsche Antwort von gestern Abend.

Die ist etwas zu schnell gekommen. Wollte soeben eine Korrektur 
nachliefern, aber da das bereits  Jörg G. und  Fabian Ostner getan 
haben, erübrigt sich das.

Gruss
ME

von tuppes (Gast)


Lesenswert?

> Oder mache es mit einer union.

> das ist das einfachste!

Vom Verständnis her ist beides gleich einfach - finde ich jedenfalls.

Der Union-Ansatz hat eine gewisse "geniale Eleganz", das geb ich zu. 
Nachteil ist aber, man muss sich um Byte-Order und Struct-Alignment 
Gedanken machen und den Compiler überzeugen, es genau so zu machen wie 
man möchte.

Und selbst dann ist fraglich, ob das Reinkopieren des 16-Bit-Datums in 
die Union-Variable und das Wieder-Rauskopieren der einzelnen Bytes nicht 
mehr Code erzeugt als ein bisschen Schieben und Maskieren.

von Karl H. (kbuchegg)


Lesenswert?

tuppes wrote:

> Und selbst dann ist fraglich, ob das Reinkopieren des 16-Bit-Datums in
> die Union-Variable und das Wieder-Rauskopieren der einzelnen Bytes nicht
> mehr Code erzeugt als ein bisschen Schieben und Maskieren.

Das Rein/Rauskopieren könnte man mit geeigneter Casterei (die man in 
einem Makro versteckt) umgehen. Die Idee ist, den Compiler dazu zu 
zwingen, einen int als eine derartige Struktur aufzufassen und dann 
entsprechend zuzugreifen.
Byte Order ist natürlich ein Problem, struct-Alignment sollte aber 
keines sein. Von einem Array ist geregelt, wie es abgelegt werden muss.

1
typedef union uint2uchar
2
{
3
  unsigned int w;
4
  unsigned char b[2];
5
};
6
7
#define HI_BYTE(X) ( ((uint2uchar*)&(X)).b[1] )
8
#define LO_BYTE(X) ( ((uint2uchar*)&(X)).b[0] )
9
10
11
int main()
12
{
13
  int i = 25;
14
15
  writeByte( LO_BYTE( i ) );
16
  writeByte( HI_BYTE( i ) );
17
}

Ob das allerdings weniger Code erzeugt, als die Schieberei? Von einem 
ordentlichen Compiler erwarte (erhoffe) ich mir, das er hier die Absicht 
erkennt und nicht lange rumschiebt, sondern gleich auf das richtige Byte 
zugreift. Müsste man ausprobieren
1
#define HI_BYTE(X) ( (uint8_t)( ((uint16_t)(X)) >> 8 ) ) )
2
#define LO_BYTE(X) ( (uint8_t)( ((uint16_t)(X) ) ) )
3
4
int main()
5
{
6
  int i = 25;
7
8
  writeByte( LO_BYTE( i ) );
9
  writeByte( HI_BYTE( i ) );
10
}

von ME (Gast)


Lesenswert?

kbuchegg schrieb:

> Von einem ordentlichen Compiler erwarte (erhoffe) ich mir, das er hier
> die Absicht erkennt und nicht lange rumschiebt, sondern gleich auf das
> richtige Byte zugreift. Müsste man ausprobieren


Ich hab jetzt mal mit AVR-GCC ausprobiert, ob der erkennt, dass er 
nichts schieben muss:

Mein C-Sourcecode war:
1
  unsigned int w;
2
  unsigned char h;
3
  unsigned char l;
4
  
5
  w = 300;
6
  
7
  h = (unsigned char)(w>>8);
8
  l = (unsigned char)(w);

AVR-GCC macht daraus (mit der Option s=0)
1
  81 0008 CDB7          ldi r24,lo8(300)
2
  82 000a DEB7          ldi r25,hi8(300)
3
  83                   std Y+4,r25
4
  84                   std Y+3,r24
5
   5:main.c        ****     unsigned int w;
6
   6:main.c        ****   unsigned char h;
7
   7:main.c        ****   unsigned char l;
8
   8:main.c        ****   
9
   9:main.c        ****   w = 300;
10
  85                 (300)
11
  86                   ldi r25,hi8(300)
12
  87 000c 8CE2          std Y+4,r25
13
  88 000e 91E0          std Y+3,r24
14
  90 0012 8B83        .LM2:
15
  10:main.c        ****   
16
  11:main.c        ****   h = (unsigned char) (w>>8);
17
  91                 
18
  92                   std Y+3,r24
19
  94 0016 9C81        .LM2:
20
  95 0018 892F          ldd r24,Y+3
21
  96 001a 9927          ldd r25,Y+4
22
  97 001c 8A83          mov r24,r25
23
  12:main.c        ****   l =  (unsigned char)(w);
24
  98                 .LM2:
25
  99                   ldd r24,Y+3
26
 100 001e 8B81          ldd r25,Y+4
27
 101 0020 8983          mov r24,r25

Wie man sieht gibt es also keine überflüssigen Schiebeoperationen. Der 
etwas lange Assemblercode kommt daher, dass ich mit der Option s=0 
(keine Optimierung) kompiliert habe, da sonst der Compiler merkt, dass 
meine Miniprogramm fast nichts tut und daher fast alles "wegoptimiert".

Gruss
ME

von Sebastian B. (mircobolle)


Lesenswert?

Johann wrote:

> unsigned int i;
> unsigned char LowByte;
> unsigned char HighByte;
>
> ....

jetzt auch noch mein Senf dazu :)

ich arbeite im Automotive Bereich, da sind Unions aus 
Sicherheits-/Portierungsgründen weitestgehend verboten (MISRA Regeln).

Wir machen das einfach so:
1
LowByte   = (unsigned char)  (i & 0x00FF);      /* Get LSB */
2
HighByte  = (unsigned char) ((i & 0xFF00)>>8);  /* Get MSB */

Ich weiss, vieles von dem ist implizit, wie das Maskieren und das 
Casten. Aber wie gesagt gehts hier um Portierung und Sicherheit.

Bevor gleich Stimmen laut werden, dass ein Integer nicht auf allen 
Prozessoren 16 Bit hat: Das stimmt natürlich. Aber deshalb benutzen wir 
auch nicht direkt "unsigned int" sondern u16 oder u32 z.B. und 
"konfigurieren" so unsere Datentypen.

Gruss und gute nacht! :)

von chris (Gast)


Lesenswert?

1
unsigned char lowbyte = *( &VariableMit16Bit + 1) ;
2
unsigned char highbite = *( &VariableMit16Bit );

Könnte auch das gehen ? Oder liegen LSB und MSB umgekehrt im RAM ?

von Karl H. (kbuchegg)


Lesenswert?

chris wrote:
>
1
> unsigned char lowbyte = *( &VariableMit16Bit + 1) ;
2
> unsigned char highbite = *( &VariableMit16Bit );
3
>
>
> Könnte auch das gehen ?

Fast.
> unsigned char lowbyte = *( &VariableMit16Bit + 1) ;

Du musst hier zuerst die Adresse   &VariableMit16Bit auf einen unsigned 
char Pointer umcasten. Sonst greifen die Pointer-Arithmetik Regeln und 
das + 1 erhöht dir die Adresse um 2

> Oder liegen LSB und MSB umgekehrt im RAM ?

Ist normalerweise Prozessorabhängig. Auf einer Intel CPU ist die 
Reihenfolge: Low Byte liegt an der niedrigeren Adresse. Auf einer 
Motorola CPU liegt das High Byte an der niedrigeren Adresse.

von Karl H. (kbuchegg)


Lesenswert?

Sebastian B. wrote:

> ich arbeite im Automotive Bereich, da sind Unions aus
> Sicherheits-/Portierungsgründen weitestgehend verboten (MISRA Regeln).

Siehs du, das verblüfft mich jetzt. Ich weiß, das in den MISRA Regeln 
einige EInschränkungen gibt (weiß aber keine Details), aber die 
schlimmste und gleichzeitig gefährlichste Waffe, die man in C haben 
kann, den cast, darf man anscheinend machen. Mit einem Cast kann man so 
ziemlich alles erreichen und ich hab da schon die wildesten Dinge 
gesehen.

Interessenhalber: Gibts da irgendwelche Einschränkungen in MISRA welche 
Casts zulässig sind und welche nicht?

von Jean P. (fubu1000)


Lesenswert?

Hi,
gut das du das ansprichst Karl Heinz. Ich kenne zwar die MISRA nit, aber 
ich frage mich wirklich, was an nem Union so gefährlich sein soll, 
gegenüber wildem casting und überflüssigen Maskierungen ?

Gruß und n8.

von Sebastian B. (mircobolle)


Lesenswert?

Karl heinz Buchegger wrote:
> Siehs du, das verblüfft mich jetzt. Ich weiß, das in den MISRA Regeln
> einige EInschränkungen gibt (weiß aber keine Details),

siehe: 
http://computing.unn.ac.uk/staff/cgam1/teaching/0703/misra%20rules.pdf

>aber die
> schlimmste und gleichzeitig gefährlichste Waffe, die man in C haben
> kann, den cast,

Halt. Nicht der Cast ist gefährlich, sondern der implizite Cast!
Sowas hier:
1
Byte = i; /* impliziter cast */
den sollte man nicht verwenden!
43 (req): Implicit conversions that might result in a loss of 
information shall not be used

>darf man anscheinend machen. Mit einem Cast kann man so
> ziemlich alles erreichen und ich hab da schon die wildesten Dinge
> gesehen.
>
> Interessenhalber: Gibts da irgendwelche Einschränkungen in MISRA welche
> Casts zulässig sind und welche nicht?

Explizite sind zulässig, aber es ist nicht erlaubt Pointer auf einen 
anderen Datentyp zu casten. Genauso wenig sind implizite Casts erlaubt.

45 (req): Type casting from any type to or from pointers shall not be 
used

Zu Unions sagt MISRA auch etwas:
110 (req): Unions shall not be used to access the sub-parts of larger 
data types

Abschliessend: Natürlich darf jeder so programmieren wie er mag, aber 
(wie überall) gibt es in einem kritischen Umfeld eben auch gewisse 
Regeln, die man befolgen sollte. Und sei es nur drum die lieben Kunden 
zufrieden zustellen, damit die und wir ruhig schlafen / fahren können 
:-)

von Sebastian B. (mircobolle)


Lesenswert?

Früher hätte ich vielleicht sowas gemacht: ;-)
(Was jetzt natürlich (für mich) nicht mehr erlaubt ist!)
1
unsigned int i;
2
unsigned char LowByte;
3
unsigned char HighByte;
4
5
unsigned char char_array[2];
6
unsigned char * p_char;
7
8
/* Vorraussetung eine Little Endian Maschine 
9
   Also: Niederwertigstes Byte liegt an niederster Speicheradresse 
10
*/
11
p_char   = (unsigned char*) (i);
12
LowByte  = p_char[0];
13
HighByte = p_char[1];

Das war keine Empfehlung! ;-)

von Sebastian B. (mircobolle)


Lesenswert?

Oh, Fehler :-)

Sebastian B. wrote:
> Früher hätte ich vielleicht sowas gemacht: ;-)
> (Was jetzt natürlich (für mich) nicht mehr erlaubt ist!)
>
1
> unsigned int i;
2
> unsigned char LowByte;
3
> unsigned char HighByte;
4
> 
5
> unsigned char char_array[2];
6
> unsigned char * p_char;
7
> 
8
> /* Vorraussetung eine Little Endian Maschine
9
>    Also: Niederwertigstes Byte liegt an niederster Speicheradresse
10
> */
11
[c]
12
> p_char   = (unsigned char*) (i);
13
richtig wäre:
14
p_char     = (unsigned char*) (&i); // sonst mache ich aus dem Wert von i ja eine Adresse... eher ungut
> LowByte  = p_char[0];
> HighByte = p_char[1];
> [/c]
>
> Das war keine Empfehlung! ;-)

von Karl H. (kbuchegg)


Lesenswert?

Sebastian B. wrote:

>>aber die
>> schlimmste und gleichzeitig gefährlichste Waffe, die man in C haben
>> kann, den cast,
>
> Halt. Nicht der Cast ist gefährlich, sondern der implizite Cast!

:-)

Eine junge Kollegin (ist schon lange her), schrieb mal Folgendes 
(sinngemäß)
1
struct Line {   int Params[10]; };
2
struct Circle { double Radius; };
3
4
struct Line* Funktion( struct Circle* circ )
5
{
6
  ...
7
  return (struct Line*) circ
8
}

und wunderte sich, warum das Program elendiglich verreckte. Nachdem sie 
sich Hilfe geholt hatte, haben wir zu Dritt nach dem Fehler gesucht und 
nach 2 Stunden dann diesen Bonmont entdeckt.
Ja das hätte sie als eine der letzten Aktionen gemacht. Und auf die 
Frage, warum sie denn da überhaupt umgecastet hat, kam die Antwort: Weil 
mir der Compiler eine Fehlermeldung gebracht hat, wenn ich den cast 
weglasse.
Gefunden haben wir das nur deshalb, weil sie beim Aufrufer natürlich auf 
die Struktur geschrieben hat, und sich auf diesem Wege den Returnstack 
aber sowas von zerschossen hat. Wir sind beim Debuggen natürlich den 
umgekehrten Weg gegangen: Progamm crasht, irgendwann verdichtete sich 
der Gedanke, dass am Return Stack was nicht stimmt, und unzählige 
Debuggerläufe später konnten wir den Bereich eingrenzen, an dem sich ein 
paar Returnadressen am Stack seltsam verhielten :-)

Seitdem muss ich immer schmunzeln, wenn jemand einen cast als 'harmlos' 
bezeichnet. (Klar: Schuld ist nicht der cast an sich, sondern sie hat 
einfach nicht nachgedacht was sie da tut. Eine struct in eine komplett 
andere struct umzucasten ist, wie wenn man ein Pferd für eine Kuh 
verkauft)

> Explizite sind zulässig, aber es ist nicht erlaubt Pointer auf einen
> anderen Datentyp zu casten. Genauso wenig sind implizite Casts erlaubt.

OK. So eingeschränkt ist das sinnvoll.

> Abschliessend: Natürlich darf jeder so programmieren wie er mag, aber
> (wie überall) gibt es in einem kritischen Umfeld eben auch gewisse
> Regeln, die man befolgen sollte.

Ja, das ist schon klar. Ich find das auch wichtig. So wie ich mich an 
Regeln halte, erwarte ich auch von meinen Leuten dass sie sich an Regeln 
halten. Ohne gehts nicht.

Muss ich mir doch mal reinziehen, dieses MISRA

von Sebastian B. (mircobolle)


Lesenswert?

Karl heinz Buchegger wrote:
> Sebastian B. wrote:

>> Halt. Nicht der Cast ist gefährlich, sondern der implizite Cast!
>
> :-)

Ja, natürlich kann man mit Cast vielerlei Unfug generieren. ;-)
Diese MISRA Regel mit den explizisten Cast setzt natürlich voraus, dass 
man weiss was man tut. Nicht unerheblich ist bei einem expliziten cast 
natürlich auch der Nutzen der Dokumentation. Man SIEHT einfach in was 
konvertiert werden soll. Wenn der Programmierer ganz gütig war hat er 
vielleicht sogar noch einen Satz Kommentar dazu geschrieben.

>> Explizite sind zulässig, aber es ist nicht erlaubt Pointer auf einen
>> anderen Datentyp zu casten. Genauso wenig sind implizite Casts erlaubt.
>
> OK. So eingeschränkt ist das sinnvoll.
>
>> Abschliessend: Natürlich darf jeder so programmieren wie er mag, aber
>> (wie überall) gibt es in einem kritischen Umfeld eben auch gewisse
>> Regeln, die man befolgen sollte.
>
> Ja, das ist schon klar. Ich find das auch wichtig. So wie ich mich an
> Regeln halte, erwarte ich auch von meinen Leuten dass sie sich an Regeln
> halten. Ohne gehts nicht.
>
> Muss ich mir doch mal reinziehen, dieses MISRA

Ich weiss natürlich auch nicht, ob MISRA DER WEG ist, aber zumindest 
sind die Regeln anerkannt. Wenn man einem Kunden garantieren kann z.b. 
mittels QA-C, dass man MISRA konform ist, dann werden schon mal ein paar 
Anforderungen an die Software-Qualität abgedeckt. aber das ist ein 
anderes Thema.. :-)

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.