Bedah Anatomi Register GPIO: Mengapa GPIO 16 Ada di GPFSEL1?

Kompilasi oleh : Reza Ervani bin Asmanu

Dalam artikel sebelumnya tentang [Peta Alamat Fisik BCM2835], kita telah membahas bahwa untuk mengatur mode GPIO, kita harus mengakses register tertentu. Secara spesifik, kita menggunakan alamat offset 0x04 (GPFSEL1) untuk mengatur GPIO 16 (Lampu LED Internal) sebagai Output.

Mungkin Anda bertanya: “Mengapa harus offset 0x04? Mengapa tidak di 0x00? Dan dari mana datangnya angka pergeseran 18 bit (<< 18) dalam kode kita?”

Artikel ini akan membedah matematika di balik manajemen register GPIO pada arsitektur ARM 32-bit.

1. Tantangan Arsitektur: 32 Bit vs 54 Jalur GPIO

Prosesor BCM2835 yang menjadi otak Raspberry Pi 1 adalah mesin 32-bit. Artinya, setiap “laci” penyimpanan (register) di dalamnya hanya memiliki lebar data 32 bit (dari bit 0 hingga bit 31).

Di sisi lain, chip BCM2835 memiliki total 54 Jalur GPIO (GPIO 0 hingga GPIO 53).

Jika setiap GPIO hanya butuh logika sederhana (1 bit untuk Input, 0 bit untuk Output), mungkin 32 GPIO bisa muat dalam satu register. Namun, GPIO pada BCM2835 sangat canggih. Setiap satu jalur GPIO memiliki 8 kemungkinan mode fungsi:

  1. Input
  2. Output
  3. Alternative Function 0 (misal: I2C SDA)
  4. Alternative Function 1 (misal: UART TX)
  5. Alternative Function 2
  6. Alternative Function 3
  7. Alternative Function 4
  8. Alternative Function 5

Secara matematika biner, untuk membedakan 8 pilihan ($2^3 = 8$), kita membutuhkan 3 bit data untuk konfigurasi setiap satu GPIO.

2. Kalkulasi “Kamar” Register

Jika satu GPIO “memakan” tempat sebanyak 3 bit, berapa banyak GPIO yang bisa ditampung dalam satu register 32-bit?

Kapasitas=Lebar RegisterKebutuhan per GPIO=32 bit3 bit=𝟏𝟎 GPIO\text{Kapasitas} = \frac{\text{Lebar Register}}{\text{Kebutuhan per GPIO}} = \frac{32 \text{ bit}}{3 \text{ bit}} = \mathbf{10 \text{ GPIO}}

Sisa pembagian:

32(10×3)=2 bit32 – (10 \times 3) = 2 \text{ bit}

Dua bit terakhir (bit 30 dan 31) tidak digunakan (Reserved), dan 30 bit pertama digunakan untuk mengatur 10 GPIO.

Karena satu register hanya muat 10 GPIO, maka Broadcom memecah pengaturan 54 GPIO tersebut ke dalam 6 register berurutan (GPFSEL0 hingga GPFSEL5):

Nama RegisterOffsetRentang GPIOKeterangan
GPFSEL00x00GPIO 0 – 9Mengatur 10 GPIO pertama
GPFSEL10x04GPIO 10 – 19Mengatur 10 GPIO kedua (Termasuk GPIO 16)
GPFSEL20x08GPIO 20 – 29Mengatur 10 GPIO ketiga
GPFSEL30x0CGPIO 30 – 39
GPFSEL40x10GPIO 40 – 49
GPFSEL50x14GPIO 50 – 53Hanya mengatur sisa 4 GPIO

3. Studi Kasus: Melacak GPIO 16

Sekarang kita paham mengapa kita menggunakan GPFSEL1 (Offset 0x04) untuk GPIO 16. Alasannya sederhana: karena angka 16 berada dalam rentang 10-19.

Langkah selanjutnya adalah menentukan posisi bit yang tepat di dalam register tersebut.

  1. Indeks Lokal: Di dalam “kamar” GPFSEL1, GPIO 10 adalah penghuni pertama (urutan ke-0). Maka, GPIO 16 adalah penghuni ke-6.
Indeks=1610=6\text{Indeks} = 16 – 10 = 6
  1. Posisi Bit: Karena setiap penghuni memakan 3 bit, kita kalikan indeks tersebut dengan 3.
Bit Mulai=Indeks×3=6×3=18\text{Bit Mulai} = \text{Indeks} \times 3 = 6 \times 3 = 18

Jadi, pengaturan untuk GPIO 16 terletak pada Bit 18, 19, dan 20 di dalam register GPFSEL1.

4. Implementasi dalam Kode C

Pemahaman ini menjelaskan logika di balik baris kode inisialisasi yang kita tulis:

/* 1. Reset Bit (Pembersihan) */
*GPFSEL1 &= ~(7 << 18);

/* 2. Set Output (Pengaturan) */
*GPFSEL1 |= (1 << 18);

Mari kita bedah operasinya:

Langkah 1: Pembersihan (Reset)

Sebelum mengatur mode, kita wajib membersihkan konfigurasi lama agar tidak tercampur.

  • Angka 7 dalam biner adalah 111 (merepresentasikan 3 bit penuh).
  • (7 << 18) menggeser angka 111 ke posisi bit 18-20.
  • Operator ~ (NOT) membalikkan semua bit, membuat “lubang nol” di posisi 18-20, dan angka 1 di tempat lain.
  • Operator &= (AND) akan memaksa bit 18-20 menjadi 000 (Input Mode/Reset), sementara bit milik GPIO lain tidak berubah.

Langkah 2: Pengaturan (Set)

Menurut datasheet, kode biner untuk Output adalah 001.

  • Angka 1 dalam biner adalah 001.
  • (1 << 18) menempatkan angka 001 tepat di posisi bit 18-20.
  • Operator |= (OR) menuliskan nilai tersebut ke register.

Kesimpulan

Satu baris kode *GPFSEL1 |= (1 << 18); yang terlihat sederhana, sebenarnya mengandung logika aritmatika sistem yang presisi:

  1. Memilih register yang tepat berdasarkan kelipatan 10 GPIO (GPFSEL1).
  2. Menghitung offset bit berdasarkan kelipatan 3 bit per GPIO (Bit 18).
  3. Menerapkan nilai fungsi yang sesuai (Output = 1).

Dengan memahami struktur ini, Anda kini bisa dengan mudah menulis driver untuk GPIO mana pun (misalnya GPIO 23 di Pin Fisik 16) tanpa harus menebak-nebak angka “ajaib” di dalam kode Anda.