Zugriff auf int-Variable über 8-bit Zeiger

Gast #281796
Lesenswert?

Hallo!

Stellt euch bitte folgende Situation vor:

In einem IC gibt es ein 16-bit breites Konfigurationsregister, dass ich
über SPI auslesen möchte. Ich definiere mir also eine Integer-Variable,
in der ich diese Info abspeichern kann. Da ich über den SPI-Bus aber
nur 8 bit auf einmal empfangen kann, muss ich das Auslesen dieses
Registers auf 2 Anläufe staffeln.
Im ersten Anlauf lese ich z.B. das High-Byte und im zweiten das
Low-Byte. Die Frage lautet nun, wie ich die zwei gelesenen Bytes in
meine ursprünglich dafür vorgesehene Variable bekomme.

Ich habe vor kurzem ein Beispielprogramm gesehen, in dem folgendes
Konstrukt dafür verwendet wurde (auszugsweise):


int config_reg;

// Aufruf der Funktion, um das Konfigurationsregister auszulesen:
get_config_reg( (unsigned char*) &config_reg);


// Inhalt der Funktion:
void get_config_reg( (unsigned char*) &config_reg)
{
     spi_byte_1 = read_spi();    // Lies erstes Byte vom SPI-Bus
     config_reg[1] = spi_byte_1;
     spi_byte_2 = read_spi();    // Lies zweites Byte vom SPI-Bus
     config_reg[0] = spi_byte_2;
}


Mich würde jetzt interessieren, ob sowas einer "sauberen"
C-Programmierung entspricht? Mir ist schon klar, wie es funktioniert
und das es korrekt ist; ich habe so etwas in der Art aber zum ersten
Mal gesehen. Ich hätte es nämlich so gemacht:

int config_reg;

// Aufruf der Funktion, um das Konfigurationsregister auszulesen:
config_reg = get_config_reg();


// Inhalt der Funktion:
int get_config_reg()
{
     int ergebnis;

     ergebnis = read_spi();      // Lies erstes Byte vom SPI-Bus
     ergebnis = ergebnis >> 8;
     ergebnis |= read_spi();    // Lies zweites Byte vom SPI-Bus

     return ergebnis;
}


Beide Konstrukte liefern ja dasselbe Ergebnis.

Wie gesagt, würde mich aber interessieren, ob so etwas "sauber" und
gebräuchlich ist.

Vielen Dank schonmal für euere Beiträge!


Gruß
Thomas
Gast #281798
Lesenswert?

Die 1. Variante ist gefährlich, da sie einen Compiler bzw. Target mit
einer bestimmten Byteorder voraussetzt.


Die Schiebvariante ist sauber, da sie völlig unabhängig vom verwendeten
Compiler und verwendeten Target immer funktioniert.


Peter
Gast #281799
Lesenswert?

Mir gefällt die zweite Variante ehrlich gesagt auch besser, auch wenn
sie vielleicht nicht ganz so ellegant ist, wie Variante 1.

@Peter:
Was meinst du mit "einer bestimmten Byteorder" (little endian oder
big endian)?

Gruß
Thomas
Persönliche Seite #281800
Lesenswert?

.

Das hier geht in die Hose:

  // Inhalt der Funktion:
  int get_config_reg()
  {
     int ergebnis;

     ergebnis = read_spi();      // Lies erstes Byte vom SPI-Bus
     ergebnis = ergebnis >> 8;
     ergebnis |= read_spi();    // Lies zweites Byte vom SPI-Bus

     return ergebnis;
  }


Man beachte die Richtung des Schiebeoperators!

Obendrein ist die Funktion des Schiebeoperators bei
vorzeichenbehafteten Werten ... seltsam.

Mehr Funktionalität dürfte das hier bringen:

  unsigned int get_config_reg()
  {
     unsigned int ergebnis;

     ergebnis = read_spi();        // 1. Byte ist LSB
     ergebnis |= read_spi() << 8;  // 2. Byte ist MSB

     return ergebnis;
  }

oder, wenn die Bytes "andersrum" abgespeichert werden sollen:

  unsigned int get_config_reg()
  {
     unsigned int ergebnis;

     ergebnis = read_spi() << 8;  // 1. Byte ist MSB
     ergebnis |= read_spi();      // 2. Byte ist LSB

     return ergebnis;
  }
Gast #281801
Lesenswert?

Die erste Variante ist ganz pfui, zu der Byteorder kommt noch die
Unsicherheit, ob der Compiler int mit 16 oder 32 Bit annimmt. Bei
big-endian und 32Bit kommt da ziemlich überraschendes bei raus und du
findest deine Konfigurationsbits niemals wieder...

Bei der Schiebevariante kannst du immer sicher sagen, wo jedes Bit aus
deinem Konfigurationsregister landet.

Und von wegen Eleganz:

int get_config_reg()
{
  union
  { unsigned char b[ sizeof(int) ];
    int           i;
  } tmp;

  tmp.b[0] = read_spi();    // Lies erstes Byte vom SPI-Bus
  tmp.b[1] = read_spi();    // Lies zweites Byte vom SPI-Bus

  return tmp.i;
}

Kann man auch machen, kommt aber u.U. der gleiche Murx bei raus.

Gruss,
Baku
Gast #281804
Lesenswert?

Ja okay, die Schieberichtung war falsch herum und auch die
Integer-Definition habe ich bei mir im konkreten Programm auf unsigned
int.

Auf jeden Fall lag ich mit meiner Meinung richtig, dass Variante 1
ziemlich unsicher ist und ich davon im Bereich
Mikrocontrollerprogrammierung wohl besser die Finger lassen werde.

Danke für eure Meinungen.

Gruß
Thomas

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren