Search code examples
cgccbit-manipulationcross-compilingbit-fields

How to access bits in mapped memory?


Imagine! I receive 8 bytes as uint8_t data[8] from an IO interface which processes some house automation. These eight bytes need to be interpreted as fallows:

Memory interpretion

How do I access the right bits? I thought now about two solutions:

Solution 1: Defining a struct with some bitfields:

struct data_s {
  uint8_t LightSwitch0:1;
  uint8_t LightSwitch1:1;
  uint8_t LightSwitch2:1;
  uint8_t LightSwitch3:1;
  uint8_t Brightness:4;
  uint8_t Dimmer0;
  uint8_t Dimmer1;
  uint8_t Dimmer2;
  uint8_t Dimmer3;
  uint8_t Dimmer4;
  uint8_t DoorSwitch:1;
  uint8_t Spare:7;
}

Next I could simply do a cast and access the structs members according to this example:

data_s *foo = data;
foo->LightSwtich1 = FALSE;
foo->Brightness = 7;
//...

Solution 2: Use bitmasks:

data[0] |= 0x02;
data[0] = (data[0] & 0x0F) | (( 7 << 4 ) & 0xF0)
//...

Well I know bitfields are compiler depended and are not that portable. Thats why the Solution 2 is often preferred. Nevertheless solution 2 looks much more complicated and is harder to read. Further more it would be much work if the mapped memory has more than eight bytes.

Do I really need to do the solution2? Or I could use the #pragma reverse_bitfields on directive to improve the portability of the solution 1? Can I use solution 1 if the code is build just for one target with a cross compiler?

So my question is: How should I access the right bits?


Solution

  • The degree to which bitfields are compiler-dependent has been somewhat exaggerated. They mostly get weird when you use them in a weird way.

    In your example, all fields are unsigned and have the same storage type, none of them cross a storage boundary, and there are no unused bits. There is the issue of ordering bits (as you've seen), but in recent years everyone's settled on the same ordering. As far as I know, no current compiler will produce anything not compatible with the IO data you're getting.