Set, Clear, Toggle and Check Bit Value in C
In the embedded system design, the system needs to check the input state and then send signals to other devices, or change the state of the outputs. Since each GPIO pin is connected to a different device (some pins are input and other pins may be output or other functions), it is not possible to check the pin state by comparing with a constant value. The setting, clearing, and toggling pins also have the same situation: constant values can not be assigned directly to the port DATA register. Because each GPIO pin is associated with the corresponding bit in the port DATA register, a bitwise operation can be used to solve these issues.
Figure 1: Example of Embedded System
Useful Pre-Definitions
Write the following definitions in a header file, such as MyDefines.h. Then includes the file in your project.
#ifndef __MYDEFINES_H #define _MYDEFINES_H #define _BIT0 (1UL << 0 ) #define _BIT1 (1UL << 1 ) #define _BIT2 (1UL << 2 ) #define _BIT3 (1UL << 3 ) #define _BIT4 (1UL << 4 ) #define _BIT5 (1UL << 5 ) #define _BIT6 (1UL << 6 ) #define _BIT7 (1UL << 7 ) #define _BIT8 (1UL << 8 ) #define _BIT9 (1UL << 9 ) #define _BIT10 (1UL << 10) #define _BIT11 (1UL << 11) #define _BIT12 (1UL << 12) #define _BIT13 (1UL << 13) #define _BIT14 (1UL << 14) #define _BIT15 (1UL << 15) #endif
Setting bits to 1
Figure 2: Truth Table for Bitwise-OR
\(n: = 0\;or\;1\quad \Rightarrow \left\{ \begin{array}{l} n|0 = n & \to {\rm{same as }}n{\rm{ value}}\\ n|1 = 1 & \to {\rm{always be 1}} \end{array} \right.\)
To turn certain bits on, the bitwise-OR operation can be used, following the principle that n OR 1 = 1
and n OR 0 = n
. Therefore, to make sure a bit is on, OR
can be used with a 1
. To leave a bit unchanged, OR
is used with a 0
.
Example: Setting 1 on bit 5 and 3, the other bits unchanged.
To light on the lamp which is connected to PB2, bit 2 of the Port B DATA register needs to be set to 1. The following expression shows how to set bit 2 to 1:
PB = PB | _BIT2;
PB |= _BIT2;
Clearing bits to 0
Figure 4: Truth Table for Bitwise-AND
\(n:0\;or\;1\quad \Rightarrow \left\{ \begin{array}{l} n\& 0 = 0 & {\rm{always be 0}} & \\ n\& 1 = n & {\rm{same as }}n{\rm{ value}} \end{array} \right.\)
From the above results, you can find that when a bit is AND
ed with a 0
, the result is always 0, i.e. n AND 0 = 0
. To leave the other bits as they were original, they can be AND
ed with 1
, since n AND 1 = n
.
Example: Clearing on bits 5 and 3, the other bits unchanged.
To turn off the lamp that is connected to PB2, bit 2 of the Port B DATA register needs to be clear to 0. The following expression shows how to clear bit 2 to 0:
PB = PB & ~_BIT2;
PB &= ~_BIT2;
Toggling bit values
Figure 6: Truth Table for Bitwise-XOR
\(n:0\;or\;1\quad \Rightarrow \left\{ \begin{array}{l} n\^0 = n & {\rm{same as }}n{\rm{ value}} & \\ n\^1 = \bar n & {\rm{invertted }}n{\rm{ value}} \end{array} \right.\)
We just discussed how to turn bits on and turn bits off, but not both at once. Sometimes it does not really what the value is, but it must be made the opposite of what it currently is. In other words, toggling means that turns the bit on if it is off, and turning it off if it is on. This can be implemented using the XOR
(exclusive or) operation. Therefore inversion of the values of bits is done by XOR
ing them with a 1
. The other unchanged bits can be XOR
ed with 0
, because n OXR 0 = n
, just like an OR
.
Example: Inverting on bits 5 and 3, the other bits unchanged.
To toggle the lamp which is connected to PB2, bit 2 of the Port B DATA register needs to be inverted. The following expression shows how to invert bit 2:
PB = PB ^ _BIT2;
PB ^= _BIT2;
Set bits to a value
To write arbitrary 1s and 0s to a subset of bits: first, write 0s to that subset, then set the high bits.
Example: Set register[4:2] to (010)2
register = (register & ~(_BIT4 | _BIT3 | _BIT2)) | (_BIT3);
Checking bit value
So far, you have already learned how to change the bit values. But now, suppose you want to determine the particular bit that is set or not. The following test does not necessarily work:
if ( PB == _BIT2) // no good - testing bit 2
That is because even if the corresponding bit in PB is set to 1, other bits might also be set to 1. The equality above is true only when the corresponding bit is 1.
The Bitwise-AND operator (&) can be used to check the bit state. First, use bitwise-AND to AND
PB with a bitmask value (The corresponding bit in bitmask must be set to 1, leaving other bits to 0). This produces a value that is 0 in all the other bit positions because 0 AND any value is 0
. Only the bits corresponding to the 1 is left unchanged because 1 AND any value is that value
. Therefore, if the result is zero, then the bit was off. But, if the result is any other value, then the bit was on. Figure 8 illustrates how this operation works.
Figure 8: How an & (and) Mask Works
The proper test is this:
if ( (PB & _BIT2) == _BIT2) // testing if bit 2 equals 1 ?
For one-bit testing, programmers often simplify this test to the following:
if (PB & _BIT2) // testing if bit 2 equals 1 ?
if ( (PB & _BIT2) == 0) // testing if bit 2 equals 0 ?
For one-bit testing, programmers often simplify this test to the following:
if ( !(PB & _BIT2)) // testing if bit 2 equals 0 ?
For multiple-bit testing, you need to identify the correct value to compare with the result:
- Checking both PB0 & PB1 are 1
if ((PB & (_BIT2 | _BIT0)) == (_BIT2 | _BIT0)) // testing bit 2 and bit 0 (both are 1)
- Checking both PB0 & PB1 are 0
if ( !(PB & (_BIT2 | _BIT0))) // testing bit 2 and bit 0 (both are 0)
Reading a bit value
To read the current state of a pin into a variable, use the bit-wise AND operator to filter the corresponding bit. Since the pin state is a logic value, only two types of values, either logic 0 or logic 1; so the data type of the variable used to store the pin state can be set to a Boolean variable.
For example, the SW1 is connected to PB2. To read PB2 state into sw1 variable:
bool sw1;
sw1 = PB2 & _BIT2;
To check the sw1 state (connected on PB2):
if (sw1 == true){
.... // sw1 is logic 1
}
if (sw1 == false){
.... // sw1 is logic 0
}
Bit Access Functions
The following functions get or set a particular bit of a variable that can make programs easier to read.
Set Any Bit to Either 0 or 1
Program 1: SetBit() function to set a particular bot to 0 or 1
#include <stdint.h> #include <stdbool.h> // x: 8-bit value. k: bit position to set, range is 0-7. b: set bit to this, either 1 or 0 uint8_t SetBit(uint8_t x, uint8_t char k, bool b) { return (b ? (x | (0x01 << k)) : (x & ~(0x01 << k)) ); // Set bit to 1 Set bit to 0 } //------------------------------------------------------------------------------ // x: 16-bit value. k: bit position to set, range is 0-15. b: set bit to this, either 1 or 0 uint16_t SetBit16(uint16_t x, uint8_t char k, bool b) { return (b ? (x | (1UL << k)) : (x & ~(1UL << k)) ); // Set bit to 1 Set bit to 0 }
Get the Value of a Particular bit in an Integer Variable
Program 2: GetBit() function to get a particular bit
#include <stdint.h> #include <stdbool.h> // x: 8-bit value. k: bit position to set, range is 0-7. bool GetBit(uint8_t x, uint8_t k) { return ((x & (0x01 << k)) != 0); } //------------------------------------------------------------------------------ // x: 16-bit value. k: bit position to set, range is 0-15. bool GetBit16(uint16_t x, uint8_t k) { return ((x & (1UL << k)) != 0); }