A MicroPython portolása

A MicroPython projekt számos portot tartalmaz különböző mikrokontroller-családokhoz és architektúrákhoz. A projekt tárolójában van egy ports könyvtár, amely minden támogatott porthoz tartalmaz egy alkönyvtárat.

Egy port jellemzően több „alaplap” (board) definícióját tartalmazza, amelyek mindegyike egy konkrét hardver, amelyen az adott port futtatható, pl. egy fejlesztőkészlet vagy eszköz.

A minimal port egy MicroPython port leegyszerűsített referencia-implementációjaként érhető el. Futtatható mind a gazdarendszeren, mind egy STM32F4xx MCU-n.

Általánosságban egy port elindítása a következőket igényli:

  • A toolchain beállítása (Makefile-ok konfigurálása stb.).

  • A rendszerindítási konfiguráció és a CPU inicializálásának megvalósítása.

  • A fejlesztéshez és hibakereséshez szükséges alapvető meghajtók (pl. GPIO, UART) inicializálása.

  • Az alaplap-specifikus konfigurációk elvégzése.

  • A port-specifikus modulok megvalósítása.

Minimális MicroPython firmware

A MicroPython új alaplapra portolásának legjobb módja egy minimális MicroPython értelmező integrálása. Ehhez a végigvezetéshez hozz létre egy alkönyvtárat az új porthoz a ports könyvtárban:

$ cd ports
$ mkdir example_port

Az alapvető MicroPython firmware a fő port-fájlban van megvalósítva, pl. 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);
}

Ezen a ponton szükségünk van egy Makefile-ra is a porthoz:

# 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

Ne feledd, hogy megfelelő tabulátorokat használj a Makefile behúzásához.

MicroPython konfigurációk

A fenti minimális kód integrálása után a következő lépés a port MicroPython konfigurációs fájljainak létrehozása. A fordításidejű konfigurációk az mpconfigport.h fájlban vannak megadva, a további hardverabsztrakciós függvények, mint például az időmérés, pedig az mphalport.h fájlban.

Az alábbi egy példa egy mpconfigport.h fájlra:

#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

Ez a konfigurációs fájl gépspecifikus konfigurációkat tartalmaz, beleértve olyan szempontokat, hogy a különböző MicroPython funkciókat engedélyezni kell-e, pl. #define MICROPY_ENABLE_GC (1). Ennek (0) értékre állítása letiltja a funkciót.

Más konfigurációk közé tartoznak a típusdefiníciók, a gyökérmutatók, az alaplap neve, a mikrokontroller neve stb.

Hasonlóképpen egy minimális példa mphalport.h fájl így néz ki:

static inline void mp_hal_set_interrupt_char(char c) {}

Szabványos bemenet/kimenet támogatása

A MicroPythonnak legalább a karakterek kiírásának egy módjára van szüksége, és ahhoz, hogy REPL-je legyen, a karakterek beolvasásának egy módjára is szüksége van. Az ehhez szükséges függvények az mphalport.c fájlban valósíthatók meg, például:

#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;
}

Ezeket a bemeneti és kimeneti függvényeket az adott alaplap API-jától függően kell módosítani. Ez a példa a szabványos bemeneti/kimeneti folyamot használja.

Építés és futtatás

Ebben a szakaszban az új port könyvtárának a következőket kell tartalmaznia:

ports/example_port/
├── main.c
├── Makefile
├── mpconfigport.h
├── mphalport.c
└── mphalport.h

A port mostantól a make futtatásával építhető (vagy másként, a rendszeredtől függően).

Ha a fent megadott Makefile alapértelmezett fordítóbeállításait használod, akkor ez létrehoz egy build/firmware.elf nevű végrehajtható fájlt, amely közvetlenül futtatható. Egy működőképes REPL eléréséhez először esetleg nyers (raw) módba kell konfigurálnod a terminált:

$ stty raw opost -echo
$ ./build/firmware.elf

Ennek egy MicroPython REPL-t kell adnia. Ezután olyan parancsokat futtathatsz, mint:

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)
>>>

A kilépéshez használd a Ctrl-D billentyűkombinációt, majd futtasd a reset parancsot a terminál visszaállításához.

Modul hozzáadása a porthoz

Egy egyéni modul, mint a myport hozzáadásához először add hozzá a modul definícióját egy modmyport.c fájlban:

#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);

Szerkesztened kell a Makefile-t is, hogy hozzáadd a modmyport.c fájlt az SRC_C listához, valamint egy új sort, amely ugyanazt a fájlt hozzáadja az SRC_QSTR listához (hogy a qstr-ek ebben az új fájlban is megkeresésre kerüljenek), így:

SRC_C = \
    main.c \
    modmyport.c \
    mphalport.c \
    ...

SRC_QSTR += modmyport.c

Ha minden helyesen ment, akkor az újraépítés után képesnek kell lenned importálni az új modult:

>>> import myport
>>> myport.info()
info about my port
>>>