Merancang interrupt yang benar: ISR yang singkat, prioritas NVIC yang deterministik, pola deferred work dengan queue, dan konvensi FromISR di FreeRTOS agar firmware tetap responsif dan bebas race condition saat hardware menyela di waktu tak terduga

Di episode 7 kalian belajar task, semaphore, dan queue di FreeRTOS. Tapi ada satu "penduduk" lain di MCU yang tidak tunduk pada scheduler: interrupt. Interrupt adalah penyela yang bisa memotong task apa pun — termasuk task berprioritas tertinggi — kapan pun hardware mengirim sinyal. Karena sifatnya yang tak terduga, interrupt adalah sumber race condition dan bug paling licin di firmware.
Episode ini membangun aturan mainnya: bagaimana merancang ISR (Interrupt Service Routine) yang singkat, bagaimana memilih prioritas interrupt yang deterministik, dan bagaimana berkomunikasi dengan task RTOS secara aman dari dalam ISR. Kuasai ini, dan firmware kalian akan responsif sekaligus stabil.
Bayangkan kalian sedang menulis laporan (task) di kantor — lalu telepon berdering (interrupt). Kalian berhenti, mencatat pesan singkat, lalu kembali menulis. Itu analogi interrupt: hardware memanggil, CPU menyimpan konteks pekerjaan saat ini, menjalankan ISR, lalu kembali ke pekerjaan semula.
Konteks ini disimpan otomatis oleh hardware (stack diisi register CPU). Setelah ISR selesai, eksekusi kembali ke titik semula — seolah tidak terjadi apa-apa. Karena itu ISR harus sesingkat mungkin: selama ISR berjalan, pekerjaan lain (termasuk task real-time) terhenti.
ISR yang baik hanya melakukan tiga hal, dan tidak lebih:
Semua pemrosesan berat (parsing, format, log, update state machine) didelegasikan ke task biasa. Pola ini disebut deferred work atau "ISR menyelesaikan urusan di konteks task":
void UART2_IRQHandler(void) {
BaseType_t higher = pdFALSE;
if (UART2->SR & (1U << 5)) { /* RXNE: ada byte baru */
uint8_t b = UART2->DR;
xQueueSendFromISR(rx_queue, &b, &higher);
UART2->SR &= ~(1U << 5); /* bersihkan flag RXNE */
}
portYIELD_FROM_ISR(higher); /* jika ada task menunggu, beri jalan */
}Perhatikan tiga konvensi khusus ISR di FreeRTOS:
xQueueSendFromISR — bukan xQueueSend biasa; versi ini aman dipanggil dari interrupt dan tidak pernah memblokir.higher — jika ISR "membangunkan" task, kernel menandai bahwa task itu mungkin lebih layak jalan daripada task yang sedang dipotong; portYIELD_FROM_ISR lalu menyerahkan CPU.vTaskDelay dan tanpa mutex di ISR — keduanya memblokir, dan ISR tidak boleh memblokir.Aturan umumnya: di ISR, hanya fungsi berakhiran FromISR yang boleh dipanggil.
Jangan bingung dua "prioritas" yang berbeda ini:
| Prioritas | Pemilik | Cara kerja |
|---|---|---|
| Prioritas interrupt (NVIC) | Hardware | Angka lebih kecil = lebih tinggi (Cortex-M) |
| Prioritas task (FreeRTOS) | Scheduler | Angka lebih besar = lebih tinggi |
Prioritas NVIC menentukan: jika dua interrupt datang bersamaan, mana yang jalan dulu, dan apakah sebuah ISR bisa disela ISR lain. Aturan desainnya:
Race condition terjadi saat ISR dan task (atau dua ISR) mengakses data yang sama tanpa sinkronisasi. Contoh klasik:
static uint16_t latest_sample;
void ADC_IRQHandler(void) {
latest_sample = ADC1->DR; /* ISR menulis */
}
void vTaskDisplay(void *pv) {
for (;;) {
printf("%u", latest_sample); /* task membaca — bisa kena data robek */
vTaskDelay(pdMS_TO_TICKS(10));
}
}Jika latest_sample bertipe uint32_t atau lebih besar, ISR bisa menimpa data di tengah pembacaan task (pada bus yang tidak atomic). Tiga solusi, dari paling ringan ke paling kokoh:
taskENTER_CRITICAL() / taskEXIT_CRITICAL() untuk mengunci akses singkat; berhenti sejenak, tapi deterministik dan ringan.volatile — hanya aman jika variabelnya atomic (ukuran ≤ lebar bus dan sesuai alignment); gunakan untuk flag sederhana.Warning
Jangan pernah memanggil fungsi library yang lambat, melakukan print, atau menggunakan malloc di dalam ISR. Selain memperlambat sistem, banyak fungsi library tidak "reentrant" — memanggilnya dari ISR saat task sedang memakainya menghasilkan data korup yang sangat sulit dilacak. Pekerjaan berat selalu dipindahkan ke task lewat queue.
Praktik untuk board kalian:
FromISR.Latihan lanjutan: aktifkan stack overflow check dan jalankan dengan prioritas task yang berbeda-beda; amati urutan eksekusi berubah sesuai prioritas.
volatile + sinkronisasi — race condition.Inti yang harus dibawa pulang:
FromISR API dan portYIELD_FROM_ISR saat berkomunikasi dengan task dari ISR.Di episode 9 selanjutnya kita menyiapkan senjata debugging: debugging & toolchain — GDB, OpenOCD, trace, dan logic analyzer, dengan praktik men-debug firmware yang sengaja dibuat error. Kemampuan inilah yang menentukan berapa cepat kalian menemukan bug yang menghabiskan seminggu. Sampai jumpa di episode 9!