Портирование MicroPython¶
Проект MicroPython содержит несколько портов на различные семейства микроконтроллеров и архитектуры. В репозитории проекта есть каталог ports, содержащий подкаталог для каждого поддерживаемого порта.
Порт обычно содержит определения для нескольких «плат», каждая из которых представляет собой конкретное аппаратное обеспечение, на котором этот порт может работать, например отладочный комплект или устройство.
Минимальный порт доступен в качестве упрощённой эталонной реализации порта MicroPython. Он может работать как на хост-системе, так и на MCU STM32F4xx.
В общем случае для начала работы над портом требуется:
Настройка инструментария (конфигурирование Makefile и т. д.).
Реализация конфигурации загрузки и инициализации ЦП.
Инициализация базовых драйверов, необходимых для разработки и отладки (например, GPIO, UART).
Выполнение специфичных для платы конфигураций.
Реализация специфичных для порта модулей.
Минимальная прошивка MicroPython¶
Лучший способ начать портирование MicroPython на новую плату — интегрировать минимальный интерпретатор MicroPython. Для этого пошагового руководства создайте подкаталог для нового порта в каталоге ports:
$ cd ports
$ mkdir example_port
Базовая прошивка MicroPython реализована в главном файле порта, например 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);
}
На этом этапе нам также нужен Makefile для порта:
# 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
Не забудьте использовать правильные табуляции для отступов в Makefile.
Конфигурации MicroPython¶
После интеграции минимального кода, приведённого выше, следующим шагом является создание файлов конфигурации MicroPython для порта. Конфигурации времени компиляции указываются в mpconfigport.h, а дополнительные функции аппаратной абстракции, такие как ведение времени, в mphalport.h.
Ниже приведён пример файла 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
Этот файл конфигурации содержит специфичные для устройства конфигурации, включая такие аспекты, как нужно ли включать те или иные возможности MicroPython, например #define MICROPY_ENABLE_GC (1). Установка этого значения в (0) отключает данную возможность.
Другие конфигурации включают определения типов, корневые указатели, имя платы, имя микроконтроллера и т. д.
Аналогично минимальный пример файла mphalport.h выглядит так:
static inline void mp_hal_set_interrupt_char(char c) {}
Поддержка стандартного ввода/вывода¶
MicroPython требует как минимум способа вывода символов, а для наличия REPL ему также требуется способ ввода символов. Функции для этого можно реализовать в файле mphalport.c, например:
#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;
}
Эти функции ввода и вывода необходимо изменить в зависимости от конкретного API платы. В этом примере используется стандартный поток ввода/вывода.
Сборка и запуск¶
На этом этапе каталог нового порта должен содержать:
ports/example_port/
├── main.c
├── Makefile
├── mpconfigport.h
├── mphalport.c
└── mphalport.h
Теперь порт можно собрать, выполнив make (или иным образом, в зависимости от вашей системы).
Если вы используете настройки компилятора по умолчанию в приведённом выше Makefile, то это создаст исполняемый файл с именем build/firmware.elf, который можно выполнить напрямую. Чтобы получить функциональный REPL, вам может потребоваться сначала настроить терминал в режим raw:
$ stty raw opost -echo
$ ./build/firmware.elf
Это должно дать вам REPL MicroPython. Затем вы можете выполнять такие команды, как:
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)
>>>
Используйте Ctrl-D для выхода, а затем выполните reset для сброса терминала.
Добавление модуля в порт¶
Чтобы добавить пользовательский модуль, такой как myport, сначала добавьте определение модуля в файл 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);
Вам также потребуется отредактировать Makefile, чтобы добавить modmyport.c в список SRC_C, и новую строку, добавляющую тот же файл в SRC_QSTR (чтобы qstr-ы искались в этом новом файле), вот так:
SRC_C = \
main.c \
modmyport.c \
mphalport.c \
...
SRC_QSTR += modmyport.c
Если всё прошло правильно, то после пересборки вы сможете импортировать новый модуль:
>>> import myport
>>> myport.info()
info about my port
>>>