Skip to content

🔢 Bit Manipulation — Control Hardware Registers

Hardware registers được điều khiển bằng từng bit. Mastering bitwise operators là kỹ năng bắt buộc!

Analogy: Bảng công tắc đèn

┌─────────────────────────────────────────────────────────────────┐
│                    LIGHT SWITCH PANEL                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Một register 8-bit giống như bảng 8 công tắc:                 │
│                                                                 │
│   ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐            │
│   │  7  │  6  │  5  │  4  │  3  │  2  │  1  │  0  │  ← Bit #   │
│   ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤            │
│   │ OFF │ ON  │ OFF │ OFF │ ON  │ OFF │ ON  │ OFF │            │
│   │  0  │  1  │  0  │  0  │  1  │  0  │  1  │  0  │  = 0x4A    │
│   └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘            │
│                                                                 │
│   Bạn cần biết cách:                                            │
│   • Bật 1 công tắc (SET bit)                                    │
│   • Tắt 1 công tắc (CLEAR bit)                                  │
│   • Đảo trạng thái (TOGGLE bit)                                 │
│   • Kiểm tra trạng thái (READ bit)                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Bitwise Operators

OperatorNameDescription
&ANDCả hai bit = 1 → kết quả = 1
|ORÍt nhất một bit = 1 → kết quả = 1
^XORHai bit khác nhau → kết quả = 1
~NOTĐảo tất cả bits
<<Left ShiftDịch trái n vị trí
>>Right ShiftDịch phải n vị trí

Truth Tables

         AND (&)              OR (|)              XOR (^)
      ┌───┬───┬───┐       ┌───┬───┬───┐       ┌───┬───┬───┐
      │ A │ B │ R │       │ A │ B │ R │       │ A │ B │ R │
      ├───┼───┼───┤       ├───┼───┼───┤       ├───┼───┼───┤
      │ 0 │ 0 │ 0 │       │ 0 │ 0 │ 0 │       │ 0 │ 0 │ 0 │
      │ 0 │ 1 │ 0 │       │ 0 │ 1 │ 1 │       │ 0 │ 1 │ 1 │
      │ 1 │ 0 │ 0 │       │ 1 │ 0 │ 1 │       │ 1 │ 0 │ 1 │
      │ 1 │ 1 │ 1 │       │ 1 │ 1 │ 1 │       │ 1 │ 1 │ 0 │
      └───┴───┴───┘       └───┴───┴───┘       └───┴───┴───┘

Core Operations

1. SET Bit (Bật)

cpp
// SET bit n to 1
// Formula: reg |= (1 << n)

uint8_t reg = 0b00000000;  // 0x00

reg |= (1 << 3);  // Set bit 3
// Result: 0b00001000 = 0x08

reg |= (1 << 7);  // Set bit 7
// Result: 0b10001000 = 0x88
     0b00000000          SET bit 3          0b00001000
     ┌─┬─┬─┬─┬─┬─┬─┬─┐   ──────────────►   ┌─┬─┬─┬─┬─┬─┬─┬─┐
     │0│0│0│0│0│0│0│0│   |= (1 << 3)       │0│0│0│0│1│0│0│0│
     └─┴─┴─┴─┴─┴─┴─┴─┘                     └─┴─┴─┴─┴─┴─┴─┴─┘

                                               bit 3 ON

2. CLEAR Bit (Tắt)

cpp
// CLEAR bit n to 0
// Formula: reg &= ~(1 << n)

uint8_t reg = 0b11111111;  // 0xFF

reg &= ~(1 << 3);  // Clear bit 3
// Result: 0b11110111 = 0xF7

reg &= ~(1 << 0);  // Clear bit 0
// Result: 0b11110110 = 0xF6
     0b11111111          CLEAR bit 3        0b11110111
     ┌─┬─┬─┬─┬─┬─┬─┬─┐   ──────────────►   ┌─┬─┬─┬─┬─┬─┬─┬─┐
     │1│1│1│1│1│1│1│1│   &= ~(1 << 3)      │1│1│1│1│0│1│1│1│
     └─┴─┴─┴─┴─┴─┴─┴─┘                     └─┴─┴─┴─┴─┴─┴─┴─┘

                                               bit 3 OFF

3. TOGGLE Bit (Đảo)

cpp
// TOGGLE bit n
// Formula: reg ^= (1 << n)

uint8_t reg = 0b00001000;

reg ^= (1 << 3);  // Toggle bit 3: ON → OFF
// Result: 0b00000000

reg ^= (1 << 3);  // Toggle bit 3: OFF → ON
// Result: 0b00001000

4. READ Bit (Đọc)

