Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Defining and using Bit Flags in C

Status
Not open for further replies.

misterT

Well-Known Member
Most Helpful Member
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:
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:
I think the traditional approach with masks is more flexible. You can set/clear/toggle several bits at a time.

If you wish you can define macros with bit numbers:

C:
#define BIT0 0x0001
#define BIT1 0x0002
#define BIT2 0x0004

Then you can operate several bits at a time:

C:
x ^= BIT0 | BIT2 | BIT11;

Compare to:

C:
fbi(x, 0);
fbi(x, 2);
fbi(x,11);

You can also store a combination of flags in a variable, for example:

C:
data_bits = BIT0 | BIT11 | some_additional_flags; // the variable now holds the set of flags
 
...
 
x |= data_bits; // and we can set all these flags
x &= ~data_bits; // or perhaps reset them late

Or, if you want it less cryptic:

C:
#define SET(x,b) x|=b
#define RESET(x,b) x&=~(b)
#define TOGGLE(x,b) x^=b
 
SET(x,data_bits);
TOGGLE(x,BIT2);

But, certainly, there are many ways to skin a cat.
 
True, good addition to the topic.

I was only considering situation where bits are used as flags.. that is: to flag out single event or condition. I assumed that only one bit at a time is modified. In that situation it is nice to name those flags and just use them. No need to know where they are actually located etc.. could be a variable in sram, could be a register.. All you need is the "name" of the flag. (and you can easily map a single bit anywhere you want)

When you change the state of one bit only the compiler is able to use the efficient SBI or CLI instruction. When you write many bits at a time then the compiler may not optimize properly.. don't know if any compiler is smart enough to use two SBI instructions when you say something like: x |= 0x03

Edit: Actually, if compiler uses two SBI instructions to do "x |= 0x03" it would be (horribly) wrong.
If simultaneous update is not important, then setting those bits one at a time can be more efficient. It can also be less efficient.. haha.
 
Last edited:
I did some tests.. Setting multiple bits in SRAM variable and in register variable.

Code:
;/* SRAM variables */

;/* Setting 3 bits in sram at the same time */
    ;sram |= BIT0 | BIT1 | BIT2;
00000052  LDS R24,0x0100        Load direct from data space
00000054  ORI R24,0x07          Logical OR with immediate
00000055  STS 0x0100,R24        Store direct to data space

;/* Setting 3 bits in sram one at a time */
    ;set_flag(SRAM0);
00000057  LDS R24,0x0100        Load direct from data space
00000059  ORI R24,0x01          Logical OR with immediate
0000005A  STS 0x0100,R24        Store direct to data space
    ;set_flag(SRAM1);
0000005C  LDS R24,0x0100        Load direct from data space
0000005E  ORI R24,0x02          Logical OR with immediate
0000005F  STS 0x0100,R24        Store direct to data space
    ;set_flag(SRAM2);
00000061  LDS R24,0x0100        Load direct from data space
00000063  ORI R24,0x04          Logical OR with immediate
00000064  STS 0x0100,R24        Store direct to data space


;/* Register variables */

;/* Setting 3 bits in a register at the same time */
    ;GPIOR0 |= BIT0 | BIT1 | BIT2;
00000066  IN R24,0x1E         In from I/O location
00000067  ORI R24,0x07        Logical OR with immediate
00000068  OUT 0x1E,R24        Out to I/O location

;/* Setting 3 bits in a register one at a time */
    ;set_flag(REG0);
00000069  SBI 0x1E,0        Set bit in I/O register
    ;set_flag(REG1);
0000006A  SBI 0x1E,1        Set bit in I/O register
    ;set_flag(REG2);
0000006B  SBI 0x1E,2        Set bit in I/O register

This is SRAM flag vs. register flag when you have those efficient instructions (SBI, CBI, SBIC, SBIS) available.
Code:
    ;if(read_flag(REG0)) { clear_flag(REG0); }
00000052  SBIC 0x1E,0        Skip if bit in I/O register cleared
00000053  CBI 0x1E,0         Clear bit in I/O register


    ;if(read_flag(SRAM0)) { clear_flag(SRAM0); }
00000052  LDS R24,0x0100        Load direct from data space
00000054  SBRS R24,0            Skip if bit in register set
00000055  RJMP PC+0x0006        Relative jump
00000056  LDS R24,0x0100        Load direct from data space
00000058  ANDI R24,0xFE         Logical AND with immediate
00000059  STS 0x0100,R24        Store direct to data space
 
Last edited:
With SRAM, it could've optimized three flag sets into one. I actually expected the code to be exact the same in both cases. I guess it depends on the compiler.

