Mengendalikan dunia luar lewat peripheral MCU: GPIO untuk pin digital, UART/SPI/I2C untuk komunikasi, timer untuk pewaktuan, dan interrupt untuk respons asinkron, lengkap dengan praktik mengontrol peripheral nyata dan tabel perbandingan protokol yang wajib dipilih dengan tepat

Di episode 3 kalian mengasah C/C++ embedded — pointer, volatile, dan pola-pola seperti ring buffer. Sekarang waktunya membuka jendela ke dunia nyata: peripheral. Inilah bagian yang membuat embedded unik — kode kalian benar-benar menggerakkan sesuatu yang bisa dilihat, disentuh, dan diukur.
Peripheral adalah modul hardware yang melekat di MCU. Kalian tidak membelinya satu per satu; semuanya sudah ada di dalam chip, tinggal diaktifkan dan dikonfigurasi lewat register. Kuncinya bukan menghafal nilai register, melainkan memahami pola kerja setiap keluarga peripheral: GPIO, UART, SPI, I2C, timer, dan interrupt. Kuasai polanya, dan board apa pun terasa familiar.
GPIO (General-Purpose Input/Output) adalah pin yang bisa jadi input atau output digital. Ini peripheral paling dasar dan paling sering dipakai. Tiga register yang harus kalian konfigurasi (STM32F4):
Contoh nyala LED dari episode 0, tanpa library:
#include <stdint.h>
#define RCC_AHB1ENR (*(volatile uint32_t *)0x40023830)
#define GPIOA_MODER (*(volatile uint32_t *)0x40020000)
#define GPIOA_ODR (*(volatile uint32_t *)0x40020014)
#define LED_PIN 5U
void led_init(void) {
RCC_AHB1ENR |= (1U << 0); /* clock GPIOA aktif */
GPIOA_MODER = (GPIOA_MODER & ~(3U << (LED_PIN * 2))) | (1U << (LED_PIN * 2)); /* output */
}
int main(void) {
led_init();
for (;;) {
GPIOA_ODR ^= (1U << LED_PIN); /* toggle LED */
for (volatile uint32_t i = 0; i < 500000; i++); /* delay kasar */
}
}Perhatikan urutannya: nyalakan clock dulu sebelum menyentuh register peripheral. Ini pitfall klasik — kalian menulis register yang clock-nya mati, dan tidak terjadi apa-apa.
UART adalah protokol point-to-point dua kabel (TX, RX) dengan kecepatan yang disepakati (baud rate). Inilah "printf" firmware — jalan utama melihat apa yang terjadi di dalam perangkat. Pada banyak board, UART terhubung ke USB-serial sehingga outputnya muncul di terminal PC.
Untuk menerima data asinkron, pakai interrupt + ring buffer — pola dari episode 3:
static volatile uint8_t rx_buf[64];
static volatile uint8_t rx_head = 0;
void USART2_IRQHandler(void) {
if (USART2->SR & (1U << 5)) { /* RXNE: data diterima */
uint8_t b = USART2->DR;
rx_buf[rx_head] = b;
rx_head = (rx_head + 1) % 64;
}
}Pola ini membuat main loop tetap bebas: data masuk mengalir di buffer tanpa menunggu, dan diproses saat dibutuhkan. Inilah fondasi komunikasi semua firmware.
Dua protokol komunikasi antar-chip yang paling umum untuk sensor, display, dan memory eksternal:
| Aspek | SPI | I2C |
|---|---|---|
| Kabel | 4 (SCLK, MOSI, MISO, CS) | 2 (SCL, SDA) |
| Kecepatan | Tinggi (MHz) | Sedang (100-400 kHz, bisa lebih) |
| Banyak device | 1 CS per device | Alamat 7-bit, banyak device di bus sama |
| Kompleksitas | Mudah, tapi butuh pin | Lebih kompleks (addressing, ack) |
| Ideal untuk | Display, flash, data besar | Sensor, RTC, konfigurasi |
Aturan praktis: data besar dan cepat → SPI; banyak sensor kecil → I2C. Keduanya dikonfigurasi dengan pola register yang sama (enable clock → set mode/kecepatan → transfer). Pilih salah satu, kuasai, maka yang lain tinggal menyesuaikan.
Timer peripheral menghitung clock dan bisa memicu aksi pada interval presisi — jauh lebih akurat daripada for loop delay yang kita pakai di awal. Inilah jantung scheduling di bare-metal (dipakai penuh di episode 6-7).
Interrupt adalah mekanisme agar hardware bisa "menyela" CPU saat ada kejadian: data UART tiba, pin berubah, timer habis. Tanpa interrupt, CPU harus polling terus-menerus — membuang clock dan daya. Desain interrupt yang benar (prioritas, ISR singkat, concurrency safety) kita bedah mendalam di episode 8.
Tip
Jangan langsung menyalahkan register saat hardware tidak merespons. Debug dengan pola yang sama setiap kali: (1) cek power & ground, (2) cek clock peripheral, (3) cek pin multiplexing, (4) cek wiring fisik. Lebih dari separuh "bug firmware" pemula ternyata adalah kabel yang salah colok.
Inti yang harus dibawa pulang:
Di episode 5 selanjutnya kita masuk ke wilayah yang membedakan firmware hebat dari firmware biasa: memory & DMA — mengelola Flash/RAM/registers, memahami alignment, dan melepas beban CPU dengan Direct Memory Access. Memori di embedded bukan sekadar tempat simpan, tapi juga masalah performa dan keandalan. Sampai jumpa di episode 5!