MicroPython Bitwise Operators
Contents
Introduction
Operators are an essential part of just about any computing language ever devised and MicroPython is of no exception. The MicroPython operators can be broadly classified into eight groups:
- Arithmetic
- , + , * , / , % , ** , // - Assignment
= , augmented assignments e.g. += - Comparison
== , != , > , < , >= , <= - Logical
and , or , not - Identity
is , is not - Membership
in , not in - Bitwise
& , ! , ^ , ~ , << , >> - Unpacking Operators
* , **
Bitwise Operations
Computers store all kinds of information as a stream of binary digits called bits. Whether working with text, images, or videos, they all boil down to ones and zeros. MicroPython's bitwise operators allow manipulation of these individual bits of data at the most granular level.[1]
Bitwise operators can be used to implement algorithms such as compression, encryption, and error detection as well as to control physical devices such as microcontrollers. Real world devices (sensors, specific IC chips, motor controllers and so on) are connected to microcontrollers via their ports. The ports are usually 8 or 16 bits wide. These bitwise operators can be used to read or write an individual bit on a port.
Most programmers will import third party code libraries that do this for them where the controller's ports are presented as a simple abstraction in the form of a class with easy to use methods that allow high level manipulation down to the bit level. However it is still useful to know about these operators and how they work.
The Bitwise Operators
Python provides six bitwise operators that are now common in many programming languages. MicroPython supports all six of the Python bitwise operators.
When operating at the binary (base 2) level it is often useful to use integers written in a direct binary format. This is easily accomplished by prefixing with 0b to indicate that the proceeding value is in binary format.
The bin() function can be used to convert an integer in any base form to the binary format. The bin() function can also be used inside the print() function to force the output to the binary representation. The following examples illustrates these points.
Example 1
# Demonstrate the bin() function
X = 0b10101010
print(X ,'(decimal)=', bin(X), '(binary)')
Y = bin(150)
print(int(Y) ,'(decimal)=', Y, '(binary)')
Output:
170 (decimal)= 0b10101010 (binary) 150 (decimal)= 0b10010110 (binary)
Note that the print() function converts an integer to decimal (base10) by default unless the bin() function is explicitly used.
The six MicroPython bitwise operators are sumarised in Table 1.
Operator | Meaning | Example |
---|---|---|
& | Bitwise AND | a & b |
! | Bitwise OR | a & b |
^ | Bitwise XOR | a ^ b |
~ | Bitwise NOT | ~a |
<< | Bitwise left shift | a << b |
>> | Bitwise right shift | a >> b |
Bitwise AND (&)
The bitwise AND operator & performs logical conjunction on the corresponding bits of its operands. For each pair of bits occupying the same position in the two numbers, it returns a one (1) only when both bits are also one (1).
Bitwise OR (|)
The bitwise OR operator | performs logical disjunction. For each corresponding pair of bits, it returns a one (1) if at least one of them is a one (1).
Bitwise XOR (^)
The name XOR stands for “exclusive or” since it performs exclusive disjunction on the bit pairs. In other words, every bit pair must contain opposing bit values to produce a one (1).
Bitwise NOT (~)
The last of the bitwise logical operators is the bitwise NOT operator ~, which expects just one argument, making it the only unary bitwise operator. It performs logical negation on a given number by flipping all of its bits i.e. a one (1) becomes a zero (0) and a zero (0) becomes a one(1).
Bitwise Left Shift (<<)
The bitwise left shift operator << moves the bits of its first operand to the left by the number of places specified in its second operand.
It also takes care of inserting enough zero bits to fill the gap that arises on the right edge of the new bit pattern. The example below shows pictorially the result of the left shift operation: 0b10010110 << 2
Bitwise Right Shift (>>)
The bitwise right shift operator >> moves the bits of its first operand to the right by the number of places specified in its second operand. The right most bits get dropped and zero bits are inserted to fill the gap that arises on the left edge of the new bit pattern. The example below shows pictorially the result of the right shift operation: 0b10010110 >> 2
While some programmers may never use these operations they are still handy to understand as they allow easy manipulation of integers down to the bit level. The following sections will demonstrate the common practical uses of these operators.
Practical Uses for the Bitwise Operators
When writing or reverse engineering drivers for intelligent devices or components interfaced to microcontrollers, an understanding of these bit-wise operators (and operations) becomes absolutely essential.
The following will discuss how to:
- Turn On or Off individual bits in a byte
- Determine whether a given bit in a byte is On or Off
Where On = 1 and Off = 0.
(1) Turn On Individual Bits in a Byte
A mask is constructed with a one (1) where the bit is to be turned On and a zero (0) where the corresponding bit is to be left unaltered. FIG 7 shows the mask that will be used if bit 0 and bit 4 are to be turned On, with the remaining bits unaltered.
The mask is then applied to the data byte with a bitwise OR operation.
FIG 8 shows bit 0 and bit 4 turned On in the byte 0b11001100 giving 0b11011101.
Example 2
# Turn on bit 0 and bit 4 on the port.
port = 0b11001100
print('port is', bin(port))
print('Turn on bit 0 and bit 4')
mask = 0b00010001
port = port | mask
print('port is now', bin(port))
Output:
port is 0b11001100 Turn on bit 0 and bit 4 port is now 0b11011101
(2) Turn Off Individual Bits in a Byte
A mask is constructed with a zero (0) where the bit is to be turned Off and a one (1) where the corresponding bit is to be left unaltered. FIG 9 shows the mask that will be used if bit 0 and bit 4 are to be turned Off, with the remaining bits unaltered.
The mask is then applied to the data byte with a bitwise AND operation.
FIG 10 shows bit 0 and bit 4 turned Off in the byte 0b10110011 giving 0b10100010.
Example 3
# Turn off bit 0 and bit 4 on the port.
port = 0b10110011
print('port is', bin(port))
print('Turn off bit 0 and bit 4')
mask = 0b11101110
port = port & mask
print('port is now', bin(port))
Output:
port is 0b10110011 Turn off bit 0 and bit 4 port is now 0b10100010
(3) Getting the Value of a Bit
The process for obtaining the value uses a simple mask, the bitwise AND operator and the bitwise Right Shift operator. Suppose we wish to know the value of bit-n in a byte. The process is as follows:
- Create a mask with a one 1 in the bit-n position. All other bits of the mask should be a zero (0). For example if n=4 then the mask will be 0b00010000.
- Apply a bitwise AND operation with the data byte and the mask. For example if the data byte is 0b11010010 the result of this operation will be 0b00010000.
- From the result of Step 2, Right Shift the bits n times. Following through with the example about, Right Shift 4 times give a result of 0b00000001.
- The value of Step 3 will be zero (0) or one (1). This is the value of bit-n of the data byte. From the example it can be seen that the value of bit 4 for the byte 0b11010010 is 1.
# Determine the value of
# bit 4 on the port.
port = 0b11010010
print('port is', bin(port))
mask = 1 << 4
print('Mask is', bin(mask))
bit4 = port & mask
bit4 = bit4 >> 4
print('Value of bit 4 is', bit4)
Output:
port is 0b11010010 Mask is 0b10000 Value of bit 4 is 1