Membuka komunikasi antar chip dan mengukur dunia fisik: memahami protokol UART, SPI, dan I2C beserta perbandingan dan kapan memakainya, ADC untuk membaca sinyal analog, timer dan PWM untuk timing presisi, serta praktik menulis driver UART pertama.

Setelah di episode 4 kalian menyalakan LED lewat register, pada episode ini firmware mulai berbicara: berkomunikasi dengan chip lain (sensor, display, radio) dan mengukur dunia analog. Ini lompatan besar — dari mengendalikan satu pin menjadi menghubungkan satu sistem.
Mengapa peripheral penting? Karena hampir semua produk firmware nyata adalah kumpulan peripheral: mikrokontroler membaca sensor via I2C/SPI, mencetak log via UART, mengatur kecerahan LED via PWM, dan mengukur baterai via ADC. Pilihan protokol yang salah bisa berarti pemborosan pin, kecepatan rendah, atau integrasi yang menyakitkan — jadi memahami tiap protokol adalah kompetensi inti.
| Aspek | UART | SPI | I2C |
|---|---|---|---|
| Sinyal | TX, RX (2) | MOSI, MISO, SCLK, CS (3–4+) | SDA, SCL (2) |
| Arah | Full-duplex | Full-duplex | Half-duplex |
| Perangkat per bus | Point-to-point | Multi-device (1 CS/device) | Multi-device (alamat 7/10-bit) |
| Kecepatan | 115200 baud (dll.) | s/d puluhan MHz | 100 kbps / 400 kbps / 1+ MHz |
| Sinkronisasi | Async (baud rate) | Sync (SCLK) | Sync (SCL) |
| ACK | Tidak ada | Tidak ada | Ada (bit ACK) |
| Cocok untuk | Debug log, GPS, Bluetooth | Flash, SD card, LCD, high-speed | Sensor, EEPROM, RTC |
Analoginya: UART seperti dua orang bicara dengan kesepakatan tempo (baud rate); SPI seperti satu raja (master) memanggil satu per satu budak (slave) dengan bendera khusus (CS); I2C seperti bus umum dengan alamat — semua orang mendengar, hanya yang alamatnya dipanggil yang menjawab.
UART mengirim byte serial asinkron: tidak ada clock terpisah, jadi kedua sisi harus sepakat soal baud rate dan format frame (misal 8-N-1 = 8 data bit, tanpa parity, 1 stop bit).
Start | D0 D1 D2 D3 D4 D5 D6 D7 | StopAkses register: tulis byte ke register data, tunggu flag TXE/TC, atau tunggu RXNE untuk menerima. Pola polling paling sederhana:
void uart_send_char(USART_TypeDef *u, char c) {
while (!(u->SR & USART_SR_TXE)); // tunggu TX buffer kosong
u->DR = c;
}
char uart_get_char(USART_TypeDef *u) {
while (!(u->SR & USART_SR_RXNE)); // tunggu data masuk
return (char)u->DR;
}Baud rate diatur lewat prescaler di register BRR (tergantung clock APB). Polling sederhana tapi memblokir; di episode 6 kita pindah ke mode interrupt/DMA agar tidak membuang CPU.
SPI adalah protokol sync full-duplex: master mengirim clock SCLK, data dua arah MOSI (master→slave) dan MISO (slave→master), dan memilih perangkat dengan menarik CS rendah. Setiap slave butuh satu pin CS sendiri.
CS : ______ ______
SCLK : _/\/\/\_..._/\/\/\_ (8 bit/byte, master kontrol)
MOSI : [cmd byte] [addr byte] [dummy/read cmd]
MISO : [dummy] [data out...]SPI populer untuk flash eksternal, SD card, dan display karena cepat dan sederhana. Ada 4 mode (CPOL/CPHA) yang menentukan polaritas/fase clock — salah mode = data "kacau" tapi tidak error, salah satu sumber debugging paling menyebalkan.
I2C memakai dua kabel open-drain (SDA, SCL) dengan pull-up resistor. Setiap perangkat punya alamat 7-bit; komunikasi diawali start condition, diikuti alamat + bit read/write, dan tiap byte dikonfirmasi dengan bit ACK:
S | [addr<<1 | W] | ACK | [reg] | ACK | [data] | ACK | P
S = start, P = stop; ACK dikirim slaveI2C hemat pin dan mendukung banyak perangkat dalam satu bus — sangat cocok untuk sensor dan EEPROM. Kelemahannya: lebih lambat dari SPI dan butuh penanganan NACK (perangkat tidak merespons) yang benar. Timing SDA/SCL sering ditangani interrupt/DMA agar tidak memblokir.
ADC (Analog-to-Digital Converter) mengubah tegangan analog menjadi angka digital. Resolusi khas 12-bit → nilai 0–4095. Pembacaan dasar:
EOC, baca register DR.adc_start_conversion(ADC1);
while (!(ADC1->SR & ADC_SR_EOC)); // tunggu konversi selesai
uint16_t raw = ADC1->DR; // 0..4095Rumus umum: tegangan = raw * VREF / 4096 (misal 3.3V). Untuk pengukuran presisi, perhatikan VREF, filter, dan waktu sampling.
Timer adalah penghitung hardware yang bekerja tanpa CPU — jantung dari timing presisi. Satu timer dikonfigurasi dengan prescaler (pembagi clock) dan auto-reload (periode):
// Konsep: prescaler + ARR menentukan frekuensi,
// CCRx menentukan duty cycle
// fPWM = clock / (prescaler+1) / (ARR+1)
// duty = CCRx / (ARR+1)PWM memakai timer dengan output compare: sinyal HIGH selama counter di bawah CCR, LOW setelahnya. Aplikasi: meredupkan LED, menggerakkan motor, mengemudi servo. Timer juga bisa memicu ADC secara berkala dan menjadi dasar "waktu" firmware (episode 7).
Buat driver UART polling untuk mencetak log — fondasi debugging semua episode berikutnya:
#ifndef UART_H
#define UART_H
void uart_init(uint32_t baud);
void uart_putc(char c);
void uart_puts(const char *s);
#endifvoid uart_puts(const char *s) {
while (*s) uart_putc(*s++);
}
void print_num(uint32_t n) { // print desimal
char buf[12]; int i = 0;
if (n == 0) { uart_putc('0'); return; }
while (n) { buf[i++] = '0' + (n % 10); n /= 10; }
while (i) uart_putc(buf[--i]);
}Tip
Dari sinilah kalian mulai membangun library pribadi: kumpulan driver kecil (uart, gpio, timer) yang bisa kalian pakai ulang. Konsistensi API — misal uart_puts, gpio_write — akan sangat membantu saat kalian menulis driver sensor di episode 10 dan RTOS di episode 7.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kita membuat firmware responsif: Interrupts & NVIC — bagaimana CPU merespons kejadian tanpa menunggu, prioritas interrupt, dan menulis interrupt-safe code yang aman. Di sinilah firmware mulai "berpikir paralel"!