Arduino bitwise operators and bitRead, on a seven-segment digit
Beginner
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
Bitwise operators work on the separate bits of a number. & (and) keeps the bits set in both numbers, | (or) the bits set in either, ^ (xor) the bits set in one but not both, ~ (not) flips every bit, and << and >> slide the bits left or right. Arduino adds bitRead(), bitWrite(), bitSet() and bitClear() for one bit at a time.
The circuit above is a seven-segment digit wired straight to an Uno, counting from 0 to 9 once a second. Each digit is one byte with segment a in bit 0, and bitRead(DIGITS[n], s) decides whether segment s lights. Press Run and watch it count; the serial monitor prints each number as it shows it.
Below is how that byte works, all six operators with what each printed, setting and testing one bit, the shifts that run out of room, & against &&, and joining two bytes into one number.
One byte per digit
Here is the table and the function from the sketch above. 0x06 is 0b00000110 in binary: bits 1 and 2 are set, which are segments b and c, the two on the right, and that is a 1. 0x3F sets the six bits for a to f, everything but the middle bar, which is a 0. show() walks the seven bits and writes each one to its segment’s pin.
Ten bytes hold all ten digits. Ten arrays of seven HIGHs and LOWs would hold the same thing in fourteen times the space.
const byte DIGITS[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
const int SEGMENT_PINS[7] = {7, 6, 5, 11, 10, 8, 9}; // a to g
const int DOT_PIN = 4;
void show(int n) {
for (int s = 0; s < 7; s++) digitalWrite(SEGMENT_PINS[s], bitRead(DIGITS[n], s));
}The six operators
With a at 1100 and b at 1010 in binary, the sketch below printed 1000 for a & b, 1110 for a | b, 110 for a ^ b, 11000 for a << 1 and 11 for a >> 2. Shifting left by one doubles a number and shifting right by one halves it, dropping the remainder.
The numbers can be written three ways. 0b1100 is binary, 0x0C is hex and 12 is decimal, and all three are the same number to the compiler. Binary is the easiest to read when each bit means one thing, such as a segment or a switch, and hex is shorter for whole bytes: each hex digit is exactly four bits.
~a printed -13, not 11110011. The ~ worked on an int, because C++ turns a byte into an int before doing arithmetic on it, so all sixteen bits flipped and the top one made it negative. Cast the answer back, (byte)~a, and it printed 11110011.
void setup() {
Serial.begin(9600);
byte a = 0b1100;
byte b = 0b1010;
Serial.println(a & b, BIN);
Serial.println(a | b, BIN);
Serial.println(a ^ b, BIN);
Serial.println(a << 1, BIN);
Serial.println(a >> 2, BIN);
Serial.println(~a);
Serial.println((byte)~a, BIN);
}
void loop() {
}Set, clear, flip and test one bit
1 << n is a number with only bit n set, called a mask. flags |= (1 << 3) sets bit 3 and leaves the rest alone, flags &= ~(1 << 3) clears it, flags ^= (1 << 0) flips bit 0, and if (flags & (1 << 0)) tests it. The sketch below printed 1000, 1001 and 1, then "bit 0 is set".
Arduino’s own names say the same things in words. bitSet(flags, 3), bitClear(flags, 0), bitWrite(flags, 7, 1) and bitRead(flags, 3) work on one bit, and bit(5) is 32, the value of bit 5 on its own. Use whichever reads better; they build to the same code.
void setup() {
Serial.begin(9600);
byte flags = 0b00000000;
flags |= (1 << 3);
Serial.println(flags, BIN);
flags ^= (1 << 0);
Serial.println(flags, BIN);
flags &= ~(1 << 3);
Serial.println(flags, BIN);
if (flags & (1 << 0)) {
Serial.println("bit 0 is set");
}
}
void loop() {
}Shifts that run out of room
A plain number such as 1 is an int, and an int on an Uno has 16 bits. 1 << 15 printed -32768, because bit 15 is the sign bit. 1 << 16 and long big = 1 << 20 shift past the end: clang warned shift count >= width of type, and they printed 256 and 16777221, neither of them right. Storing the answer in a long does not help, because the shift has already gone wrong.
Make the 1 an unsigned long first: 1UL << 16 printed 65536. Do the same whenever a shift can pass bit 14, for a 32-bit register value or a mask for a 24-pin display.
& is not &&, and the brackets that matter
&& asks whether both sides are true; & compares bits. With x at 4, if (x & 2) was false, because 100 and 010 share no bit, and if (x && 2) was true, because neither is zero. Clang warned on the second: use of logical '&&' with constant operand. The same goes for | and ||.
== is worked out before &, so if (flags & 0b1000 == 0b1000) tests flags & 1. With flags at 1000 it printed "not set". Clang warned & has lower precedence than ==; == will be evaluated first. Put brackets around the mask: if ((flags & 0b1000) == 0b1000).
Two bytes into one number, and back
Sensors on I2C and SPI often send a reading as a high byte and a low byte. (high << 8) | low puts them back together: 0x03 and 0xE8 made 1000. value >> 8 takes the high byte out again and value & 0xFF the low one, which printed 3 and 232.
void setup() {
Serial.begin(9600);
byte high = 0x03;
byte low = 0xE8;
int value = (high << 8) | low;
Serial.println(value);
Serial.println(value >> 8);
Serial.println(value & 0xFF);
}
void loop() {
}Questions
What does & do in Arduino?
Between two numbers it is bitwise and: each bit of the answer is 1 only where both numbers have a 1. Before a variable it means "the address of", which is a different thing.
What is the difference between & and && in Arduino?
&& is logical and, true when both sides are non-zero. & compares the numbers bit by bit. 4 & 2 is 0, which is false; 4 && 2 is true.
How do I read one bit of a byte in Arduino?
bitRead(value, n) gives 0 or 1 for bit n, counting from 0 at the right. (value >> n) & 1 is the same thing written with operators.
What does 1 << n mean?
A number with only bit n set: 1 << 3 is 8. It is the mask for setting, clearing or testing bit n. Write 1UL << n when n can be 15 or more.
Keep going
Where the facts on this page come from
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.