2013-04-15 6 views
5

Ich habe eine 16-Bit-Variable data, das heißt:Kann man auf endian-unabhängige Weise einen Zeiger auf das untere Byte bekommen?

volatile uint16_t data; 

Ich brauche diesen Wert auf den Inhalt von zwei 8-Bit-Register auf einem externen Sensor basierend aufzufüllen. Auf diese wird über I2C/TWI zugegriffen.

Meine TWI Routine ist async * und hat die Unterschrift:

bool twi_read_register(uint8_t sla, uint8_t reg, uint8_t *data, void (*callback)(void)); 

Dieser Wert von reg auf sla in *data liest, dann ruft callback().

Wenn ich wusste, dass die uint16_t als, sagen wir, MSB LSB im Speicher angeordnet wurde, dann könnte ich tun:

twi_read_register(SLA, REG_MSB, (uint8_t *)&data, NULL); 
twi_read_register(SLA, REG_LSB, (uint8_t *)&data + 1, NULL); 

aber ich weiß nicht wie Endian-Abhängigkeit in meinen Code zu backen. Gibt es einen Weg, dies endian-unabhängig zu erreichen?

(Nebenbemerkung: meine eigentliche Problemumgehung zur Zeit eine Struktur beinhaltet die Verwendung, das heißt:

typedef struct { 
    uint8_t msb; 
    uint8_t lsb; 
} SensorReading; 

aber ich bin neugierig, wenn ich es mit einem einfachen uint16_t tun könnte)

EDIT

(* durch async ich meine split-phase, dh *data wird zu einem bestimmten Zeitpunkt in der Zukunft festgelegt werden, an diesem Punkt wird der Angerufene über die callback Funktion notified werden, wenn angefordert)

+1

Warum wollen Sie sich beschränken wollen Wert Zeiger Zugriff auf den 16-Bit? Die meisten würden dies mit shift und add oder or oder operators machen. Wenn Sie es jedoch wirklich mit Zeigern machen möchten, könnten Sie Makros verwenden, die die Endian-Ness erkennen und kompensieren. –

+0

Nein, kann nicht ... zuverlässig. Verwenden Sie eine Maske und verschieben Sie, wenn Sie zuverlässigen und tragbaren Code möchten. –

+0

tatsächlich die Maske und die Verschiebung funktioniert auch nicht, oder? –

Antwort

4

Würde das Folgende nicht funktionieren?

uint8_t v1, v2; 
twi_read_register(SLA, REG_MSB, &v1, NULL); 
twi_read_register(SLA, REG_LSB, &v2, NULL); 
data = ((uint16_t)v1<<8)|v2; 

Oder ist data so flüchtig, dass die twi_read_register es schreiben muss. In diesem Fall denke ich, dass Sie mit endianabhängigem Code festgefahren sind.

Wie Sie unten hingewiesen data ist in der Tat so flüchtig, weil noch ein anderes Gerät es liest. Es wird also eine Speicherzuordnungsverbindung zwischen zwei Vorrichtungen hergestellt, die sich in der Endianz unterscheiden können. Dies bedeutet, dass Sie mit endianabhängigem Code festgefahren sind.

Sie erwähnen die Struktur als eine Umgehungsmöglichkeit, aber das ist eine Art Standardmethode, um damit umzugehen.

#ifdef BIGENDIAN 
typedef struct 
{  uint8_t msb, lsb; 
} uint16_as_uint8_t; 
#else 
typedef struct 
{  uint8_t lsb, msb; 
} uint16_as_uint8_t; 
#endif 

Obendrein könnten Sie eine union

union 
{  uint16_as_uint8_t as8; 
     uint16_t   as16; 
}; 

Hinweis setzen, dass diese einen Verstoß gegen das C89-Standard ist, wie es Ihre klare Absicht ist von einem Feld des union und lesen, schreiben ein anderer, der zu einem unspezifizierten Wert führt. Ab C99 wird dies (glücklicherweise) unterstützt. In C89 würde man Zeigerumwandlungen durch (char*) verwenden, um dies in einer tragbaren Weise zu tun.

Beachten Sie, dass die oben genannten scheinen die Endianness in einer tragbaren Weise zu verbergen, Struktur Verpackung kann auch von Ziel zu Ziel abweichen und es könnte immer noch auf einige Ziel zu brechen.Für das obige Beispiel ist das unwahrscheinlich, aber es gibt einige bizarre Ziele in der Nähe. Was ich versuche zu sagen, ist, dass es wahrscheinlich nicht möglich ist, portable auf dieser Geräteebene zu programmieren, und es ist vielleicht besser, das zu akzeptieren und alle Details in einer kompakten Zielschnittstelle zu verbergen, also eine Header-Datei für das Ziel zu ändern wäre genug, um es zu unterstützen. Der Rest des Codes kann dann zielunabhängig aussehen.

+1

Nicht sicher, warum Sie "volatile", obwohl. –

+0

'twi_read_register' ist asynchron/split-phase, also werden' v1' und 'v2' in Ihrem Beispiel erst in einer unbestimmten Zeit in der Zukunft gesetzt. 'Daten' haben einen undefinierten Wert. – sapi

+0

Ich verstehe. Dann könntest du das Kombinieren in "uint16_t" machen, sobald du weißt, dass die Werte bereit sind. –

0

Wie wäre es mit so etwas?

uint16_t myValue = ...; 
uint8_t LSB = (uint8_t)(myValue % 256); 
uint8_t MSB = (uint8_t)(myValue/256); 
+0

Dies ist ein gängiger Ansatz, obwohl ich die Frage gelesen habe, dass es das Problem in die andere Richtung bringt, indem es einen 16-Bit-Wert aus zwei 8-Bit-Werten auffüllt. –

+0

Ich sehe keine Schwierigkeit mit "myValue = MSB * 256 + LSB;" –

0
volatile uint16_t data; 
    uint16_t v = 1; 
    twi_read_register(SLA, REG_MSB, (uint8_t *)&data + *((uint8_t *)&v), NULL); 
    twi_read_register(SLA, REG_LSB, (uint8_t *)&data + *((uint8_t *)&v + 1), NULL);