Assalamu'alaikum warohmatulohi wabarakatuh,
Kali ini kita akan membahas cara menjalankan FreeRTOS di mikrokontroler STM32H743VIT6 dengan software STM32CubeIDE. Sebelum masuk ke praktik, kita bahas dulu apa itu RTOS, apa kelebihannya, dan mengapa FreeRTOS banyak dipakai.
Apa itu RTOS?
RTOS (Real Time Operating System) adalah sistem operasi kecil untuk mikrokontroler yang tugas utamanya mengatur kapan setiap pekerjaan dijalankan. Pekerjaan-pekerjaan itu dipecah menjadi beberapa task (disebut juga thread). Setiap task adalah fungsi dengan perulangan sendiri, punya stack sendiri, dan punya prioritas.
Inti dari RTOS adalah scheduler. Scheduler memilih task mana yang boleh memakai CPU. Pada STM32 (Cortex-M), scheduler berjalan setiap tick (biasanya 1 ms) dan setiap kali ada task yang menunggu sesuatu. Karena perpindahan antar task terjadi sangat cepat, beberapa task terlihat berjalan bersamaan, padahal CPU-nya hanya satu.
Kata real time di sini bukan berarti "cepat", tetapi tepat waktu dan bisa diprediksi. RTOS menjamin task berprioritas tinggi langsung dijalankan begitu siap, sehingga waktu respons sistem bisa dihitung dan dijamin.
Super loop vs RTOS
Tanpa RTOS, program mikrokontroler biasanya ditulis dengan pola super loop: semua pekerjaan dipanggil berurutan di dalam while(1). Cara ini sederhana, tetapi makin sulit diatur ketika pekerjaannya bertambah banyak. Satu fungsi yang lambat (misalnya menunggu data atau memakai HAL_Delay()) membuat semua pekerjaan lain ikut tertahan.
| Aspek | Super loop (tanpa RTOS) | Dengan RTOS |
| Struktur program | Semua pekerjaan di satu while(1) | Setiap pekerjaan punya task sendiri |
| Delay / menunggu | HAL_Delay() menghentikan seluruh program | osDelay() hanya menghentikan task itu, task lain tetap jalan |
| Prioritas | Tidak ada, kecuali lewat interrupt | Setiap task punya prioritas, task penting bisa menyela task lain |
| Komunikasi antar bagian | Variabel global dan flag | Queue, semaphore, mutex, event flag yang aman |
| Penambahan fitur | Harus mengatur ulang urutan dan timing di loop | Cukup menambah task baru |
| Kebutuhan memori | Sangat kecil | Butuh RAM tambahan untuk stack tiap task dan kernel |
Kelebihan memakai RTOS
- Program lebih modular. Setiap fungsi (baca sensor, kirim data, tampilkan ke LCD) ditulis sebagai task terpisah, sehingga lebih mudah dibaca, diuji, dan dikerjakan bersama tim.
- Respons lebih cepat dan terjamin. Task berprioritas tinggi langsung menyela task berprioritas rendah (preemption), sehingga pekerjaan penting tidak menunggu pekerjaan lain selesai.
- CPU tidak terbuang untuk menunggu. Task yang sedang menunggu (delay, data, atau sinyal) masuk keadaan blocked dan tidak memakai CPU sama sekali. Waktu CPU dipakai task lain.
- Komunikasi antar task aman. RTOS menyediakan queue untuk mengirim data, semaphore untuk sinyal (misalnya dari interrupt), dan mutex untuk berbagi resource seperti UART atau SPI, sehingga data tidak rusak saat diakses beberapa task.
- Timing lebih rapi. Fungsi seperti
osDelay(), osDelayUntil() dan software timer membuat pekerjaan periodik mudah dibuat tanpa menghitung timing secara manual.
- Mudah dikembangkan. Menambah fitur baru cukup dengan menambah task, tanpa mengubah timing bagian program yang sudah berjalan.
- Hemat daya. Saat semua task sedang menunggu, RTOS menjalankan idle task yang bisa dipakai untuk membuat MCU masuk mode sleep.
Tentu ada konsekuensinya: RTOS membutuhkan RAM tambahan (stack untuk setiap task dan memori kernel), dan programmer perlu memahami konsep baru seperti prioritas, deadlock, dan race condition. Untuk MCU sebesar STM32H743 dengan RAM sekitar 1 MB, kebutuhan memori ini sama sekali bukan masalah.
Mengenal FreeRTOS
FreeRTOS adalah RTOS open source yang paling banyak dipakai di dunia mikrokontroler. FreeRTOS dikembangkan oleh Richard Barry sejak awal tahun 2000-an, dan sejak 2017 dikelola oleh Amazon Web Services (AWS) dengan lisensi MIT, sehingga gratis dipakai termasuk untuk produk komersial.
Beberapa alasan FreeRTOS populer:
- Kecil dan ringan. Kernel-nya hanya beberapa file C (
tasks.c, queue.c, list.c, timers.c, dan lain-lain) dan cukup beberapa kilobyte flash.
- Mendukung banyak arsitektur. Berjalan di ARM Cortex-M, Cortex-A, RISC-V, ESP32, AVR, PIC, dan puluhan arsitektur lain.
- Fiturnya lengkap. Ada task dengan prioritas, scheduler preemptive, queue, semaphore (binary dan counting), mutex (dengan priority inheritance), software timer, event group, stream buffer dan message buffer.
- Sudah terintegrasi di STM32CubeIDE. FreeRTOS bisa diaktifkan cukup dengan beberapa klik di STM32CubeMX, lalu kode inisialisasinya dibuat otomatis.
- Dokumentasi dan komunitasnya besar, jadi contoh program dan solusi masalah mudah ditemukan.
Di STM32CubeIDE, FreeRTOS dibungkus dengan lapisan CMSIS-RTOS buatan ARM. Ada dua pilihan, CMSIS_V1 dan CMSIS_V2. Pada tutorial ini kita memakai CMSIS_V2, sehingga fungsi yang dipakai di program berawalan os..., misalnya osThreadNew(), osDelay(), osMessageQueuePut() dan osMutexAcquire(). Fungsi-fungsi ini memanggil fungsi asli FreeRTOS seperti xTaskCreate() dan vTaskDelay(). Kelebihannya, kode kita lebih mudah dipindah ke RTOS lain yang juga mendukung CMSIS-RTOS. Versi FreeRTOS yang dipakai di project ini adalah 10.3.1.
Beberapa istilah penting di FreeRTOS:
| Istilah | Penjelasan singkat |
| Task / Thread | Fungsi yang berjalan sendiri dengan stack dan prioritas sendiri |
| Scheduler | Bagian kernel yang memilih task mana yang dijalankan CPU |
| Tick | Interrupt periodik (di sini 1 ms dari SysTick) sebagai satuan waktu RTOS |
| State task | Running (sedang jalan), Ready (siap jalan), Blocked (menunggu delay/data/sinyal), Suspended (dihentikan sementara) |
| Queue | Antrian untuk mengirim data dari satu task ke task lain |
| Semaphore | Sinyal antar task atau dari interrupt ke task |
| Mutex | Kunci supaya satu resource (misalnya UART) hanya dipakai satu task pada satu waktu |
| Software timer | Timer berbasis tick yang memanggil fungsi callback secara periodik atau sekali |
| Idle task | Task prioritas paling rendah yang dijalankan saat semua task lain sedang menunggu |
| Heap | Area memori tempat kernel membuat task, queue, dan objek lain (di project ini memakai heap_4) |
Praktik: FreeRTOS di STM32H743VIT6
Konfigurasi FreeRTOS di tutorial ini saya ambil dari project yang saya pakai sehari-hari. Agar mudah dipahami, kita mulai dari project baru dan cukup membuat test LED berkedip di dalam task FreeRTOS: LED nyala 1 detik, lalu mati 2 detik. Kalau LED sudah berkedip dengan pola itu, berarti scheduler FreeRTOS sudah berjalan dengan benar dan project siap dikembangkan.
Tutorial ini saya praktikkan di STM32H743VIT6, tetapi langkah-langkahnya juga berlaku untuk seri STM32H7 lain seperti STM32H750. Cukup pilih chip yang sesuai di Langkah 1.
Alat dan bahan
- Board STM32H743VIT6 (LED terhubung ke pin PA1)
- ST-Link V2 / V3 untuk upload dan debug
- STM32CubeIDE (saya memakai STM32CubeIDE 1.17.0, dengan project dibuat di STM32CubeMX 6.9.1 dan firmware package STM32Cube FW_H7 V1.11.2)
Langkah 1 – Membuat project baru
- Buka STM32CubeIDE, lalu pilih File → New → STM32 Project.
- Di tab MCU/MPU Selector, ketik STM32H743VIT6, pilih chip tersebut, lalu klik Next.
- Beri nama project, misalnya
H743_FreeRTOS_LED, lalu klik Finish.
Langkah 2 – Konfigurasi System Core
GPIO: klik pin PA1, pilih GPIO_Output, lalu beri label LED (klik kanan → Enter User Label). Biarkan setting default: Output Push Pull, No pull-up and no pull-down, output level Low.
Pin PA1 diset sebagai GPIO_Output dengan label LED
SYS: ubah Timebase Source dari SysTick ke TIM1.
Ini penting! FreeRTOS memakai SysTick sebagai tick RTOS, jadi HAL harus memakai timer lain. Kalau tidak diubah, CubeIDE akan memberi peringatan saat generate code.
Timebase Source HAL dipindah ke TIM1
RCC: set High Speed Clock (HSE) dan Low Speed Clock (LSE) ke Crystal/Ceramic Resonator. Pada tutorial ini sumber clock sistem tetap memakai HSI internal, jadi HSE/LSE disiapkan saja untuk pengembangan berikutnya.
HSE dan LSE diset ke Crystal/Ceramic Resonator
NVIC: pastikan Priority Group adalah 4 bits for pre-emption priority (default saat FreeRTOS diaktifkan).
Langkah 3 – Konfigurasi Clock
Di project ini saya memakai clock 75 MHz yang berasal dari HSI (64 MHz) lewat PLL1. Clock ini sudah cukup untuk FreeRTOS dan lebih hemat daya. Buka tab Clock Configuration lalu atur:
| Parameter | Nilai |
| PLL Source Mux | HSI (64 MHz) |
| DIVM1 | /4 (input PLL = 16 MHz) |
| DIVN1 | X 9, dengan fracn1 = 3072 (VCO = 150 MHz) |
| DIVP1 | /2 (PLL1P = 75 MHz) |
| DIVQ1 | /3 (PLL1Q = 50 MHz) |
| System Clock Mux | PLLCLK |
| SYSCLK / HCLK / APBx | 75 MHz (semua prescaler = /1) |
Clock Configuration: HSI 64 MHz → PLL1 → SYSCLK 75 MHz
Di RCC → Parameter Settings, Power Regulator Voltage Scale cukup Scale 3 dan Supply Source = PWR_LDO_SUPPLY. Untuk clock 75 MHz, flash latency yang dihasilkan CubeMX adalah FLASH_LATENCY_1.
Kalau nanti butuh performa maksimal, clock bisa dinaikkan sampai 480 MHz (revisi V) dengan Voltage Scale 0.
Langkah 4 – Mengaktifkan FreeRTOS
- Buka Middleware and Software Packs → FREERTOS.
- Pada Interface, pilih CMSIS_V2.
- Di tab Config parameters, saya memakai nilai berikut:
| Parameter | Nilai | Keterangan |
| USE_PREEMPTION | Enabled | Task prioritas tinggi bisa menyela task prioritas rendah |
| TICK_RATE_HZ | 1000 | 1 tick = 1 ms, jadi osDelay(1000) = 1 detik |
| MAX_PRIORITIES | 56 | Default CMSIS_V2 |
| MINIMAL_STACK_SIZE | 7680 words | Stack minimal setiap task |
| TOTAL_HEAP_SIZE | 70360 bytes | Memori untuk membuat task, queue, mutex |
| USE_NEWLIB_REENTRANT | Enabled | Wajib kalau memakai printf/sprintf/malloc di dalam task |
| USE_MUTEXES / USE_RECURSIVE_MUTEXES | Enabled | Default |
| USE_COUNTING_SEMAPHORES | Enabled | Default |
| USE_TIMERS | Enabled | Software timer |
FreeRTOS dengan interface CMSIS_V2, TICK_RATE_HZ 1000 dan MINIMAL_STACK_SIZE 7680 words
Catatan: nilai MINIMAL_STACK_SIZE dan TOTAL_HEAP_SIZE di atas cukup besar karena project asli saya juga menjalankan protokol komunikasi yang butuh banyak memori. Untuk test LED saja, nilai default CubeMX sebenarnya sudah cukup, tapi nilai di atas aman dipakai karena RAM STM32H743 besar.
- Di tab Tasks and Queues, biarkan task bawaan defaultTask: Priority osPriorityNormal, Entry Function StartDefaultTask, Allocation Dynamic. Stack Size otomatis mengikuti MINIMAL_STACK_SIZE, yaitu 7680 words, karena CubeMX tidak mengizinkan stack task lebih kecil dari nilai minimal tersebut.
Tab Tasks and Queues: defaultTask dengan entry function StartDefaultTask
Simpan (Ctrl+S) lalu klik Yes untuk generate code.
Langkah 5 – Menulis program test LED
Setelah generate code, CubeIDE otomatis membuat definisi pin di Core/Inc/main.h:
#define LED_Pin GPIO_PIN_1
#define LED_GPIO_Port GPIOA
Di Core/Src/main.c, fungsi main() sudah berisi urutan inisialisasi FreeRTOS. Kita tidak perlu mengubahnya:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
/* Init scheduler */
osKernelInitialize();
/* Create the thread(s) */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* Start scheduler */
osKernelStart();
/* Program tidak akan sampai ke sini karena sudah diambil alih scheduler */
while (1)
{
}
}
Atribut task yang dibuat CubeMX (stack dalam byte = words × 4):
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 7680 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
Sekarang cari fungsi StartDefaultTask di bagian bawah main.c, lalu isi dengan kode test LED berikut. LED dinyalakan selama 1 detik, lalu dimatikan selama 2 detik:
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // LED PA1 nyala
osDelay(1000); // nyala 1 detik
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // LED PA1 mati
osDelay(2000); // mati 2 detik
}
/* USER CODE END 5 */
}
Kode test LED di dalam StartDefaultTask (main.c)
Pastikan kode ditulis di antara USER CODE BEGIN dan USER CODE END supaya tidak terhapus saat generate code ulang.
Di sini kita memakai HAL_GPIO_WritePin(), bukan HAL_GPIO_TogglePin(), supaya lama nyala dan lama mati bisa diatur berbeda.
Karena timebase HAL dipindah ke TIM1, CubeMX juga membuat callback berikut di main.c. Callback ini yang menjaga HAL_Delay() dan HAL_GetTick() tetap berjalan:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1) {
HAL_IncTick();
}
}
Perhatikan: di dalam task, selalu gunakan osDelay(), bukan HAL_Delay(). osDelay() memberi kesempatan task lain untuk berjalan, sedangkan HAL_Delay() hanya membuat CPU menunggu (busy wait).
Langkah 6 – Build dan upload
- Klik Project → Build Project (atau ikon palu) dan pastikan tidak ada error.
- Sambungkan ST-Link (SWDIO, SWCLK, GND, 3.3V).
- Klik Run → Run As → STM32 C/C++ Application, lalu klik OK pada jendela konfigurasi.
- Kalau muncul tawaran update firmware ST-Link, boleh pilih No untuk melanjutkan upload.
Kalau berhasil, di Console akan muncul tulisan Download verified successfully:
Upload ke STM32H743 (Rev V, flash 2 MB) berhasil diverifikasi
Hasilnya, LED di PA1 akan nyala 1 detik lalu mati 2 detik, berulang terus. Artinya FreeRTOS sudah berjalan: osKernelStart() menjalankan scheduler, lalu scheduler menjalankan defaultTask.
Langkah 7 – Demo multitasking: Queue, Mutex, Semaphore dan Software Timer
Test LED sudah membuktikan FreeRTOS berjalan, tetapi belum memperlihatkan kelebihan RTOS yang sebenarnya. Pada langkah ini kita membuat demo dengan 5 task yang saling berkomunikasi, lalu melihat hasilnya lewat serial monitor.
Project demo ini saya buat terpisah dengan nama H743_FreeRTOS_Demo, dengan konfigurasi yang sama seperti project LED ditambah USART1 untuk mencetak log.
Rancangan demo
| Task | Prioritas | Tugas | Fitur RTOS yang diperlihatkan |
| defaultTask | Normal | LED PA1 nyala 1 detik, mati 2 detik | osDelay |
| sensorTask | Normal | Setiap 500 ms membaca data sensor (simulasi suhu) dan mengirimnya ke queue | Queue, osDelayUntil |
| processTask | AboveNormal | Menunggu data di queue, lalu menghitung rata-rata setiap 4 sampel | Blocking pada queue, preemption |
| eventTask | High | Menunggu sinyal dari software timer setiap 3 detik | Binary semaphore, software timer |
| monitorTask | Low | Setiap 10 detik menampilkan status semua task, sisa stack dan sisa heap | osThreadGetState, osThreadGetStackSpace |
Semua task mencetak ke UART lewat fungsi log_printf() yang dilindungi mutex, supaya teks dari task yang berbeda tidak tercampur.
Konfigurasi USART1 dan sambungan kabel
Di STM32CubeIDE, buka Connectivity → USART1 lalu set Mode ke Asynchronous dengan baudrate 115200, 8 bit, tanpa parity, 1 stop bit. Pin yang dipakai adalah PA9 (TX) dan PA10 (RX).
Board yang saya pakai adalah DevEBox STM32H7XX_M, sedangkan debugger-nya WeAct Mini Debugger. Debugger ini selain untuk upload (SWD) juga punya USB to UART, jadi tidak perlu USB-TTL terpisah. Sambungannya seperti ini (TX dan RX disilang):
| DevEBox STM32H7XX_M | WeAct Mini Debugger |
| PA9 (TX1) | RXD |
| PA10 (RX1) | TXD |
| GND | GND |
| DIO / DCLK (header SWD) | SWDIO / SWCLK |
Sambungan PA9/PA10 board DevEBox ke TXD/RXD WeAct Mini Debugger
Catatan: PA9/PA10 berbeda dengan pin CAN bus. Pada project saya yang lain, FDCAN1 memakai PA11 (RX) dan PA12 (TX), yang letaknya bersebelahan, jadi hati-hati saat memasang kabel.
Membuat objek RTOS
Task tambahan dan objek RTOS dibuat di dalam blok USER CODE pada fungsi main(), supaya tidak hilang saat generate code ulang:
/* USER CODE BEGIN RTOS_MUTEX */
uartMutexHandle = osMutexNew(&uartMutex_attributes);
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* Binary semaphore: nilai maksimum 1, nilai awal 0 (belum ada sinyal) */
eventSemHandle = osSemaphoreNew(1, 0, &eventSem_attributes);
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
eventTimerHandle = osTimerNew(EventTimerCallback, osTimerPeriodic, NULL, &eventTimer_attributes);
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* Queue 8 elemen, tiap elemen berukuran uint32_t */
sensorQueueHandle = osMessageQueueNew(8, sizeof(uint32_t), &sensorQueue_attributes);
/* USER CODE END RTOS_QUEUES */
/* USER CODE BEGIN RTOS_THREADS */
sensorTaskHandle = osThreadNew(StartSensorTask, NULL, &sensorTask_attributes);
processTaskHandle = osThreadNew(StartProcessTask, NULL, &processTask_attributes);
eventTaskHandle = osThreadNew(StartEventTask, NULL, &eventTask_attributes);
monitorTaskHandle = osThreadNew(StartMonitorTask, NULL, &monitorTask_attributes);
/* USER CODE END RTOS_THREADS */
Contoh atribut salah satu task (stack 512 words, prioritas AboveNormal):
const osThreadAttr_t processTask_attributes = {
.name = "processTask",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityAboveNormal,
};
Mutex: printf yang aman untuk banyak task
static void log_printf(const char *fmt, ...)
{
char buf[160];
int len = snprintf(buf, sizeof(buf), "[%7lu ms] ", (unsigned long)osKernelGetTickCount());
va_list args;
va_start(args, fmt);
len += vsnprintf(buf + len, sizeof(buf) - len, fmt, args);
va_end(args);
if (len >= (int)sizeof(buf)) len = sizeof(buf) - 1;
osMutexAcquire(uartMutexHandle, osWaitForever); /* kunci UART */
HAL_UART_Transmit(&huart1, (uint8_t *)buf, (uint16_t)len, 100);
osMutexRelease(uartMutexHandle); /* lepas kunci */
}
Tanpa mutex, dua task yang mencetak hampir bersamaan bisa menghasilkan teks yang campur aduk. Dengan mutex, task kedua akan menunggu (blocked) sampai task pertama selesai memakai UART.
Queue: sensorTask mengirim, processTask menerima
void StartSensorTask(void *argument)
{
uint32_t n = 0;
uint32_t lastWake = osKernelGetTickCount();
for(;;)
{
/* Data simulasi: suhu x10, antara 25,0 C dan 27,9 C */
uint32_t suhu_x10 = 250 + ((n * 7U) % 30U);
log_printf("[SENSOR ] baca #%lu = %lu.%lu C -> kirim ke queue\r\n",
(unsigned long)n, (unsigned long)(suhu_x10 / 10), (unsigned long)(suhu_x10 % 10));
osMessageQueuePut(sensorQueueHandle, &suhu_x10, 0, 0);
n++;
/* Periode tetap 500 ms, tidak terpengaruh lama proses */
lastWake += 500;
osDelayUntil(lastWake);
}
}
void StartProcessTask(void *argument)
{
uint32_t data, sum = 0, count = 0;
for(;;)
{
/* Blocked sampai ada data di queue, tidak memakai CPU sama sekali */
if (osMessageQueueGet(sensorQueueHandle, &data, NULL, osWaitForever) == osOK)
{
sum += data;
count++;
log_printf("[PROCESS] terima %lu.%lu C (sampel %lu/4)\r\n",
(unsigned long)(data / 10), (unsigned long)(data % 10), (unsigned long)count);
if (count >= 4)
{
uint32_t avg = sum / count;
log_printf("[PROCESS] >>> rata-rata 4 sampel = %lu.%lu C\r\n",
(unsigned long)(avg / 10), (unsigned long)(avg % 10));
sum = 0;
count = 0;
}
}
}
}
Karena processTask berprioritas lebih tinggi, begitu osMessageQueuePut() dipanggil, scheduler langsung memindahkan CPU ke processTask (preemption). Di log terlihat baris [PROCESS] muncul hanya sekitar 5 ms setelah [SENSOR].
Software timer dan semaphore
/* Dipanggil oleh Timer Service Task FreeRTOS setiap 3 detik */
void EventTimerCallback(void *argument)
{
osSemaphoreRelease(eventSemHandle); /* beri sinyal */
}
void StartEventTask(void *argument)
{
uint32_t events = 0;
osTimerStart(eventTimerHandle, 3000);
for(;;)
{
/* Tunggu sinyal dari timer */
if (osSemaphoreAcquire(eventSemHandle, osWaitForever) == osOK)
{
events++;
log_printf("[EVENT ] semaphore diterima dari timer (event ke-%lu)\r\n",
(unsigned long)events);
}
}
}
Pola yang sama dipakai untuk menunggu sinyal dari interrupt, misalnya tombol ditekan atau data UART masuk. Interrupt cukup memanggil osSemaphoreRelease(), lalu pekerjaan yang lebih berat dikerjakan oleh task.
Monitor: melihat isi "dapur" RTOS
void StartMonitorTask(void *argument)
{
const osThreadId_t tasks[] = { defaultTaskHandle, sensorTaskHandle, processTaskHandle,
eventTaskHandle, monitorTaskHandle };
for(;;)
{
osDelay(10000);
for (uint32_t i = 0; i < 5; i++)
{
log_printf("[MONITOR] %-12s prio=%2d state=%-8s sisa stack=%5lu byte\r\n",
osThreadGetName(tasks[i]), (int)osThreadGetPriority(tasks[i]),
state_name(osThreadGetState(tasks[i])),
(unsigned long)osThreadGetStackSpace(tasks[i]));
}
log_printf("[MONITOR] jumlah task=%lu sisa heap=%lu byte isi queue=%lu\r\n",
(unsigned long)osThreadGetCount(), (unsigned long)xPortGetFreeHeapSize(),
(unsigned long)osMessageQueueGetCount(sensorQueueHandle));
}
}
Hasil di serial monitor
Setelah program di-upload, buka serial monitor di port COM milik Mini Debugger (di laptop saya COM7) dengan baudrate 115200. Di STM32CubeIDE bisa lewat Console → Open Console → Command Shell Console → Serial Port. Hasilnya seperti ini:
Log demo FreeRTOS: SENSOR, PROCESS, EVENT dan MONITOR berjalan bersamaan
Dari log di atas bisa dilihat:
- Queue dan preemption: setiap 500 ms
[SENSOR] mengirim data, dan sekitar 5 ms kemudian [PROCESS] langsung menerimanya. Setiap 4 sampel muncul hasil rata-rata.
- Software timer dan semaphore:
[EVENT] muncul tepat setiap 3 detik.
- Monitor: ada 7 task yang berjalan, yaitu 5 task buatan kita ditambah Idle task dan Timer Service task bawaan FreeRTOS. Saat laporan dibuat,
monitorTask berstatus Running, sedangkan task lain Blocked karena sedang menunggu delay, data, atau sinyal. Artinya CPU tidak terbuang untuk menunggu.
- Sisa stack setiap task masih 1,3–2,4 KB, dan sisa heap 29.120 byte. Informasi ini berguna untuk menentukan ukuran stack yang pas.
- Mutex: tidak ada baris log yang tercampur, walaupun 5 task mencetak ke UART yang sama.
Sementara itu LED PA1 tetap berkedip dengan pola 1 detik nyala dan 2 detik mati, tanpa terganggu oleh task lain.
Catatan tentang revisi chip STM32H743VIT6
Ada dua revisi STM32H743 di pasaran. Revisi V bisa berjalan sampai 480 MHz, sedangkan revisi Y (lebih lama) hanya sampai 400 MHz. Revisinya bisa dilihat dari huruf terakhir pada kode di badan chip, atau dari baris Revision ID di Console saat upload (lihat gambar di atas, chip saya Rev V). Pada tutorial ini clock hanya 75 MHz, jadi kedua revisi aman dipakai.
Troubleshooting
- Pola kedip terbalik (nyala 2 detik, mati 1 detik): LED di board kalian aktif-low (menyala saat pin LOW). Tukar saja
GPIO_PIN_SET dan GPIO_PIN_RESET di kode.
- LED tidak berkedip: cek apakah LED di board memang terhubung ke PA1. Kalau LED di board kalian ada di pin lain, ganti pin di CubeMX dan beri label
LED yang sama, maka kode tidak perlu diubah.
- Program masuk HardFault: biasanya karena stack task kurang. Naikkan Stack Size task, terutama task yang memakai
sprintf/printf.
- Program berhenti di
configASSERT: cek prioritas interrupt. Interrupt yang memanggil API FreeRTOS (fungsi berakhiran FromISR) harus punya prioritas ≥ 5 (angka lebih besar = prioritas lebih rendah). Nilai ini ditentukan oleh configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY di FreeRTOSConfig.h.
- Muncul peringatan SysTick saat generate code: Timebase Source di SYS belum diubah ke TIM1.
- Muncul pesan "No ST-LINK detected": ST-Link belum tersambung ke laptop. Sambungkan lalu klik Run lagi.
- Board tidak bisa di-upload / terkunci: aktifkan Trace and Debug → DEBUG → Serial Wire supaya pin SWD (PA13/PA14) tidak dipakai fungsi lain. Kalau terlanjur terkunci, gunakan mode Connect Under Reset di STM32CubeProgrammer.
Penutup
Itulah cara dasar mengimplementasikan FreeRTOS di STM32H743VIT6 menggunakan STM32CubeIDE, dengan test LED berkedip sebagai pembuktian. Lewat demo multitasking kita juga sudah melihat cara kerja Queue, Mutex, Semaphore dan Software Timer. Dari sini kalian bisa mengembangkan project dengan sensor sungguhan, interrupt, atau komunikasi seperti CAN bus. InsyaAllah akan dibahas di tutorial berikutnya.
Semoga bermanfaat. Kalau ada pertanyaan, silakan tulis di kolom komentar.
Wassalamu'alaikum warohmatulohi wabarakatuh.