Ejecutando micropyton en STM32Nucleo-F4

Todos los que me conocen también son conscientes de que mi lenguaje de programación favorito es Python. Escribí cientos de miles de líneas de código en Python, desde aplicaciones web hasta software que se ejecuta en computadoras integradas de una sola tarjeta como la Raspberry PI. Debido a su naturaleza de alto nivel y lenguaje de programación interpretado, Python aún no está presente en los verdaderos microcontroladores integrados, incluso si se están volviendo cada vez más poderosos. Por ejemplo, la plataforma STM32 es una familia de MCU realmente rentable teniendo en cuenta la característica que implementa.

Pero las cosas están cambiando. Micropython es un esfuerzo realmente impresionante de Damien George (y la comunidad) para portar Python 3.xa MCU de bajo costo. μPython no es un simple puerto uno a uno de Python, pero es un esfuerzo complejo para adaptar el CPython oficial a las arquitecturas de hardware con recursos físicos realmente bajos (especialmente RAM). El proyecto μPython también se ha ganado con éxito en fundamentos en kickstarter para dos placas electrónicas: la PyBoard, una placa de prototipos realmente simple basada en la familia STM32F4, y WiPy basada en el chip CC3200 Wi-Fi de TI.

Al ser el PyBoard basado en la familia STM32F4 de MCU y la HAL proporcionada por ST, es bastante sencillo adaptar μPython a otras placas con la misma MCU, incluso a una placa diseñada a medida. En este post te mostraré cómo compilar con éxito μPython para el tablero Nucleo-F401RE, el que ya he usado en mis tutoriales anteriores. Ya que no hay soporte oficial para este tablero, he bifurcado el repositorio de github y he hecho todas las modificaciones necesarias. Puedes descargarlo desde mi cuenta github .

Suposiciones y requisitos

En esta publicación asumiré los siguientes requisitos de hardware y sistema:

  • Usted tiene una placa STM32Nucleo-F401RE .
  • Tiene una PC basada en UNIX (tanto Linux como MacOS son bienvenidos, no debería ser demasiado difícil organizar mis instrucciones en una PC con Windows).
  • Tiene una cadena de herramientas GCC totalmente funcional instalada en ~ / STM32Toolchain / gcc-toolchain
    • eres libre de usar un camino diferente, pero ordena mis instrucciones de acuerdo a eso;
    • Si necesita instalar un GCC / Toolchain completo, puede seguir esta guía .
  • Ha compilado OpenOCD (sugiero la última versión 0.9.0) y lo ha instalado correctamente en ~ / STM32Toolchain / openocd .

Guía rápida para chicos impacientes.

Antes de describir los cambios en la fuente principal de μPython, le mostraré una guía realmente rápida que le permite actualizar su tablero Nucleo con μPython en menos de 5 minutos. Al final de este procedimiento, podrá ejecutar la consola interactiva clásica de Python (también conocida como ciclo de lectura-evaluación-impresión (REPL)) y comenzar a divertirse con ella. En primer lugar, comience a clonar mi repositorio de micropython github dentro del directorio ~ / STM32Toolchain :

1$ git clone https://github.com/cnoviello/micropython

Luego, ingrese dentro del  subdirectorio micropython / stmhal y comience a compilarlo con el siguiente comando:

12$ cd micropython/stmhal$ PATH=~/STM32Toolchain/gcc-toolchain/bin:$PATH BOARD=STM32F4NUCLEO make

Después de un tiempo, debería obtener los binarios de firmware, como se muestra a continuación:

12345LINK build-STM32F4NUCLEO/firmware.elf   text    data     bss     dec     hex filename 247624      92   43636 291352   47218 build-STM32F4NUCLEO/firmware.elfCreate build-STM32F4NUCLEO/firmware.dfuCreate build-STM32F4NUCLEO/firmware.hex

GUAU. Lo mas se hace

