There are many ways to access single bits in C. This first one is my favorite method.
First, lets define some macros to help manipulate single bits:
That allready looks pretty handy, but you still need to remember where each flag is located and what the purpose of the flag is.
With couple of more macros and defines we can hide this little detail from the user and make our code more readable, flexible and more safe.
The above method gives you the freedom to locate individual flags where ever you want.. even in a general purpose register for efficient access.
It also lets you change the flag location without modifying every line of code where you access the bit.
One other method is to use structs and bitfields, but this is not as flexible as the previous method.
I tested these methods with 8 bit AVR (GCC) and all the methods produced exactly same results in simple test cases. Register variables of course were much more efficient than flags in sram.
First, lets define some macros to help manipulate single bits:
C:
/* Bit Operation macros */
#define sbi(b,n) ((b) |= (1<<(n))) /* Set bit number n in byte b */
#define cbi(b,n) ((b) &= (~(1<<(n)))) /* Clear bit number n in byte b */
#define fbi(b,n) ((b) ^= (1<<(n))) /* Flip bit number n in byte b */
#define rbi(b,n) ((b) & (1<<(n))) /* Read bit number n in byte b */
/* Examples */
volatile uint8_t flags;
sbi(flags,0); /* set bit 0 in 'flags' */
cbi(flags,3); /* clear bit 3 in 'flags' */
/* Test whether bit 0 is set */
if(rbi(flags,0)) { /*...*/ }
That allready looks pretty handy, but you still need to remember where each flag is located and what the purpose of the flag is.
With couple of more macros and defines we can hide this little detail from the user and make our code more readable, flexible and more safe.
C:
#define set_flag(combo) sbi(combo)
#define clear_flag(combo) cbi(combo)
#define read_flag(combo) rbi(combo)
/* Define your flags in the format: (variable),(bit_number) */
#define FLAG_SRAM (sram_flags),(0)
#define FLAG_REFRESH (sram_flags),(1)
#define FLAG_REGISTER (GPIOR0),(0)
#define FLAG_ERROR (GPIOR0),(1)
#define FLAG_INTERRUPT (GPIOR0),(3)
/* Now our code starts to look very readable, making comments almost useless and redundant */
volatile uint8_t sram_flags;
set_flag(FLAG_SRAM) /* set sram flag */
clear_flag(FLAG_ERROR); /* clear error flag */
/* Test whether error flag is set */
if(read_flag(FLAG_ERROR)) { /*...*/ }
/* You can make those if-statements even more readable by defining */
#define flag_is_set(combo) rbi(combo)
#define flag_is_clear(combo) (!rbi(combo))
if(flag_is_set(FLAG_SRAM)) { /*...*/ }
if(flag_is_clear(FLAG_SRAM)) { /*...*/ } /* That is almost like reading plain english. */
The above method gives you the freedom to locate individual flags where ever you want.. even in a general purpose register for efficient access.
It also lets you change the flag location without modifying every line of code where you access the bit.
One other method is to use structs and bitfields, but this is not as flexible as the previous method.
C:
/* Define structure with 8 one bit bitfields */
struct bits
{
uint8_t bit0:1;
uint8_t bit1:1;
uint8_t bit2:1;
uint8_t bit3:1;
uint8_t bit4:1;
uint8_t bit5:1;
uint8_t bit6:1;
uint8_t bit7:1;
};
/* Now, we could use this structure straight as a variable like this */
volatile struct bits flags;
flags.bit0 = 1; /* set bit 0 in 'flags' */
flags.bit3 = 0; /* clear bit 3 in 'flags' */
/* Test whether bit 0 is set */
if(flags.bit0) { /*...*/ }
/* Or, we could map the structure to some register or variable for extra flexibility and performanse */
#define flags_bit0 (((volatile struct bits*)&flags)->bit0)
#define flags_bit1 (((volatile struct bits*)&flags)->bit1)
#define flags_bit2 (((volatile struct bits*)&flags)->bit2)
#define flags_bit3 (((volatile struct bits*)&flags)->bit3)
#define flags_bit4 (((volatile struct bits*)&flags)->bit4)
#define flags_bit5 (((volatile struct bits*)&flags)->bit5)
#define flags_bit6 (((volatile struct bits*)&flags)->bit6)
#define flags_bit7 (((volatile struct bits*)(0x1E))->bit7) /* This bit is mapped in register (memory address 0x1E) for extra performance */
volatile uint8_t flags;
flags_bit0 = 1; /* set bit 0 in 'flags' */
flags_bit3 = 0; /* clear bit 3 in 'flags' */
/* Test whether bit 0 is set */
if(flags_bit0) { /*...*/ }
I tested these methods with 8 bit AVR (GCC) and all the methods produced exactly same results in simple test cases. Register variables of course were much more efficient than flags in sram.
Last edited: