You can use bit fields in a struct to accomplish this. Depending on what you are doing, you may need to use #pragma pack
which will fix alignment issues.
enum class latching_comparator : bool
{
non_latching= false, //default
latching = true,
};
enum class comparator_polarity : bool
{
low = false, //default
high= true,
};
#pragma pack(push, 1)
typedef struct
{
comparator_polarity polarity : 1;
latching_comparator latching : 1;
uint8_t test : 3;
} comparator_t;
#pragma pack(pop)
This says that each enum in the struct uses only 1 bit. The test member uses 3 bits, which could be a 3 bit value or flag member, just as an example. This will only use one byte for both enums. Technically it only uses 5 bits, but 8 bit arch technically can't go lower, so you always use at least 8. If you use 9 bits in the struct, it will use 2 bytes since you move into another byte for bit 9.
std::cout << sizeof(comparitor_t) << std::endl;
Will print 1
because it only uses one byte to accomplish this.
I didn't read the documentation on the hardware you're interfacing with, but it would be best to design the entire struct around the registers you're configuring. The bit sizes will vary for each configuration in that register, but generally I write one struct for each register, manipulate the struct, then i just output the bytes of the struct straight into the communication interface, no need to hassle with bit shifting etc.
Edit: I'm feeling generous today, so here is some of the code to work with the config register of that chip (reading or writing). The register has more configurations in it, so you still need to finish it, but it gives you a direct example of what I'm talking about.
typedef enum
{
ADS115_CONFIG_COMP_QUE_ASSERT_ONE = 0b00,
ADS115_CONFIG_COMP_QUE_ASSERT_TWO = 0b01,
ADS115_CONFIG_COMP_QUE_ASSERT_FOUR = 0b10,
ADS115_CONFIG_COMP_QUE_ASSERT_DISABLE = 0b11
} ads115_config_comp_que_t;
typedef enum
{
ADS115_CONFIG_COMP_LAT_NONLATCHING = 0b0,
ADS115_CONFIG_COMP_LAT_LATCHING = 0b1
} ads115_config_comp_lat_t;
typedef enum
{
ADS115_CONFIG_COMP_POL_ACTIVE_LOW = 0b0,
ADS115_CONFIG_COMP_POL_ACTIVE_HIGH = 0b1,
} ads115_config_comp_pol_t;
typedef enum
{
ADS115_CONFIG_COMP_MODE_TRADITIONAL = 0b0,
ADS115_CONFIG_COMP_MODE_WINDOW = 0b1
} ads115_config_comp_mode_t;
typedef enum
{
ADS115_CONFIG_DR_8_SPS = 0b000,
ADS115_CONFIG_DR_16_SPS = 0b001,
ADS115_CONFIG_DR_32_SPS = 0b010,
ADS115_CONFIG_DR_64_SPS = 0b011,
ADS115_CONFIG_DR_128_SPS = 0b100,
ADS115_CONFIG_DR_250_SPS = 0b101,
ADS115_CONFIG_DR_475_SPS = 0b110,
ADS115_CONFIG_DR_860_SPS = 0b111,
} ads115_config_dr_t;
typedef enum
{
ADS115_CONFIG_MODE_CONTINUOUS = 0b0,
ADS115_CONFIG_MODE_ONESHOT = 0b1,
} ads115_config_mode_t;
#pragma pack(push, 1)
typedef struct
{
ads115_config_comp_que_t comp_que : 2;
ads115_config_comp_lat_t comp_lat : 1;
ads115_config_comp_pol_t comp_pol : 1;
ads115_config_comp_mode_t comp_mode : 1;
ads115_config_dr_t dr : 3;
ads115_config_mode_t mode : 1;
// @TODO: other configurations
} ads115_reg_config_t;
#pragma pack(pop)