En este punto, a continuación se muestra la documentación oficial de μPython que muestra cómo hacer flash en el PyBoard usando el modo DFU (esta es la forma en que las MCU STM32, y todas las MCU basadas en ARM en general, se pueden flashear sin utilizar circuitos adicionales en la placa final). Sin embargo, todas las placas Nucleo proporcionan un depurador ST-Link integrado. Esto significa que no hay manera (a menos que decida jugar con los pines expuestos en el conector morpho) para programar directamente la MCU de destino utilizando el protocolo DFU. Por esta razón, utilizaremos OpenOCD.

Sin abandonar el subdirectorio stmhal , conecte la placa Nucleo a su PC y escriba el siguiente comando:

1~/STM32Toolchain/openocd/src/openocd –search ~/STM32Toolchain/openocd/tcl -f board/st_nucleo_f4.cfg

Si todo está bien, estos mensajes aparecerán en el terminal:

12345678910111213141516Open On-Chip Debugger 0.9.0 (2015-06-01-09:22)Licensed under GNU GPL v2For bug reports, read http://openocd.org/doc/doxygen/bugs.htmlInfo : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWDadapter speed: 2000 kHzadapter_nsrst_delay: 100none separatesrst_only separate srst_nogate srst_open_drain connect_deassert_srstInfo : Unable to match requested speed 2000 kHz, using 1800 kHzInfo : Unable to match requested speed 2000 kHz, using 1800 kHzInfo : clock speed 1800 kHzInfo : STLINK v2 JTAG v20 API v2 SWIM v4 VID 0x0483 PID 0x374BInfo : using stlink api v2Info : Target voltage: 3.261782Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints

Ahora que OpenOCD está conectado a nuestro Nucleo, podemos transferir el binario generado a la MCU de destino. Para hacer esto, necesitamos enviar comandos a OpenOCD a través de una sesión telnet.

Abra otra consola de terminal y escriba el siguiente comando:

12345678$ telnet localhost 4444Trying ::1…telnet: connect to address ::1: Connection refusedTrying 127.0.0.1…Connected to localhost.Escape character is ‘^]’.Open On-Chip Debugger>

Ahora estamos conectados a OpenOCD y podemos comenzar a cargar el firmware con estos comandos:

123> reset halt…> flash write_image erase build-STM32F4NUCLEO/firmware.elf

De acuerdo. Hemos terminado

Para probar μPython necesita un emulador de terminal, como Kermit o PicoCOM. En los sistemas similares a UNIX, el puerto COM virtual de ST-Link generalmente se asigna con un nombre de dispositivo similar a /dev/tty.usbmodem1a1313 . Compruebe exactamente el nombre en su caso. Si tiene kermit, puede escribir los comandos que se muestran en la siguiente imagen.

Pantalla 2015-06-01 a las 14.48.43

Ahora puedes comenzar a divertirte con Python y tu Nucleo. Para comprobar si todo funciona correctamente, puede comenzar a encender el led LD2 (el verde) en la placa usando esta instrucción:

1>>> pyb.LED(1).on()

Como puede ver, es realmente sencillo trasladar μPython a una placa Nucleo-F4. Esto se debe principalmente al excelente trabajo realizado por la comunidad μPython. Pero si está interesado en la historia completa, puede continuar leyendo este tutorial. La información que daré puede ser realmente útil si necesita adaptar μPython a un tablero personalizado basado en la plataforma STM32F4.

Cómo adaptar el microtón a una tabla diferente.

Permítanme aclarar una vez más: el proceso que voy a describir es para tableros basados ​​en STM32. Para diferentes arquitecturas tienes que jugar con fuentes μPython.

Para adaptar las fuentes μPython a un tablero personalizado, necesita esencialmente estos pasos:

  • Cree un subdirectorio dentro de stmhal / boards con el nombre de su placa (en nuestro caso, STM32F4NUCLEO).
  • Lugar dentro de esa dir:
    • un archivo llamado  mpconfigboard.h que contiene la directiva que describe su placa (más adelante ).
    • un archivo llamado  mpconfigboard.mk que contiene un enlace a las tablas de Funciones alternativas (AF) y el script de configuración de GNU LD.
    • un archivo llamado pins.csv con la lista de pines usados ​​(esto depende del diseño del hardware de su placa).
    • un archivo llamado  stm32f4xx_hal_conf.h que contiene las declaraciones de definición e importación para HAL de STM32Cube (el generado por la herramienta STM32CubeMX debería ser suficiente).

