📚 Bitwise Operations in C for Microcontroller Programming
Bitwise operations are essential for low-level programming, especially when working with microcontrollers and GPIO. They allow precise control over individual bits in registers.
🎯 Learning Objectives
By the end of this lesson, students should be able to:
- Itentify the Bitwise Operators and how they are used in the C language
- Understand how to SET, CLEAR, MASK, and TOGGLE a bit
- Successfully use bit-shifting for efficient code statements
1️⃣ Core Bitwise Operators
| Operator | Symbol | Description | Example |
|---|---|---|---|
| AND | & |
Sets each bit to 1 if both bits are 1 | 5 & 3 = 1 |
| OR | | |
Sets each bit to 1 if at least one bit is 1 | 5 | 3 = 7 |
| XOR | ^ |
Sets each bit to 1 if only one bit is 1 | 5 ^ 3 = 6 |
| NOT | ~ |
Inverts all bits | ~5 = -6 |
| Left Shift | << |
Shifts bits left, filling with 0s | 4 << 1 = 8 |
| Right Shift | >> |
Shifts bits right, discarding bits | 4 >> 1 = 2 |
2️⃣ Bit Manipulation Techniques
- These are commonly used to control GPIO pins or manage flags.
- Let’s pretend we want to control and monitor bits in an 8-bit register called GPIOA.
Objectives
- We want to be able to write one specific bit as “0” or “1’” without modifying the rest
- We want to be able to read one specific bit to know if is either “1” or “0”
GPIOA
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Terminology
- Writing a “1” to a specific bit leaving the rest intact is called SET
- Writing a “0” to a specific bit leaving the rest intact is called CLEAR
-
Checking the status of a bit to know if itg’s either “1” or “0” is called BIT MASKING
- Assuming we have previously defined this mask:
const uint8_t BIT5 = 0b00100000; // and 8-bit mask that has a "1" only in b5
3️⃣ Relevant Manipulation Operations
✅ Set a Bit (write a 1)
Turn ON a specific bit using OR:
GPIOA |= BIT5; // Set BIT5, GPIOA = GPIOA | BIT5
❌ Clear a Bit (write a 0)
Turn OFF a specific bit using AND with NOT
GPIOA &= ~BIT5; // Clear BIT5, GPIOA = GPIOA & ~BIT5
🔁 Toggle a Bit (chanmge 1 to 0 and vice-versa)
Flip the state of a bit using XOR:
GPIOA ^= BIT5; // Toggle BIT5, GPIOA = GPIOA ^ BIT5
🔍 Mask a Bit (test a bit)
Check if a bit is HIGH using AND:
if (GPIOA & BIT5)
{//BIT5 is HIGH (1)
//Do something
}
else
{//BIT5 is LOW (0)
//Do something
}
4️⃣ Shift Operations
Left Shift (<<)
uint8_t mask = 1 << 5; // Creates 0b00100000
//Example
const uint8_t FLAG_READY (1 << 0) //0b00000001
const uint8_t FLAG_ERROR (1 << 1) //0b00000010
uint8_t status = 0; //variable to check status
status |= FLAG_READY; //SET FLAG_READY bit
status &= ~FLAG_ERROR; //CLEAR FLAG_ERROR bit
if (status & FLAG_READY)
{//Flag ready is SET
// Proceed with operation
}
🎯Practical Activity
- Using the virtual console on Segger create a simple program that reads a positive integer from the console and saves it into a 16-bit unsigned int variable
- Using bit-tmasking and bit-shifting operations display the number in binary (16-bit) on the debug console
- Remember to read a number from the virtual console you need to use ‘scanf’ and to print it ‘printf’ with the proper string formatter.