Kode main.c ini adalah implementasi klasik dari konsep Memory Mapped I/O (MMIO). Karena kita tidak punya OS (tidak ada sysfs, tidak ada driver), satu-satunya cara kita bicara dengan hardware adalah dengan “menulis” nilai ke alamat memori tertentu yang sebenarnya terhubung langsung ke kaki-kaki chip BCM2835.
Mari kita bedah teorinya per blok:
1. Base Address (Peta Harta Karun)
#define PERIPHERAL_BASE 0x20000000
#define GPIO_BASE (PERIPHERAL_BASE + 0x200000)
- Teori: Pada Raspberry Pi 1 (prosesor BCM2835), alamat fisik periferal dimulai dari
0x20000000. Ini berbeda dengan RPi 2/3 (0x3F...) atau RPi 4 (0xFE...). Inilah alasan kenapa kode bare metal RPi 1 seringkali tidak jalan di versi yang lebih baru tanpa modifikasi. - Offset:
0x200000adalah jarak dari pintu gerbang utama (Base) menuju blok kontrol GPIO.
2. Pointer & Keyword volatile (Kunci Utama)
volatile uint32_t* const GPFSEL1 = (volatile uint32_t*)(GPIO_BASE + 0x04);
Ini adalah baris paling kritikal dalam embedded C.
volatile: Ini memberitahu compiler (GCC): “Jangan pernah mengoptimasi variabel ini! Selalu baca/tulis langsung ke alamat memorinya setiap kali kode dijalankan.”- Tanpa
volatile, compiler mungkin berpikir: “Ah, variabel ini nilainya diubah tapi tidak pernah dibaca lagi, saya hapus saja supaya cepat.” Akibatnya, lampu tidak akan berkedip.
- Tanpa
const: PointerGPFSEL1itu sendiri (alamatnya) bersifat konstan, tidak boleh diubah-ubah ke alamat lain.uint32_t: Register di ARM1176JZF-S adalah 32-bit. Kita harus menulis 32 bit sekaligus agar aman.
3. Logika Register GPFSEL (Function Select)
*GPFSEL1 &= ~(7 << 18);
*GPFSEL1 |= (1 << 18);
Di sini kita mengatur Mode Pin.
- Kenapa GPFSEL1?
- Setiap register GPFSEL mengatur 10 pin.
- GPFSEL0: Pin 0-9
- GPFSEL1: Pin 10-19 (Kita pakai GPIO 16, jadi ada di sini).
- Kenapa Bit 18-20?
- Satu pin butuh 3 bit konfigurasi.
- GPIO 16 adalah pin ke-6 di register ini ($16 – 10 = 6$).
- $6 \times 3 \text{ bit} = 18$. Jadi bit mulainya adalah bit ke-18.
- Operasi Bitwise:
~(7 << 18): Angka 7 binernya111. Digeser ke kiri 18 kali, lalu di-NOT kan (~). Ini teknik Masking untuk membersihkan (meng-nol-kan) bit 18, 19, dan 20 tanpa mengganggu bit milik pin lain.(1 << 18): Memasukkan nilai001ke posisi tersebut. Menurut datasheet BCM2835,001artinya Output.
4. Register GPSET dan GPCLR (Atomic Operations)
*GPCLR0 = (1 << 16); // Nyala (Active Low)
*GPSET0 = (1 << 16); // Mati
Di sinilah keunikan arsitektur BCM2835 dibanding mikrokontroler sederhana seperti AVR (Arduino Uno).
- Kenapa dipisah SET dan CLR?Biasanya di chip lain kita pakai PORTA |= (1<<16) (Read-Modify-Write). Tapi ini berbahaya di sistem yang kompleks karena bisa terjadi Race Condition jika ada interupsi di tengah proses baca-tulis.
- Cara Kerja:
- GPSET0: Jika kita menulis
1di bit ke-16, pin 16 jadi HIGH. Jika kita tulis0, tidak ada efek apa-apa. - GPCLR0: Jika kita menulis
1di bit ke-16, pin 16 jadi LOW. - Ini disebut operasi Atomik. Kita tidak perlu membaca nilai lama dulu, langsung tembak tulis.
- GPSET0: Jika kita menulis
5. Fungsi sleep dan nop
asm volatile("nop");
- Karena kita belum mengaktifkan System Timer atau Interrupt, kita membuat delay manual (Busy Wait).
nop(No Operation) adalah instruksi Assembly yang menyuruh CPU “menganggur” selama 1 siklus clock. Ini mencegah loop kosong dihilangkan total oleh optimizer compiler.
Analisis Hardware (Active Low):
Di kode tertulis:
GPCLR(Clear/Low) = NyalaGPSET(Set/High) = Mati
Ini berarti rangkaian LED di PCB/Breadboard Anda dirangkai secara Common Anode atau Sinking Current (VCC -> LED -> Pin GPIO). Kalau LED-nya dipasang standar (Pin GPIO -> LED -> GND), logikanya akan terbalik.
