Bitwise operations are mathematical transformations applied to binary numbers. Binary numbers are written in base two, which means that they are composed of a string of zeroes and ones. The numbers that most people are familiar with are written in base ten, which means that there are ten digits with each digit indicating a number of powers of ten:
10^{3} | 10^{2} | 10^{1} | 10^{0} | = | Base 10 |
1 | 0 | 2 | 4 | = | 1024 |
3 | 7 | 5 | 6 | = | 3756 |
Some of the more familiar operators in base ten are addition (symbolized as +), subtraction (symbolized as -), multiplication (symbolized in many different ways), and division (also symbolized in many different ways). Consider the operation of addition in base ten:
10^{3} | 10^{2} | 10^{1} | 10^{0} | = | Base 10 | |
1 | 0 | 2 | 4 | = | 1024 | |
+ | 3 | 7 | 5 | 6 | = | 3756 |
4 | 7 | 8 | 0 | = | 4780 |
Notice in particular how the digits in the 10^{0} column added. Adding six counts of 10^{0} with four counts of 10^{0} results in a single count of 10^{1}, which means it must be listed in the 10^{1} column. Another way of thinking about the process is to see that base ten has no single digit to represent the sum “6 + 4″; the sum modulus ten is displayed in the column instead.
The same ideas hold true in base two, but instead of using the digits 0-9, only the digits 0 and 1 are used:
2^{7} | 2^{6} | 2^{5} | 2^{4} | 2^{3} | 2^{2} | 2^{1} | 2^{0} | = | Base 10 |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | = | 166 |
1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | = | 197 |
Adding these numbers using traditional addition:
2^{8} | 2^{7} | 2^{6} | 2^{5} | 2^{4} | 2^{3} | 2^{2} | 2^{1} | 2^{0} | = | Base 10 | |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | = | 166 | ||
+ | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | = | 197 | |
1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | = | 363 |
Because there is no digit to represent “1 + 1″ in base two, the sum modulus two must be listed in the column, and the overflow sent to the next column to the left.
In the world of microcontrollers, everything is stored as either a zero or a one. Each zero or one is called a bit, and the registers on a chip can store a string of these bits. Programs may read and change these bits.
The code from the previous post used variables as placeholders for some of these binary numbers:
void adc_init() {
// sets the analog to digital converter
// for external reference (5v), single ended input ADC0
ADMUX = 0;
// enable the analogue to digital converter
// with its clock scaled to 1/128 speed
// so that the ADC clock runs at 115.2 kHz
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
// perform a conversion just to get the ADC up and running
ADCSRA |= (1<<ADSC);
}
The capitalized variables correspond to binary numbers stored in the ATmega168′s registers or other values from the chip (the registers to which ADCSRA and the other variables correspond are defined in the include files). Each variable holds a byte of information – a string of eight bits. The code also makes use of bitwise operators, in this case the ones represented by the pipe (“|
“) and angle brackets (“<<
“).
The term “bitwise operator” used in the C programming language is poorly chosen because the operations can only be applied to eight bits (an entire byte) at a time. These operations have more to do with logic than with arithmetic. Consider the bitwise AND operator:
2^{7} | 2^{6} | 2^{5} | 2^{4} | 2^{3} | 2^{2} | 2^{1} | 2^{0} | Base 10 | |||
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | & | 166 | & | |
1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | = | 197 | = | |
1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 132 |
The corresponding base ten interpretation (listed to the right) holds little meaning. But in terms of logic, the binary AND operator can be useful; a one (“true”) is output if and only if both corresponding inputs are also ones.
The bitwise OR operator is used in the sample code seen above. An example:
2^{7} | 2^{6} | 2^{5} | 2^{4} | 2^{3} | 2^{2} | 2^{1} | 2^{0} | |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | | |
1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | = |
1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
The OR operation outputs a one if either of the two inputs are ones. In computer and electrical engineering, it is more common to use the exclusive-or, or XOR operation:
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | ^ |
1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | = |
0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
With the exclusive-or, the output is a one if and only if exactly one of the inputs is also a one.
The compliment (also called the NOT operator) differs from the other bitwise operators because it is a unary operator as opposed to a binary operator, i.e. it takes a single input rather than two inputs:
~10100110 = 01011001
The final two bitwise operators are the left shift (“<<
“) and right shift (“>>
“) operators. These operations move the bits to the left and to the right, respectively, with zeroes added in the empty slots. Examples:
10100110 << 2 = 10011000
10100110 << 3 = 00110000
10100110 >> 2 = 00101001
10100110 >> 3 = 00010100
The shift operations do have a meaningful interpretation in base ten. Each left shift is equivalent to multiplying the corresponding base ten number by a factor of two, and each right shift is equivalent to dividing the corresponding base ten number by a factor of two and truncating the remainder.
With these rules in mind, it is now possible to understand the code used to initialize the analogue to digital converter:
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
In this line of code, the ADCSRA register is set to the result of a series of bitwise operations. The binary number “1″ is left shifted by the value located in the ADEN variable, and the following ones are left shifted by the values in the ADPS2, ADPS1, and ADPS0 variables. Then the OR operation is performed on all four bytes and the result is saved in the ADCSRA register.
ADCSRA |= (1<<ADSC);
Here, the one is left shifted by the value located in the ADSC variable, and then the OR output between ADCSRA and the result is saved in the ADCSRA register. This line of code could also have been written as:
ADCSRA = (1<<ADSC) | ADCSRA;
Notice that all of the bitwise operators preserve the number of bits, i.e. if the input values are eight bits then so are the output values (The 1s are automatically appended with preceding zeroes). This differs from the traditional addition performed between the binary numbers near the beginning of the post. When adding 10100110 with 11000101, the result is 101101011, a nine bit binary number. Since the registers in the chip can only hold up to eight bits (one byte), trying to add binary numbers in this manner would result in errors.