Electronic – Most efficient way of creating a bool array in C – AVR

avrcembeddedmicrocontrolleroptimization

I'mĀ using an Atmega32 to read various sensors using its ADC.

Using digital logic and a few multiplexers, I've multiplexed the entirety of PORTA, using PA0:6 for address and PA7 as the input. Therefore, I can have a maximum of 128 inputs from just a single PORT.

Now, since the user will be operating the system via a computer (using RS232), it'll be possible to add or remove sensors, so the system must keep track of which addresses are free to add sensors and which addresses to read from.

I was thinking of using a 128 bit boolean array as flags to indicate if there is a sensor at a certain address.

While C doesn't have native support for single bit variables, it is possible to use bitfields to pack together up to 8 "bool variables" into a single unsigned char.

So, I've created the following struct:

typedef struct bool_s{
      uint8_t bit1:1;
}bool_t;

Then, I created a array of type bool_t with a size of 128, hoping that everything would be nicely packed together, but sizeof() tells me that the array size is 128 bytes instead of the 16 bytes I was expecting.

Technically, I could create a single struct with 128 1 bit variables:

typedef struct Flag_s{
      uint8_t F1:1;
      uint8_t F2:1;
      uint8_t F3:1;
      [...]
      uint8_t F128:1;
}Flag_t;

The problem with this approach is that while it does reduce memory usage, it's not very practical to use and it occupies way too much real estate in the code.

Is there any easy way to create a huge amount of flags or am I asking for too much? I mean, it's not like saving 112 bytes is going to make a big difference when you have 2K available, but what if I needed even more flags?

Best Answer

Bit fields don't work like that. Cventu's answer shows a correct way to use them, but in this case, I recommend avoiding them altogether.

Instead, create an array of 8-bit values and use shifting and masking to access it:

uint8_t flags[16];

//Initialize the flags
void init_flags(void)
{
    for (i = 0; i < 16; i++)
    {
        flags[i] = 0x00;
    }
}

//Set one flag based on the address
void set_flag(uint8_t address)
{
    flags[address/8] |= 0x1 << (address % 8);
}

//Clear one flag based on the address
void clear_flag(uint8_t address)
{
    flags[address/8] &= ~(0x1 << (address % 8));
}

//Check whether a flag is set
bool get_flag_state(uint8_t address)
{
    return (flags[address/8] & (0x1 << (address % 0x8))) != 0x00;
}

This is probably what the compiler will do with bit field accesses anyway, and it's easier to work with. Some optimizers are bad at optimizing bit fields, so the compiler might even do worse. All compilers I've seen turn division and modulus by a constant power of two into right-shift and AND instructions. You can use those operations directly if you're feeling paranoid:

flags[address>>3] |= 0x1 << (address & 0x7);

Bit fields are more like structures than arrays. In my experience, they're only useful for things that have names, like register fields.