Can you get a pointer to the lower byte in an endian-independent way?

c, embedded, endianness

Solution

Would the following not work?

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;

Or is `data` so volatile that the `twi_read_register` needs to write it. In that case I think you're stuck with endian dependent code.

As you pointed out below the `data` is indeed that volatile, because yet another device is reading it. So a memory mapped connection is established between two devices that may differ in endianness. This means you are stuck with endian dependent code.

You mention the struct as a workaround, but that is kind of a standard way of dealing with this.

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

On top of that you could put a `union`

union
{       uint16_as_uint8_t  as8;
        uint16_t           as16;
};

Note that the latter is in violation of the C89 standard as it is your clear intention to write one field of the `union` and read from another, which results in an unspecified value. From off C99 this is (fortunately) supported. In C89 one would use pointer conversions through `(char*)` to do this in a portable way.

Note that the above may to seem hide the endianness in a portable way, structure packing may also differ from target to target and it might still break on some target. For the above example this is unlikely, but there are some bizarre targets around. What I'm trying to say is that it is probably not possible to program portable on this device level and it might be better to accept that and strive to hide all the details in a compact target interface, so changing one header file for the target would be enough to support it. The remainder of the code then can look target independent.

Problem

I have a 16 bit variable `data`, ie: ``` volatile uint16_t data; ``` I need to populate this value based on the contents of two 8 bit registers on an external sensor. These are accessed over I2C/TWI. My TWI routine is async*, and has the signature: ``` bool twi_read_register(uint8_t sla, uint8_t reg, uint8_t *data, void (*callback)(void)); ``` This reads the value of `reg` on `sla` into `*data`, then calls `callback()`. If I knew the `uint16_t` was arranged in memory as, say, `MSB LSB`, then I could do: ``` twi_read_register(SLA, REG_MSB, (uint8_t *)&data, NULL); twi_read_register(SLA, REG_LSB, (uint8_t *)&data + 1, NULL); ``` However, I don't like baking endian dependence into my code. Is there a way to achieve this in an endian-independent way? (side note: my actual workaround at the moment involves using a struct, ie: ``` typedef struct { uint8_t msb; uint8_t lsb; } SensorReading; ``` but I'm curious if I could do it with a simple `uint16_t`) EDIT (* by async I mean split-phase, ie `*data` will be set at some point in the future, at which point the callee will be notifed via the `callback` function if requested)

Original source

Related problems