Vamos a analizar en profundidad estos archivos.

El archivo  mpconfigboard.h le dice a μPython más sobre las capacidades de la placa. Este archivo está compuesto esencialmente por directivas #define , que habilitan y configuran el stmhal para su tablero específico. En el caso de STM32NucleoF4 usé las siguientes definiciones:

123456789101112131415161718192021222324252627282930313233343536373839404142#define STM32F4NUCLEO #define MICROPY_HW_BOARD_NAME       “F4NUCLEO”#define MICROPY_HW_MCU_NAME         “STM32F401xE” #define MICROPY_HW_HAS_SWITCH       (1)#define MICROPY_HW_ENABLE_RTC       (1) // HSI is 16MHz#define MICROPY_HW_CLK_PLLM (16)#define MICROPY_HW_CLK_PLLN (336)#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV4)#define MICROPY_HW_CLK_PLLQ (7) // UART config#define MICROPY_HW_UART2_PORT (GPIOA)#define MICROPY_HW_UART2_PINS (GPIO_PIN_2 | GPIO_PIN_3)#define MICROPY_HW_UART6_PORT (GPIOC)#define MICROPY_HW_UART6_PINS (GPIO_PIN_6 | GPIO_PIN_7) #define MICROPY_HW_UART_REPL        PYB_UART_2#define MICROPY_HW_UART_REPL_BAUD   115200 // I2C busses#define MICROPY_HW_I2C1_SCL (pin_B6)#define MICROPY_HW_I2C1_SDA (pin_B7) // USRSW is pulled low. Pressing the button makes the input go high.#define MICROPY_HW_USRSW_PIN        (pin_C13)#define MICROPY_HW_USRSW_PULL       (GPIO_NOPULL)#define MICROPY_HW_USRSW_EXTI_MODE  (GPIO_MODE_IT_FALLING)#define MICROPY_HW_USRSW_PRESSED    (0) // LEDs#define MICROPY_HW_LED1             (pin_A5) // Green LD2 LED on Nucleo#define MICROPY_HW_LED_OTYPE        (GPIO_MODE_OUTPUT_PP)#define MICROPY_HW_LED_ON(pin)      (pin->gpio->BSRRL = pin->pin_mask)#define MICROPY_HW_LED_OFF(pin)     (pin->gpio->BSRRH = pin->pin_mask) // USB config#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9)#define MICROPY_HW_USB_OTG_ID_PIN      (pin_A10)

Las diferencias entre Nucleo y PyBoard son principalmente un número menor de LED de usuario (solo uno en el caso de Nucleo), diferentes UART (por defecto, el VCP de Nucleo está asociado a UART2), una configuración diferente para el cambio de usuario (el azul en Nucleo) y no hay soporte externo para tarjetas SD. Este es un aspecto no trivial para el uso de μPython en Nucleo, ya que no hay una forma rápida de cargar archivos fuente de Python como en PyBoard. Por este motivo, tendremos que habilitar la consola interactiva (REPL) agregando estas dos macros dentro del archivo mpconfigboard.h ::

20212223…#define MICROPY_HW_UART_REPL        PYB_UART_2#define MICROPY_HW_UART_REPL_BAUD   115200…

Cómo depurar micropython

Durante el puerto de μPython a una placa diferente, podría ser realmente útil hacer una depuración en vivo del firmware cargado. Esto me sucedió y creo que esta es una situación muy común. Para realizar una depuración paso a paso, puede utilizar GDB junto con OpenOCD. Antes de depurar μPython, debe compilarlo con símbolos de depuración. Esto se hace con los siguientes comandos:

1$ PATH=~/STM32Toolchain/gcc-toolchain/bin:$PATH BOARD=STM32F4NUCLEO DEBUG=1 make