With GPIO, the possibility to optimize depends on the number of bits being set. If you set 1 or 2 bits, then it's better to use SBI. If you need to set 3 bits, it's three instructions either way, but SBI is still preferable because it doesn't use any working register. If you need to set 4 or more bits, ORI is better because it's only 3 instructions. I wonder if it would figure out to use two SBI instructions instead of ORI if you would ask it to set exactly 2 bits, e.g. "GPIOR0 |= 3"?
 
With SRAM, it could've optimized three flag sets into one. I actually expected the code to be exact the same in both cases. I guess it depends on the compiler.
In AVR the SBI etc. instructions are only available to memory locations 0xFF and below. So it does depend on the hardware and the compiler.

If you mean that it could combine the individual flag sets into one ORI.. I think that would be wrong to optimize like that. What if you intend to set the flags one after the other.. not all at once.
 
If you mean that it could combine the individual flag sets into one ORI.. I think that would be wrong to optimize like that. What if you intend to set the flags one after the other.. not all at once.

Yes, you're right. With I/O it could be important to set flags in some order. For example, in PICs you often must set all bits in a module register before you set the "enable" bit, otherwise it gets enabled with old bits.

However, with RAM locations these two should be equivalent:

C:
x |= 7;

C:
x |= 1;
x |= 2;
x |= 4;
 
However, with RAM locations these two should be equivalent:

C:
x |= 7;

C:
x |= 1;
x |= 2;
x |= 4;

I think it is wrong for a compiler to make assumptions about the order you intend to do things. And those two examples above are different.

Should this:
C:
x |= 1;
x |= 2;
x |= 4;
Compile to exactly same asm instructions as this:
C:
x |= 1;
x |= 4;
x |= 2;
I think not. No matter where x is located.
 
Last edited:
I think it is wrong for a compiler to make assumptions about the order you intend to do things.

Lot of optimization is done that way. This only matters if if these variables may be accessed from other threads or ISRs. If that's a problem, then these variables can be defined as volatile, which tells the compiler to avoid this sort of optimization.

For example:

C:
for (i = 0; i < n; i++) {
   x[i] = a + b;
}

This could be optimized as:

C:
temp = a + b;
for (i = 0; i < n; i++) {
   x[i] = temp;
}

This makes the code faster without any side effects. Of course, if "a" or "b" is volatile (e.g. an SFR, or a variable modifiable from an ISR), such optimization cannot be done. But otherwise, you won't even notice uless you look at the disassembly.

Moreover, it's perfectly perfect for the compiler to cast away "i" completely and use some fast way to fill the array with a fixed value.
 
Here's an example with GNU C

C:
#include <stdio.h>
int x = 0;
 
void p1() {
  x |= 7;
}
 
void p2() {
  x |= 1;
  x |= 2;
  x |= 4;
}
 
void main() {
 
p1();
p2();
 
printf("%d",x);
 
}

Code:
#cc b.c -o b -O2 --save-temps

Code:
    .file    "b.c"
    .text
    .p2align 4,,15
.globl p1
    .type    p1, @function
p1:
    pushl    %ebp
    movl    %esp, %ebp
    orl    $7, x
    popl    %ebp
    ret
    .size    p1, .-p1
    .p2align 4,,15
.globl p2
    .type    p2, @function
p2:
    pushl    %ebp
    movl    %esp, %ebp
    orl    $7, x
    popl    %ebp
    ret
    .size    p2, .-p2
    .section    .rodata
.LC0:
    .string    "%d"
    .text
    .p2align 4,,15
.globl main
    .type    main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    movl    %esp, %ebp
    pushl    %ecx
    subl    $12, %esp
    movl    x, %eax
    orl    $7, %eax
    movl    %eax, x
    pushl    %eax
    pushl    $.LC0
    call    printf
    addl    $16, %esp
    movl    -4(%ebp), %ecx
    leave
    leal    -4(%ecx), %esp
    ret
    .size    main, .-main
.globl x
    .bss
    .align 4
    .type    x, @object
    .size    x, 4
x:
    .zero    4
    .ident    "GCC: (GNU) 4.5.0"
    .section    .note.GNU-stack,"",@progbits

As you can see, they both came out the same.
 
Lot of optimization is done that way. This only matters if if these variables may be accessed from other threads or ISRs. If that's a problem, then these variables can be defined as volatile, which tells the compiler to avoid this sort of optimization.

Of course. I assume that the code is used in time critical embedded application.. This is electronics forum. If I code for PC I do not bother with bit flags..
 
Last edited:
If I code for PC I do not bother with bit flags..
I wish to add... Pics have separate access registers...

There only several locations where bit access is allowed on the humble pic... Each range is different. The only way to "bit access" a normal ram location, is via high level code...

If I create a union to access bit flags, it has to be created with a bit access register! Bit manipulation is a different thing.

RB0 = 1; is bit access..

char x;
if(x & 0x1)... is bit manipulation...
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top