Search code examples
cserial-communicationbit-fields

Mapping a number to bit position in C


I'm developing an programm running on Atmel AT90CAN128. Connected to this controller there are 40 devices, each with a status (on/off). As I need to report the status of each of this devices to a PC through Serial Communication, I have 40 bits, which define whether the device is on or off. In addition, the PC can turn any of this devices on or off.

So, my first attempt was to create the following struct:

typedef struct {
     unsigned char length;      //!< Data Length
     unsigned data_type;        //!< Data type
     unsigned char data[5];     //!< CAN data array 5 * 8 = 40 bits
} SERIAL_packet;

The problem with this was that the PC will send an unsigned char address telling me the device to turn on/off, so accessing the bit corresponding to that address number turned out to be rather complicated...

So I started looking for options, and I stumbled upon the C99 _Bool type. I thought, great so now I'll just create a _Bool data[40] and I can access the address bit just by indexing my data array. Turns out that in C (or C++) memory mapping needs an entire byte for addressing it. So even if I declare a _Bool the size of that _Bool will be 8 bits which is a problem (it needs to be as fast as possible so the more bits I send the slower it gets, and the PC will be specting 40 bits only) and not very efficient for the communication. So I started looking into Bit Fields, and tried the following:

typedef struct {
    unsigned char length;   //!< Data Length
    unsigned data_type;     //!< Data type
    arrayData data[40]; //!< Data array 5 bytes == 40 bits
} SERIAL_packet;

typedef struct {
    unsigned char aux : 1;
} arrayData;

And I wonder, is this going to map that data[40] into a consequent memory block with a size of 40 bits (5 bytes)?

If not, is there any obvious solution I'm missing? This doesn't seem like a very complicated thing to do (would be much simpler if there were less than 32 devices so I could use a int and just access through a bit mask).


Solution

  • Assuming the addresses you get back are in the range 0 - 39 and that a char has 8 bits, you can treat your data array as an array of bits:

    | data[0]                       | data[1]                           ...
    -----------------------------------------------------------------
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15|
    -----------------------------------------------------------------
    

    To set bit i:

    packet.data[i/8] |= (1 << (i%8));
    

    To clear bit i:

    packet.data[i/8] &= (1 << (i%8)) ^ 0xff;
    

    To read bit i:

     int flag = (packet.data[i/8] & (1 << (i%8)) != 0;