Pembahasan Kode main.c

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: 0x200000 adalah 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.
  • const: Pointer GPFSEL1 itu 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:
    1. ~(7 << 18): Angka 7 binernya 111. 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.
    2. (1 << 18): Memasukkan nilai 001 ke posisi tersebut. Menurut datasheet BCM2835, 001 artinya 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 1 di bit ke-16, pin 16 jadi HIGH. Jika kita tulis 0, tidak ada efek apa-apa.
    • GPCLR0: Jika kita menulis 1 di bit ke-16, pin 16 jadi LOW.
    • Ini disebut operasi Atomik. Kita tidak perlu membaca nilai lama dulu, langsung tembak tulis.

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) = Nyala
  • GPSET (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.