cpp
// READ bit n
// Formula: (reg >> n) & 1  OR  (reg & (1 << n)) != 0

uint8_t reg = 0b10101010;

bool bit3 = (reg >> 3) & 1;  // bit3 = 1
bool bit2 = (reg >> 2) & 1;  // bit2 = 0

// Alternative
if (reg & (1 << 7)) {
    // Bit 7 is set
}

Practical Example: GPIO Control

cpp
// Simulated GPIO register addresses
volatile uint8_t* const GPIO_DIR   = (uint8_t*)0x40021000;  // Direction
volatile uint8_t* const GPIO_OUT   = (uint8_t*)0x40021004;  // Output
volatile uint8_t* const GPIO_IN    = (uint8_t*)0x40021008;  // Input

// Pin definitions
constexpr uint8_t LED_PIN    = 5;
constexpr uint8_t BUTTON_PIN = 3;

// Configure pin as output
void pinMode_output(uint8_t pin) {
    *GPIO_DIR |= (1 << pin);  // Set direction bit
}

// Configure pin as input
void pinMode_input(uint8_t pin) {
    *GPIO_DIR &= ~(1 << pin);  // Clear direction bit
}

// Write to output pin
void digitalWrite(uint8_t pin, bool value) {
    if (value) {
        *GPIO_OUT |= (1 << pin);   // SET
    } else {
        *GPIO_OUT &= ~(1 << pin);  // CLEAR
    }
}

// Read input pin
bool digitalRead(uint8_t pin) {
    return (*GPIO_IN >> pin) & 1;
}

// Toggle LED
void toggleLED() {
    *GPIO_OUT ^= (1 << LED_PIN);  // TOGGLE
}

Multiple Bits Operations

cpp
// Set multiple bits at once
uint8_t reg = 0;
reg |= (1 << 3) | (1 << 5) | (1 << 7);  // Set bits 3, 5, 7
// Result: 0b10101000

// Clear multiple bits
reg &= ~((1 << 3) | (1 << 5));  // Clear bits 3 and 5
// Result: 0b10000000

// Read a field (multiple bits)
uint8_t config = 0b11010110;
uint8_t field = (config >> 2) & 0x07;  // Read bits 2-4 (3 bits)
// field = 0b101 = 5

Bit Masks & Macros

cpp
// Common macros for bit manipulation
#define BIT(n)           (1U << (n))
#define SET_BIT(reg, n)   ((reg) |= BIT(n))
#define CLEAR_BIT(reg, n) ((reg) &= ~BIT(n))
#define TOGGLE_BIT(reg, n) ((reg) ^= BIT(n))
#define READ_BIT(reg, n)  (((reg) >> (n)) & 1U)

// Field extraction
#define GET_FIELD(reg, mask, shift) \
    (((reg) & (mask)) >> (shift))

#define SET_FIELD(reg, mask, shift, value) \
    ((reg) = ((reg) & ~(mask)) | (((value) << (shift)) & (mask)))

// Usage
uint8_t status = 0;
SET_BIT(status, 3);      // Set bit 3
CLEAR_BIT(status, 3);    // Clear bit 3
bool b = READ_BIT(status, 3);

Hardware Register Example

cpp
// Timer configuration register
// Bits 0-1: Mode (0=off, 1=oneshot, 2=repeat)
// Bit 2: Enable
// Bits 3-7: Prescaler value

volatile uint8_t* const TIMER_CTRL = (uint8_t*)0x40010000;

// Mode field
constexpr uint8_t MODE_MASK  = 0b00000011;
constexpr uint8_t MODE_SHIFT = 0;

// Enable bit
constexpr uint8_t ENABLE_BIT = 2;

// Prescaler field
constexpr uint8_t PRESCALER_MASK  = 0b11111000;
constexpr uint8_t PRESCALER_SHIFT = 3;

void setupTimer(uint8_t mode, uint8_t prescaler) {
    uint8_t reg = 0;
    
    // Set mode (bits 0-1)
    reg |= (mode & 0x03);
    
    // Set prescaler (bits 3-7)
    reg |= (prescaler << PRESCALER_SHIFT);
    
    // Enable timer
    reg |= (1 << ENABLE_BIT);
    
    *TIMER_CTRL = reg;
}

📚 Tổng kết

OperationFormulaExample
SETreg |= (1 << n)Turn ON bit n
CLEARreg &= ~(1 << n)Turn OFF bit n
TOGGLEreg ^= (1 << n)Flip bit n
READ(reg >> n) & 1Get value of bit n

➡️ Tiếp theo

Khi đọc/ghi hardware registers, compiler có thể "tối ưu" sai cách...

volatile → — Tại sao cần volatile cho hardware access.