Portar MicroPython¶
El proyecto MicroPython contiene varios ports a diferentes familias y arquitecturas de microcontroladores. El repositorio del proyecto tiene un directorio ports que contiene un subdirectorio para cada port compatible.
Un port normalmente contiene definiciones para múltiples «placas», cada una de las cuales es una pieza de hardware específica en la que ese port puede ejecutarse, p. ej. un kit de desarrollo o un dispositivo.
El port minimal está disponible como una implementación de referencia simplificada de un port de MicroPython. Puede ejecutarse tanto en el sistema anfitrión como en un MCU STM32F4xx.
En general, iniciar un port requiere:
Configurar la cadena de herramientas (configurar los Makefiles, etc.).
Implementar la configuración de arranque y la inicialización de la CPU.
Inicializar los controladores básicos necesarios para el desarrollo y la depuración (p. ej. GPIO, UART).
Realizar las configuraciones específicas de la placa.
Implementar los módulos específicos del port.
Firmware mínimo de MicroPython¶
La mejor manera de empezar a portar MicroPython a una nueva placa es integrando un intérprete mínimo de MicroPython. Para este recorrido, cree un subdirectorio para el nuevo port en el directorio ports:
$ cd ports
$ mkdir example_port
El firmware básico de MicroPython se implementa en el archivo principal del port, p. ej. 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);
}
También necesitamos un Makefile en este punto para el 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
Recuerde usar tabulaciones adecuadas para indentar el Makefile.
Configuraciones de MicroPython¶
Después de integrar el código mínimo anterior, el siguiente paso es crear los archivos de configuración de MicroPython para el port. Las configuraciones en tiempo de compilación se especifican en mpconfigport.h y las funciones adicionales de abstracción de hardware, como la gestión del tiempo, en mphalport.h.
El siguiente es un ejemplo de un archivo 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
Este archivo de configuración contiene configuraciones específicas de la máquina, incluyendo aspectos como si deben habilitarse diferentes características de MicroPython, p. ej. #define MICROPY_ENABLE_GC (1). Establecer esto a (0) deshabilita la característica.
Otras configuraciones incluyen definiciones de tipos, punteros raíz, nombre de la placa, nombre del microcontrolador, etc.
De forma similar, un ejemplo mínimo de archivo mphalport.h tiene este aspecto:
static inline void mp_hal_set_interrupt_char(char c) {}
Compatibilidad con la entrada/salida estándar¶
MicroPython requiere al menos una forma de generar caracteres, y para tener un REPL también requiere una forma de introducir caracteres. Las funciones para esto pueden implementarse en el archivo mphalport.c, por ejemplo:
#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;
}
Estas funciones de entrada y salida tienen que modificarse dependiendo de la API específica de la placa. Este ejemplo usa el flujo de entrada/salida estándar.
Compilación y ejecución¶
En esta etapa el directorio del nuevo port debería contener:
ports/example_port/
├── main.c
├── Makefile
├── mpconfigport.h
├── mphalport.c
└── mphalport.h
El port ya puede compilarse ejecutando make (o de otro modo, según su sistema).
Si está usando la configuración predeterminada del compilador en el Makefile dado anteriormente, esto creará un ejecutable llamado build/firmware.elf que puede ejecutarse directamente. Para obtener un REPL funcional puede que primero necesite configurar el terminal en modo raw:
$ stty raw opost -echo
$ ./build/firmware.elf
Eso debería darle un REPL de MicroPython. Luego puede ejecutar comandos como:
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)
>>>
Use Ctrl-D para salir, y luego ejecute reset para restablecer el terminal.
Añadir un módulo al port¶
Para añadir un módulo personalizado como myport, primero añada la definición del módulo en un archivo 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);
También necesitará editar el Makefile para añadir modmyport.c a la lista SRC_C, y una nueva línea que añada el mismo archivo a SRC_QSTR (para que se busquen los qstrs en este nuevo archivo), de esta manera:
SRC_C = \
main.c \
modmyport.c \
mphalport.c \
...
SRC_QSTR += modmyport.c
Si todo fue correcto entonces, después de recompilar, debería poder importar el nuevo módulo:
>>> import myport
>>> myport.info()
info about my port
>>>