Bitmasking Tutorial
Bit-masking is a technique to selectively modify individual bits without affecting other bits.
Contents
Bit SET
To set a bit, we need to use the OR operator. This is just like an OR logical gate you should've learnt in Digital Design course. To set a bit, you would OR a memory with a bit number and the bit number with which you will OR will end up getting set.
// Assume we want to set Bit#7 of a register called: REG
REG = REG | 0x80;
// Let's set bit#31:
REG = REG | 0x80000000;
// Let's show you the easier way:
// (1 << 31) means 1 gets shifted left 31 times to produce 0x80000000
REG = REG | (1 << 31);
// Simplify further:
REG |= (1 << 31);
// Set Bit#21 and Bit# 23:
REG |= (1 << 21) | (1 << 23);
Bit RESET
To reset a bit, the logic is similar, but instead of ORing a bit, we will AND a bit. Remember that AND gate resets a bit if you AND it with 0 so we need to use a tilde (~) to come up with the correct logic:
// Assume we want to reset Bit#7 of a register called: REG
REG = REG & 0x7F;
REG = REG & ~(0x80); // Same thing as above, but using ~ is easier
// Let's reset bit#31:
REG = REG & ~(0x80000000);
// Let's show you the easier way:
REG = REG & ~(1 << 31);
// Simplify further:
REG &= ~(1 << 31);
// Reset Bit#21 and Bit# 23:
REG &= ~( (1 << 21) | (1 << 23) );
Bit checking
Suppose you want to wait for a register's bit#7 to set:
// One way:
while( (1<<7) != (REG & (1 << 7))) {
};
// Easier way:
while( ! (REG & (1 << 7)) );
Now let's work through another example in which we want to wait until bit#9 is 0
// One way:
while(REG & (1 << 9) != 0);
// Easier way:
while(REG & (1 << 9));
GPIO Example of LPC17xx
In this example, we will work with an imaginary circuit of a switch and an LED. For a given port, the following registers will apply:
- GPIO selection: PINSEL register (not covered by this example)
- GPIO direction: DIR (direction) register
- GPIO read: IOPIN register
- GPIO write: IOPIN register
Suppose a switch is connected to GPIO Port P1.14 and an LED is connected to Port P1.15. Note that if a bit is set of FIODIR register, the pin is OUTPUT otherwise the pin is INPUT. So... 1=OUTPUT, 0=INPUT
// Set P1.14 as INPUT for the switch:
LPC_GPIO1->FIODIR &= ~(1 << 14);
// Set P1.19 as OUTPUT for the LED:
LPC_GPIO1->FIODIR |= (1 << 15);
// Read value of the switch:
if(LPC_GPIO1->FIOPIN & (1 << 14)) {
// Light up the LED:
LPC_GPIO1->FIOPIN |= (1 << 15);
}
else {
// Else turn off the LED:
LPC_GPIO1->FIOPIN &= ~(1 << 15);
}
LPC also has dedicated registers to set or reset an IOPIN with hardware AND and OR logic:
Bit-masking using Bit Structures
If you've got comfortable with bit-masking, you can also let the compiler handle bit-masking. Let's now modify our example to use bit structures:
// Declare this 1-bit structure somewhere:
typedef union
{
unsigned int full32bit;
struct {
unsigned b0 :1; unsigned b1 :1; unsigned b2 :1; unsigned b3 :1;
unsigned b4 :1; unsigned b5 :1; unsigned b6 :1; unsigned b7 :1;
unsigned b8 :1; unsigned b9 :1; unsigned b10 :1; unsigned b11 :1;
unsigned b12 :1; unsigned b13 :1; unsigned b14 :1; unsigned b15 :1;
unsigned b16 :1; unsigned b17 :1; unsigned b18 :1; unsigned b19 :1;
unsigned b20 :1; unsigned b21 :1; unsigned b22 :1; unsigned b23 :1;
unsigned b24 :1; unsigned b25 :1; unsigned b26 :1; unsigned b27 :1;
unsigned b28 :1; unsigned b29 :1; unsigned b30 :1; unsigned b31 :1;
}__attribute__((packed));
} BitStructType;
/**
* Look how simple the code becomes now:
* Compiler will read 1-bit and set 1-bit of FIOPIN1 with bit-masking.
* No need for if/else statements or the AND/OR logic.
*/
BitStructType *pFioPin1 = &(LPC_GPIO->FIOPIN1);
pFioPin1->b15 = pFioPin1->b14;
Unions
In the bit-struct example, struct was enclosed inside a union. A union is a structure whose members share memory. In the next example, you should notice that the full32bit overlaps the memory of the structure and the structure size is also 32-bits. The packed attribute is needed such that the compiler will not optimize the structure memory and it will indeed use precisely the bits you requested for each variable. The memory looks like this:
| --------- full32bit ----------- |
| byte3 | byte 2| byte 1 | byte 0 |
Modifying byte 0 will also affect full32bit member. Here is an example:
typedef union
{
unsigned int full32bit;
// This assumes Little Endian machine otherwise byte order is swapped.
struct {
unsigned int byte0 : 8; // b7 : b0
unsigned int byte1 : 8; // b15 : b8
unsigned int byte2 : 8; // b23 : b16
unsigned int byte3 : 8; // b31 : b24
}__attribute__((packed));
} BitStructType;
BitStructType var = { 0 };
var.byte0 = 1;
var.byte1 = 1;
printf("%i\n", var.full32bit); // This will print 257
var.byte0 = 0;
printf("%i\n", var.full32bit); // This will print 256