Sin embargo, tuve que modificar un par de cosas para permitir la depuración de μPython en mi Nucleo. El PyBoard MCU tiene 1Mb de flash, mientras que el Nucleo MCU tiene solo 512kb. Así que tuve que modificar el Makefile en la línea 57 cambiando el ” COPT = -O0 ” a ” COPT = -O1 ” (esto habilita el primer nivel de optimización del compilador reduciendo dramáticamente la dimensión binaria). Además, tuve que aumentar la región FLASH_ISR de 16k a 32k (dos bloques de 16k) dentro del archivo de script del vinculador de GNU (stm32f401.ld), como se muestra a continuación:

1234567891011121314/*    GNU linker script for STM32F401*/ /* Specify the memory areas *//* TODO verify these regions */MEMORY{    FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 0x080000 /* entire flash, 512 KiB */    FLASH_ISR (rx)  : ORIGIN = 0x08000000, LENGTH = 0x008000 /* sector 0,1  2×16 KiB */    FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 0x080000 /* sectors 5,6,7,8, 4*128KiB = 512 KiB */    RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 0x018000 /* 96 KiB */}…

Cuando se genera el binario del firmware, puede cargarlo y depurarlo utilizando GDB y OpenOCD. Para hacer esto, inicie GDB con el siguiente comando:

1~/STM32Toolchain/gcc-toolchain/bin/arm-none-eabi-gdb -tui –eval-command=”target remote localhost:3333″ build-STM32F4NUCLEO/firmware.elf

Al invocar GDB de esta manera, estamos haciendo tres cosas:

  • –tui inicia GDB con la interfaz de usuario de texto, un modo que permite ver el código fuente durante la depuración;
  • –eval-command = “target localhost remoto: 3333” conecta automáticamente GDB a OpenOCD usando el puerto 3333 (el puerto donde OpenOCD espera la conexión de GDB)
  • build-STM32F4NUCLEO / firmware.elf le dice a GDB que cargue el firmware binario (tanto el código como los símbolos de depuración).

De acuerdo. Ahora podemos cargar el firmware a Nucleo MCU de la misma manera que antes:

1234567891011(gdb) monitor reset halttarget state: haltedtarget halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x0804b928 msp: 0x20018000(gdb) loadLoading section .isr_vector, size 0x4070 lma 0x8000000Loading section .text, size 0x50230 lma 0x8020000Loading section .data, size 0x5c lma 0x8070230Start address 0x804b928, load size 344828Transfer rate: 23 KB/sec, 14367 bytes/write.(gdb)

Una vez completado, podemos colocar un punto de interrupción en la función main () e iniciar la ejecución:

1234567(gdb) break mainBreakpoint 1 at 0x80440a4: file main.c, line 247. (gdb) monitor reset halttarget state: haltedtarget halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x0804b928 msp: 0x20018000(gdb) continue

La siguiente captura de pantalla muestra la interfaz GDB durante la depuración:

Pantalla 2015-06-02 a 07.36.03

Para realizar una depuración paso a paso, puede usar los comandos GDB clásicos como s (tep) para hacer el paso y la n (ext) para hacer una depuración.

Programación con micropython.

Como dije antes, STM32Nucleo no proporciona soporte para tarjetas SD externas como lo hace PyBoard. Esto significa que no hay una manera directa de cargar archivos .py de origen y verificar su ejecución. Solo hay dos opciones que surgieron de la caja con el árbol de fuente estándar de μPython. El primero es usar el REPL. Sin embargo, esto es bastante incómodo si quieres hacer cosas serias. La segunda opción es usar una interfaz conveniente disponible en μPython que permita ejecutar comandos remotos en la placa de destino desde una PC. Esta herramienta se llama pyboard y está disponible dentro del subdirectorio de herramientas en el árbol principal de μPython.

Pyboard  es muy fácil de usar. Se puede usar tanto como una biblioteca como una herramienta de línea de comandos. Por ejemplo, suponga que ha creado un archivo fuente llamado foo.py y desea ejecutarlo en el Nucleo. Puedes invocar  Pyboard  de esta manera:

