Assalamu'alaikum warohmatulohi wabarakatuh,
tunggu wangsit dulu ya...
Assalamu'alaikum warohmatulohi wabarakatuh,
tunggu wangsit dulu ya...
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.
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.
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 |
osDelay(), osDelayUntil() dan software timer membuat pekerjaan periodik mudah dibuat tanpa menghitung timing secara manual.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.
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:
tasks.c, queue.c, list.c, timers.c, dan lain-lain) dan cukup beberapa kilobyte flash.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) |
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.
H743_FreeRTOS_LED, lalu klik Finish.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).
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.
| 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.
Tab Tasks and Queues: defaultTask dengan entry function StartDefaultTask
Simpan (Ctrl+S) lalu klik Yes untuk generate code.
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).
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.
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.
| 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.
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.
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,
};
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.
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].
/* 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.
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));
}
}
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:
[SENSOR] mengirim data, dan sekitar 5 ms kemudian [PROCESS] langsung menerimanya. Setiap 4 sampel muncul hasil rata-rata.[EVENT] muncul tepat setiap 3 detik.monitorTask berstatus Running, sedangkan task lain Blocked karena sedang menunggu delay, data, atau sinyal. Artinya CPU tidak terbuang untuk menunggu.Sementara itu LED PA1 tetap berkedip dengan pola 1 detik nyala dan 2 detik mati, tanpa terganggu oleh task lain.
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.
GPIO_PIN_SET dan GPIO_PIN_RESET di kode.LED yang sama, maka kode tidak perlu diubah.sprintf/printf.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.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.
Assalamu'alaikum warohmatullahi wabarakatuh,
Pada tulisan kali ini saya akan berbagi cara memprogram mikrokontroler STM32H7 series, khususnya STM32H743 dan STM32H750, menggunakan STM32CubeIDE dan downloader WeAct ST-Link V2.1 lewat jalur SWD (Serial Wire Debug). Modul ini kompatibel dengan ST-LINK V2, murah, dan sudah dilengkapi USB-UART, sehingga cocok untuk belajar. ST-LINK V2 original maupun clone juga tetap bisa dipakai dengan langkah yang sama.
STM32H7 adalah keluarga ARM Cortex-M7 yang sangat kencang. STM32H743 bisa berjalan sampai 480 MHz dengan flash internal 2 MB, sedangkan STM32H750 adalah versi "value line" dengan core yang sama tetapi flash internal hanya 128 KB. Namun, H7 punya beberapa jebakan yang tidak ada di seri F1 atau F4, terutama soal konfigurasi power supply. Kalau salah setting, chip bisa terlihat seperti "mati" setelah di-flash.
Pada tutorial ini saya menggunakan core board DevEBox STM32H7XX_M buatan mcudev (Ver V2.0) dengan chip STM32H743VIT6: kemasan LQFP100, flash internal 2 MB, RAM 1 MB, dan kecepatan maksimal 480 MHz. Board ini juga dilengkapi kristal 25 MHz dan 32,768 kHz, QSPI flash W25Q64 (8 MB), slot microSD, konektor kamera DCMI, header LCD/OLED SPI, tombol RST/K1/K2, serta LED user D2. Dengan flash internal 2 MB, program bisa langsung disimpan di flash internal tanpa perlu QSPI eksternal.
Tampak depan board DevEBox STM32H7XX_M. Header SWD (J1) ada di sebelah port USB.
Yang akan dibahas:
| Item | Keterangan |
|---|---|
| Board STM32H7 | DevEBox STM32H7XX_M (mcudev) dengan STM32H743VIT6; versi STM32H750VBT6 juga tersedia |
| Downloader ST-LINK | Saya memakai WeAct ST-Link V2.1 (Mini Debugger): port USB-C, SWD, dan USB-UART (virtual COM port) dalam satu modul. ST-LINK V2 original atau clone juga bisa dipakai |
| Kabel jumper female–female | Minimal 4 kabel untuk SWD, ditambah 2–3 kabel jika memakai UART |
| Kabel USB | USB-C untuk debugger, dan kabel micro USB untuk board jika board diberi daya sendiri |
| STM32CubeIDE | IDE gratis dari ST, sudah termasuk STM32CubeMX dan driver ST-LINK |
| STM32CubeProgrammer | Untuk flash file .hex/.bin/.elf dan erase chip |
Pastikan driver ST-LINK sudah terpasang. Biasanya driver ikut terpasang bersama STM32CubeIDE. Jika belum, cari paket "STSW-LINK009" di situs ST.
SWD hanya butuh dua jalur data, yaitu SWDIO dan SWCLK, ditambah GND. Pada STM32H7, kedua pin tersebut ada di PA13 dan PA14.
Sebagai downloader saya memakai WeAct ST-Link V2.1. Modul ini kompatibel dengan ST-LINK V2, tetapi memakai firmware ST-LINK V2-1 yang menambahkan USB-UART (virtual COM port). Jadi selain untuk flash dan debug lewat SWD, modul yang sama bisa dipakai untuk melihat output printf dari board tanpa USB-TTL tambahan. Modul ini bernama Mini Debugger V1.0 dari WeAct Studio, dengan konektor 10 pin yang labelnya tercetak di bagian belakang PCB.
Modul WeAct ST-Link V2.1: port USB-C di satu sisi, konektor kabel untuk jalur SWD dan UART di sisi lain.
Label pin WeAct Mini Debugger V1.0 di bagian belakang PCB.
| Kelompok | Pin (urut dari atas) |
|---|---|
| SWD | 3V3, SWDIO, SWCLK, GND, SWO, NRST |
| UART | TXD, RXD, GND, 5V |
Pin SWO dan 5V tidak dipakai di tutorial ini. Pin NRST bisa disambung ke pin NRST board jika tersedia; header J1 di board DevEBox tidak menyediakannya.
Board DevEBox sudah menyediakan header SWD 5 pin (J1) di sebelah port micro USB, dengan label BT0, 3V3, GND, DIO, CLK. Sambungkan seperti tabel berikut:
| Pin WeAct Mini Debugger | Pin header J1 board | Pin STM32H7 | Keterangan |
|---|---|---|---|
| SWDIO | DIO | PA13 | Jalur data dua arah |
| SWCLK | CLK | PA14 | Clock dari ST-LINK |
| GND | GND | GND | Wajib tersambung |
| 3V3 | 3V3 | 3V3 | Opsional; jangan disambung jika board sudah diberi daya dari micro USB |
| (tidak disambung) | BT0 | BOOT0 | Normalnya dibiarkan; BOOT0 sudah ditarik ke GND lewat resistor di board |
Ilustrasi wiring WeAct ST-Link V2.1 ke DevEBox STM32H7XX_M: jalur SWD ke header J1 dan jalur UART ke PA9/PA10.
Header J1 tidak memiliki pin NRST. Jika butuh mode connect under reset, gunakan tombol RST di board (lihat bagian 5 dan 8).
Tampak belakang board DevEBox STM32H7XX_M: label nama pin I/O tercetak di kedua sisi board, header SWD (J1) di dekat port USB.
Beberapa tips:
Karena WeAct ST-Link V2.1 punya USB-UART, sambungkan juga kabel berikut ke USART1 board DevEBox:
| Pin WeAct Mini Debugger | Pin board DevEBox | Keterangan |
|---|---|---|
| TXD | PA10 (RX1) | Data dari PC ke STM32 |
| RXD | PA9 (TX1) | Data dari STM32 ke PC |
| GND | GND | Cukup satu GND jika GND SWD sudah tersambung |
TX selalu disilang ke RX. Di PC, modul ini muncul sebagai port COM bernama STMicroelectronics STLink Virtual COM Port. Buka dengan terminal serial (PuTTY, Tera Term, atau terminal bawaan CubeIDE) pada baud rate yang sama dengan setting USART1, misalnya 115200.
Bagian ini saya praktikkan langsung di STM32CubeIDE 1.17.0 dengan board DevEBox STM32H743VIT6 dan WeAct ST-Link V2.1. Semua gambar di bawah diambil dari software-nya.
Buka STM32CubeIDE, lalu pilih File → New → STM32 Project.
Menu File → New → STM32 Project.
Jendela Target Selection akan terbuka. Saat pertama dibuka, CubeIDE mencoba mengunduh database MCU terbaru. Jika muncul pesan gagal download, klik OK saja; database lokal tetap bisa dipakai.
Di tab MCU/MPU Selector, ketik STM32H743VI pada kolom Commercial Part Number, lalu klik baris STM32H743VIT6 (kemasan LQFP100) di daftar MCU. Klik Next.
Memilih STM32H743VIT6 di MCU/MPU Selector.
Isi Project Name, misalnya H7_Blink. Biarkan Targeted Language = C, Binary Type = Executable, dan Project Type = STM32Cube, lalu klik Finish.
Mengisi nama project H7_Blink.
Akan muncul pertanyaan Memory Protection Unit For Cortex-M7. Pilih Yes agar CubeIDE memasang konfigurasi MPU default yang direkomendasikan untuk Cortex-M7. Setelah itu file H7_Blink.ioc terbuka di tab Pinout & Configuration.
Pada STM32H7, pengaturan debug tidak ada di SYS, melainkan di Trace and Debug → DEBUG. Ubah Debug dari Disable menjadi Serial Wire. Pin PA13 (SWDIO) dan PA14 (SWCLK) akan berubah hijau.
Trace and Debug → DEBUG = Serial Wire. PA13 dan PA14 otomatis terpakai untuk SWD.
Langkah ini paling penting. Jika dibiarkan Disable, setelah program pertama di-flash ST-LINK tidak bisa lagi terhubung secara normal.
Buka System Core → RCC, lalu set High Speed Clock (HSE) = Crystal/Ceramic Resonator. Pin PH0 dan PH1 akan terpakai untuk kristal 25 MHz di board.
Di bagian bawah, buka Parameter Settings → Power Parameters dan pastikan SupplySource = PWR_LDO_SUPPLY. Ini sudah menjadi nilai default dan sesuai dengan board DevEBox yang memakai regulator AMS1117.
RCC: HSE = Crystal/Ceramic Resonator, SupplySource = PWR_LDO_SUPPLY.
LED user D2 di board DevEBox tersambung ke PA1 dan bersifat active low. Klik pin PA1 di gambar chip, lalu pilih GPIO_Output.
Klik pin PA1, lalu pilih GPIO_Output.
Klik kanan PA1 → Enter User Label, lalu isi LED. Setelah itu buka System Core → GPIO, klik baris PA1, dan set GPIO output level = High supaya LED mati saat awal.
Konfigurasi PA1: Output Push Pull, output level High, User Label = LED.
Buka tab Clock Configuration. Input frequency HSE sudah 25 MHz. Lakukan langkah berikut:
400, lalu tekan Enter. CubeIDE akan mencari pembagi clock secara otomatis.Hasilnya: DIVM1 = /2, DIVN1 = ×64, DIVP1 = /2, sehingga SYSCLK = 400 MHz dan HCLK = 200 MHz.
Clock tree: HSE 25 MHz → PLL1 → SYSCLK 400 MHz.
Tekan Ctrl+S untuk menyimpan file .ioc. Saat muncul pertanyaan Do you want generate Code?, pilih Yes. CubeIDE akan membuat kode inisialisasi dan membuka main.c.
Di Core/Src/main.c, cari loop while (1). Tulis kode di antara komentar USER CODE BEGIN WHILE dan USER CODE END WHILE supaya tidak terhapus saat kode di-generate ulang:
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
Kode blink LED di dalam loop while(1).
LED_GPIO_Port dan LED_Pin dibuat otomatis oleh CubeMX dari user label LED. Simpan file, klik project H7_Blink di Project Explorer, lalu pilih Project → Build Project (atau Ctrl+B). Pastikan Console menampilkan Build Finished. 0 errors, 0 warnings.
Build berhasil: 0 errors, 0 warnings. Ukuran program sekitar 8 KB.
Pastikan WeAct ST-Link sudah tercolok ke USB dan kabel SWD sudah tersambung ke header J1 board. Pilih Run → Debug Configurations, lalu klik dua kali STM32 C/C++ Application. CubeIDE membuat konfigurasi baru H7_Blink Debug dengan file Debug/H7_Blink.elf.
Tab Main: project H7_Blink dan file Debug/H7_Blink.elf.
Buka tab Debugger dan atur seperti berikut:
| Opsi | Nilai |
|---|---|
| Debug probe | ST-LINK (ST-LINK GDB server) |
| Interface | SWD |
| Frequency (kHz) | Auto (turunkan ke 1000 jika koneksi gagal) |
| Access port | 0 - Cortex-M7 |
| Reset behaviour | Software system reset, karena header J1 board tidak punya pin NRST. Pilih Connect under reset hanya jika NRST disambung. |
Tab Debugger: ST-LINK GDB server, SWD, Software system reset.
Klik Apply, lalu Debug.
Saat pertama kali terhubung, CubeIDE memeriksa firmware WeAct ST-Link. Jika ada versi lebih baru, muncul pertanyaan untuk update. Pilih Yes untuk update (disarankan), atau No untuk lanjut dengan firmware yang ada. Pada percobaan ini saya pilih No dan upload tetap berhasil.
Pertanyaan update firmware ST-LINK.
Jika yang muncul justru pesan "No ST-LINK detected!", berarti debugger belum terdeteksi. Cek kabel USB WeAct ST-Link dan driver ST-LINK di Device Manager, lalu ulangi Debug.
CubeIDE mem-flash program, menampilkan "Download verified successfully" di Console, lalu pindah ke perspektif Debug. Program berhenti (Suspended : Breakpoint) di awal fungsi main().
Perspektif Debug: "Download verified successfully", program berhenti di main().
Tekan F8 (Resume). Status thread berubah menjadi Running dan LED D2 di board berkedip setiap 500 ms.
Untuk upload berikutnya tanpa masuk mode debug, cukup klik project lalu pilih Run → Run As → STM32 C/C++ Application. Program akan di-flash dan langsung berjalan.
STM32CubeProgrammer berguna untuk mem-flash file hasil build tanpa membuka IDE, misalnya saat produksi, atau untuk erase total chip yang bermasalah.
.elf atau .hex dari folder Debug project. Jika memakai file .bin, isi Start address = 0x08000000.Karena STM32H743VIT6 sudah memiliki flash internal 2 MB, bagian ini tidak diperlukan untuk board yang saya pakai. Catatan berikut hanya untuk pembaca yang memakai STM32H750.
STM32H750 hanya memiliki 128 KB flash internal, satu sektor saja. Untuk program kecil seperti blink, langkah di atas berlaku sama persis dengan H743.
Masalah muncul ketika program membesar, misalnya saat memakai FreeRTOS, LwIP, atau library grafis. Solusinya adalah menyimpan program di QSPI flash eksternal (W25Q64 8 MB juga tersedia pada board DevEBox versi H750) dan menjalankannya dari alamat 0x90000000 (XIP, execute in place). Skemanya:
0x08000000) yang menginisialisasi QSPI dalam mode memory-mapped, lalu melompat ke 0x90000000.0x90000000..stldr) supaya ST-LINK bisa menulis ke QSPI flash. File ini didaftarkan di STM32CubeProgrammer (menu EL) dan di CubeIDE (Debug Configurations → Debugger → External Loaders).Pembahasan lengkapnya akan saya tulis di artikel terpisah.
| Gejala | Kemungkinan penyebab | Solusi |
|---|---|---|
| "No STM32 target found" | Wiring salah atau longgar, GND tidak tersambung, board tidak mendapat daya | Cek ulang SWDIO/SWCLK/GND, pastikan board menyala, gunakan kabel pendek |
| "Target is not responding, retrying..." | Frekuensi SWD terlalu tinggi, atau pin SWD dimatikan oleh program | Turunkan frekuensi ke 1000 kHz atau lebih kecil; tahan tombol RST saat klik Connect |
| Bisa flash sekali, setelah itu tidak bisa connect lagi | Debug masih "Disable" (Trace and Debug → DEBUG), atau Supply Source (LDO/SMPS) salah | Pulihkan chip dengan BOOT0 (lihat di bawah), lalu perbaiki .ioc |
| ST-LINK tidak terdeteksi oleh PC | Driver belum terpasang | Install driver STSW-LINK009, coba port USB lain |
| "ST-LINK firmware is too old" | Firmware ST-LINK lama | Update lewat Help → ST-LINK Upgrade |
| Program H750 gagal di-link ("region FLASH overflowed") | Ukuran program melebihi 128 KB | Optimasi ukuran (-Os) atau pindahkan program ke QSPI |
Jika chip tidak bisa connect sama sekali, biasanya karena setting Supply Source salah atau pin SWD dimatikan. Pulihkan dengan cara berikut:
.ioc (DEBUG = Serial Wire, Supply Source = PWR_LDO_SUPPLY), generate ulang kode, lalu flash kembali.Memprogram STM32H7 dengan ST-LINK sebenarnya tidak jauh berbeda dengan seri STM32 lain. Kuncinya ada di tiga hal: aktifkan DEBUG = Serial Wire, pastikan Supply Source sesuai hardware board, dan manfaatkan pin BT0 di header SWD untuk memulihkan chip yang terkunci. Untuk STM32H750, perhatikan batas flash internal 128 KB sejak awal perencanaan program.
Pada artikel berikutnya, kita akan membahas implementasi FreeRTOS pada STM32H7 series.
Semoga bermanfaat. Jika ada pertanyaan, silakan tulis di kolom komentar.
Wassalamu'alaikum warohmatullahi wabarakatuh.