2013-04-15 6 views
5

Ho un 16 bit variabile data, cioè:È possibile ottenere un puntatore al byte inferiore in modo indipendente dall'endian?

volatile uint16_t data; 

ho bisogno per completare questo valore in base al contenuto di due registri a 8 bit su un sensore esterno. Questi sono accessibili tramite I2C/TWI.

mio TWI di routine è asincrona *, ed ha la firma:

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

Questa legge il valore di reg su sla in *data, poi chiama callback().

se conoscessi la uint16_t è stato organizzato in memoria come, ad esempio, MSB LSB, quindi ho potuto fare:

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

Tuttavia, non mi piace la cottura dipendenza endian nel mio codice. C'è un modo per raggiungere questo in modo indipendente dall'endian?

(nota a margine: la mia soluzione effettiva al momento comporta l'uso di una struttura, cioè:

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

ma io sono curioso di sapere se ho potuto farlo con un semplice uint16_t)

EDIT

(* per asincrono intendo split-phase, ovvero *data verrà impostato in futuro, a quel punto il callee verrà notificato tramite la funzione callback se richiesto)

+1

Perché vuoi per vincolare a voi stessi di puntatore accesso al valore a 16 bit? La maggior parte lo farebbe con shift e add o o operatori. Tuttavia, se si vuole veramente farlo con i puntatori, è possibile utilizzare macro che rilevano e compensano l'endianità. –

+0

No non puoi ... affidabile. Utilizzare una maschera e spostare se si desidera codice affidabile e portatile. –

+0

in realtà neanche la maschera e il turno funzionano, vero? –

risposta

4

Potrebbe non funzionare?

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; 

O è data così volatile che la twi_read_register ha bisogno di scriverlo. In quel caso penso che tu sia bloccato con il codice dipendente da endian.

Come hai indicato sotto lo data è davvero così volatile, perché un altro dispositivo lo sta leggendo. Quindi viene stabilita una connessione mappata in memoria tra due dispositivi che possono differire in endianness. Ciò significa che sei bloccato con il codice dipendente da endian.

Si cita la struttura come soluzione alternativa, ma è un modo standard per gestirlo.

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

In cima a quello che si potrebbe mettere un union

union 
{  uint16_as_uint8_t as8; 
     uint16_t   as16; 
}; 

Nota che quest'ultimo è in violazione dello standard C89 come è vostra intenzione chiara di scrivere un campo del union e leggere un altro, che si traduce in un valore non specificato. Da fuori C99 questo è (fortunatamente) supportato. In C89 si utilizzano le conversioni puntatore tramite (char*) per eseguire questa operazione in modo portatile.

Si noti che quanto sopra può sembrare nascondere il endianness in un modo portabile, l'impacchettamento della struttura può anche differire da un obiettivo all'altro e potrebbe comunque rompersi su qualche obiettivo.Per l'esempio sopra questo è improbabile, ma ci sono alcuni obiettivi bizzarri in giro. Quello che sto cercando di dire è che probabilmente non è possibile programmare portatile su questo livello di dispositivo e potrebbe essere meglio accettarlo e sforzarsi di nascondere tutti i dettagli in un'interfaccia di destinazione compatta, quindi cambiare un file di intestazione per il target basterebbe sostenerlo. Il resto del codice può quindi sembrare indipendente dal target.

+1

Non sono sicuro del motivo per cui stai citando 'volatile', però. –

+0

'twi_read_register' è asincrono/split-phase, quindi' v1' e 'v2' nell'esempio non saranno impostati fino a un tempo indeterminato in futuro. 'data' avrà un valore indefinito. – sapi

+0

Vedo. Allora forse potresti fare la combinazione in 'uint16_t' una volta che sai che i valori sono pronti. –

0

Che ne dici di qualcosa del genere?

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

Questo è un approccio comune, sebbene la mia lettura della domanda sia che pone il problema nella direzione opposta, popolando un valore a 16 bit da due a 8 bit. –

+0

Non vedo alcuna difficoltà con "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); 
Problemi correlati