Why it’s so important to know how it works? Well, if you’re making a software for microcontrollers you have to use it in many places in the code to for example manipulate table of processor registers. It also helps to make the program smaller, much faster and allows to perform neat tricks (this isn’t always the best thing to do). Important to note is that when you’re coding for 8-bit processors then you can get 8-bit value as result of operation in a one clock cycle (analogously for 16-bit and 32-bit microprocessors).
In this article I will focus on basic bit-level operations. In the programming languages like C or ASM, operations can be performed on a bit-level using bitwise operators: AND (&), OR (|), XOR (^), NOT (~) and shift operators: SHIFT LEFT (<<), SHIFT RIGHT (>>).
BITWISE AND (&)
The bitwise AND operator is a single ampersand: &. It is just a representation of AND which does its work on the bits of the operands rather than the truth value of the operands. Bitwise binary AND does the logical AND of the bits in each position of a number in its binary form.
For instance, working with a two 8-bit operands and AND operator:
0b10111001 (185) & 0b11111000 (248) ---------- = 0b10111000 (184)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = 185 & 248; /* x = 184 */ printf("x=%hhu\n", x); return (0); }
BITWISE OR (|)
Similar to bitwise AND, bitwise OR only operates at the bit level. Its result is a 1 if one of the either bits is 1 and zero only when both bits are 0. Its symbol is | which can be called a pipe.
For instance, working with a two 8-bit operands and OR operator:
0b10111001 (185) | 0b11111000 (248) ---------- = 0b11111001 (249)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = 185 | 248; /* x = 249 */ printf("x=%hhu\n", x); return (0); }
BITWISE XOR (^)
The bitwise XOR (Exclusive-OR) performs a logical XOR function, which is equivalent to adding two bits and discarding the carry. The result is zero only when we have two zeroes or two ones. XOR can be used to toggle the bits between 1 and 0. Thus i = i ^ 1 when used in a loop toggles its values between 1 and 0
For instance, working with a two 8-bit operands and XOR operator:
0b10111001 (185) ^ 0b11111000 (248) ---------- = 0b01000001 (65)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = 185 ^ 248; /* x = 65 */ printf("x=%hhu\n", x); return (0); }
BITWISE NOT (~)
The ones’ complement (~) or the bitwise complement gets us the complement of a given number. Thus we get the bits inverted, for every bit 1 the result is bit 0 and conversely for every bit 0 we have a bit 1.
For instance, working with a one 8-bit operand and NOT operator:
~ 0b00100000 (32) ---------- = 0b11011111 (223)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = ~32; /* x = 223 */ printf("x=%hhu\n", x); return (0); }
SHIFT LEFT (<<)
The shift-left operator is the equivalent of moving all the bits of a specified number of places to the left. Note that a bitwise shift-left is the equivalent of multiplying by a power of two.
For instance, working with a two 8-bit operands and SHIFT LEFT operator:
0b00111001 (57) << 0b00000001 (1) ---------- = 0b01110010 (114)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = 57 << 1; /* x = 57 * 2 = 114 */ printf("x=%hhu\n", x); return (0); }
SHIFT RIGHT (>>)
The shift right operator is the equivalent of moving all the bits of a specified number of places to the right. Note that a bitwise shift-right will be the equivalent of integer division by 2.
For instance, working with a two 8-bit operands and SHIFT RIGHT operator:
0b00111001 (57) >> 0b00000001 (1) ---------- = 0b00011100 (28)
Example code:
#include <stdint.h> #include <stdio.h> int main(void) { uint8_t x; x = 57 >> 1; /* x = 57 / 2 = 28 */ printf("x=%hhu\n", x); return (0); }
what if I do something like this (I2C1CON & 0x1F)