Melakukan Porting MicroPython¶
Proyek MicroPython berisi beberapa port ke berbagai keluarga dan arsitektur mikrokontroler. Repositori proyek memiliki direktori ports yang berisi subdirektori untuk setiap port yang didukung.
Sebuah port biasanya berisi definisi untuk beberapa "board", masing-masing merupakan perangkat keras tertentu yang dapat dijalankan oleh port tersebut, misalnya kit pengembangan atau perangkat.
Port minimal tersedia sebagai implementasi referensi yang disederhanakan dari sebuah port MicroPython. Ini dapat berjalan baik di sistem host maupun MCU STM32F4xx.
Secara umum, memulai sebuah port memerlukan:
Menyiapkan toolchain (mengonfigurasi Makefile, dll.).
Mengimplementasikan konfigurasi boot dan inisialisasi CPU.
Menginisialisasi driver dasar yang diperlukan untuk pengembangan dan debugging (misalnya GPIO, UART).
Melakukan konfigurasi spesifik board.
Mengimplementasikan modul spesifik port.
Firmware MicroPython minimal¶
Cara terbaik untuk mulai melakukan porting MicroPython ke board baru adalah dengan mengintegrasikan interpreter MicroPython minimal. Untuk panduan ini, buat subdirektori untuk port baru di direktori ports:
$ cd ports
$ mkdir example_port
Firmware MicroPython dasar diimplementasikan dalam file port utama, misalnya main.c:
#include "py/builtin.h"
#include "py/compile.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/pyexec.h"
// Allocate memory for the MicroPython GC heap.
static char heap[4096];
int main(int argc, char **argv) {
// Initialise the MicroPython runtime.
mp_cstack_init_with_sp_here(2048);
gc_init(heap, heap + sizeof(heap));
mp_init();
// Start a normal REPL; will exit when ctrl-D is entered on a blank line.
pyexec_friendly_repl();
// Deinitialise the runtime.
gc_sweep_all();
mp_deinit();
return 0;
}
// Handle uncaught exceptions (should never be reached in a correct C implementation).
void nlr_jump_fail(void *val) {
for (;;) {
}
}
// Do a garbage collection cycle.
void gc_collect(void) {
gc_collect_start();
gc_helper_collect_regs_and_stack();
gc_collect_end();
}
// There is no filesystem so stat'ing returns nothing.
mp_import_stat_t mp_import_stat(const char *path) {
return MP_IMPORT_STAT_NO_EXIST;
}
// There is no filesystem so opening a file raises an exception.
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
mp_raise_OSError(MP_ENOENT);
}
Kita juga memerlukan Makefile pada tahap ini untuk port:
# Include the core environment definitions; this will set $(TOP).
include ../../py/mkenv.mk
# Include py core make definitions.
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk
# Set CFLAGS and libraries.
CFLAGS += -I. -I$(BUILD) -I$(TOP)
LIBS += -lm
# Define the required source files.
SRC_C = \
main.c \
mphalport.c \
shared/readline/readline.c \
shared/runtime/gchelper_generic.c \
shared/runtime/pyexec.c \
shared/runtime/stdout_helpers.c \
# Define source files containing qstrs.
SRC_QSTR += shared/readline/readline.c shared/runtime/pyexec.c
# Define the required object files.
OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
# Define the top-level target, the main firmware.
all: $(BUILD)/firmware.elf
# Define how to build the firmware.
$(BUILD)/firmware.elf: $(OBJ)
$(ECHO) "LINK $@"
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
# Include remaining core make rules.
include $(TOP)/py/mkrules.mk
Ingat untuk menggunakan tab yang tepat dalam mengindentasi Makefile.
Konfigurasi MicroPython¶
Setelah mengintegrasikan kode minimal di atas, langkah selanjutnya adalah membuat file konfigurasi MicroPython untuk port. Konfigurasi pada waktu kompilasi ditentukan dalam mpconfigport.h dan fungsi abstraksi perangkat keras tambahan, seperti penjaga waktu, dalam mphalport.h.
Berikut ini adalah contoh file mpconfigport.h:
#include <stdint.h>
// Python internal features.
#define MICROPY_ENABLE_GC (1)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
// Fine control over Python builtins, classes, modules, etc.
#define MICROPY_PY_ASYNC_AWAIT (0)
#define MICROPY_PY_BUILTINS_SET (0)
#define MICROPY_PY_ATTRTUPLE (0)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_MATH (0)
#define MICROPY_PY_IO (0)
#define MICROPY_PY_STRUCT (0)
// Type definitions for the specific machine.
typedef long mp_off_t;
// We need to provide a declaration/definition of alloca().
#include <alloca.h>
// Define the port's name and hardware.
#define MICROPY_HW_BOARD_NAME "example-board"
#define MICROPY_HW_MCU_NAME "unknown-cpu"
#define MP_STATE_PORT MP_STATE_VM
File konfigurasi ini berisi konfigurasi spesifik mesin termasuk aspek seperti apakah berbagai fitur MicroPython harus diaktifkan, misalnya #define MICROPY_ENABLE_GC (1). Menetapkannya ke (0) menonaktifkan fitur tersebut.
Konfigurasi lain mencakup definisi tipe, pointer root, nama board, nama mikrokontroler, dll.
Demikian pula, contoh minimal file mphalport.h terlihat seperti ini:
static inline void mp_hal_set_interrupt_char(char c) {}
Dukungan untuk input/output standar¶
MicroPython memerlukan setidaknya cara untuk mengeluarkan karakter, dan untuk memiliki REPL juga memerlukan cara untuk memasukkan karakter. Fungsi untuk ini dapat diimplementasikan dalam file mphalport.c, misalnya:
#include <unistd.h>
#include "py/mpconfig.h"
// Receive single character, blocking until one is available.
int mp_hal_stdin_rx_chr(void) {
unsigned char c = 0;
int r = read(STDIN_FILENO, &c, 1);
(void)r;
return c;
}
// Send the string of given length.
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
int r = write(STDOUT_FILENO, str, len);
(void)r;
}
Fungsi input dan output ini harus dimodifikasi tergantung pada API board tertentu. Contoh ini menggunakan stream input/output standar.
Membangun dan menjalankan¶
Pada tahap ini direktori port baru seharusnya berisi:
ports/example_port/
├── main.c
├── Makefile
├── mpconfigport.h
├── mphalport.c
└── mphalport.h
Port kini dapat dibangun dengan menjalankan make (atau dengan cara lain, tergantung pada sistem Anda).
Jika Anda menggunakan pengaturan kompilator default dalam Makefile yang diberikan di atas maka ini akan membuat file eksekutabel bernama build/firmware.elf yang dapat dieksekusi langsung. Untuk mendapatkan REPL yang fungsional, Anda mungkin perlu terlebih dahulu mengonfigurasi terminal ke mode raw:
$ stty raw opost -echo
$ ./build/firmware.elf
Itu seharusnya memberikan REPL MicroPython. Anda kemudian dapat menjalankan perintah seperti:
MicroPython v1.26.0-preview on 2025-08-01; minimal with unknown-cpu
>>> def sum(n, m):
... return n + m
...
>>> 3, 4, sum(3, 4)
(3, 4, 7)
>>>
Gunakan Ctrl-D untuk keluar, lalu jalankan reset untuk mereset terminal.
Menambahkan modul ke port¶
Untuk menambahkan modul kustom seperti myport, pertama tambahkan definisi modul dalam file modmyport.c:
#include "py/runtime.h"
static mp_obj_t myport_info(void) {
mp_printf(&mp_plat_print, "info about my port\n");
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(myport_info_obj, myport_info);
static const mp_rom_map_elem_t myport_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_myport) },
{ MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&myport_info_obj) },
};
static MP_DEFINE_CONST_DICT(myport_module_globals, myport_module_globals_table);
const mp_obj_module_t myport_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&myport_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_myport, myport_module);
Anda juga perlu mengedit Makefile untuk menambahkan modmyport.c ke daftar SRC_C, dan baris baru yang menambahkan file yang sama ke SRC_QSTR (sehingga qstr dicari dalam file baru ini), seperti ini:
SRC_C = \
main.c \
modmyport.c \
mphalport.c \
...
SRC_QSTR += modmyport.c
Jika semuanya berjalan dengan benar maka, setelah membangun ulang, Anda seharusnya dapat mengimpor modul baru:
>>> import myport
>>> myport.info()
info about my port
>>>