1$ ./pyboard.py –device=/dev/tty.usbmodem1a1313 foo.py

Cada vez que se transfiere un archivo a PyBoard, μPython realiza un reinicio por software. Esta opción es realmente útil durante el proceso de desarrollo. Sin embargo, esto requiere volver a cargar las instrucciones del programa cada vez que la placa se reinicie.

Introduje una tercera forma de programar nuestra placa Nucleo con μPython: un modo de transferencia de archivos.

Transfiere archivos .py a la memoria flash interna

Cada vez que destellamos nuestro tablero Nucleo con μPython, se borra todo el flash. En el primer arranque después de la programación flash, μPython recrea un sistema de archivos FAT interno montado como  / flash . μPython también crea un archivo llamado boot.py y otra que se llama main.py . Esta tarea se realiza mediante la función   init_flash_fs () , dentro del archivo stmhal / main.c :

160161162163164165166167168169170171172173174175176177178179180181182183184185186187188void init_flash_fs(uint reset_mode) {    // try to mount the flash    FRESULT res = f_mount(&fatfs0, “/flash”, 1);     if (reset_mode == 3 || res == FR_NO_FILESYSTEM) {        // no filesystem, or asked to reset it, so create a fresh one         // LED on to indicate creation of LFS        led_state(PYB_LED_R2, 1);        uint32_t start_tick = HAL_GetTick();         res = f_mkfs(“/flash”, 0, 0);        if (res == FR_OK) {            // success creating fresh LFS        } else {            __fatal_error(“could not create LFS”);        }         // set label        f_setlabel(“/flash/pybflash”);         // create empty main.py        FIL fp;        f_open(&fp, “/flash/main.py”, FA_WRITE | FA_CREATE_ALWAYS);        UINT n;        f_write(&fp, fresh_main_py, sizeof(fresh_main_py) – 1 /* don’t count null terminator */, &n);        // TODO check we could write n bytes        f_close(&fp);…

Lo que he implementado es realmente simple. He añadido otro modo disponible en el REPL. Al pulsar CTRL + F se pone μPython en el modo de transferencia de archivos . μPython espera el nombre del archivo, seguido del carácter CTRL + A. Cuando se envía CTRL + A al REPL, μPython abre para crear el archivo en la partición / flash y comienza a esperar todos los caracteres contenidos en el archivo, hasta que se envía otro CTRL + A al REPL. Cuando esto sucede, μPython cierra el archivo y comienza a esperar otro nombre de archivo. μPython sale de este bucle cuando se envía un carácter CTRL + C, y realiza un reinicio por software.

Entonces, recapitulemos el procedimiento:

  1. El envío de CTRL + F a REPL pone a μPython en el modo de transferencia de archivos;
  2. μPython espera el nombre del archivo que queremos crear o sobrescribir;
  3. cuando termina, un CTRL + A pone μPython esperando el contenido del archivo;
  4. para cada carácter enviado en la consola, μPython lo coloca dentro del archivo hasta que se envía un CTRL + A;
  5. μPython volver al punto 2 hasta que se envíe un CTRL + C.

De esta manera es posible guardar el programa python dentro del flash Nucleo. También he implementado una forma de eliminar archivos, simplemente agregando un “-” antes del nombre del archivo. Por ejemplo, para eliminar el archivo foo.py, simplemente escriba -foo.py cuando se le solicite un nombre de archivo.

Para simplificar la carga del archivo, he modificado las herramientas de archivo / pyboard.py. Para transferir una lista de archivos a Nucleo, simplemente ejecute:

1$ ./pyboard.py –device=/dev/tty.usbmodemfd1313 –send filename

En su lugar, para eliminar una ejecución de archivo:

1$ ./pyboard.py –device=/dev/tty.usbmodemfd1313 –delete filename

Si tienes curiosidad por lo que he hecho, puedes hacer una diferencia entre mi repo y la oficial. Sin embargo, la mayor parte del trabajo lo realiza la función pyexec_file_upload () en el archivo pyexec.c .

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *