venerdì , 15 Novembre 2019
iten
Home » Elettronica » Compilare micropython per una scheda STM32Nucleo-F4

Compilare micropython per una scheda STM32Nucleo-F4

Chi mi conosce sa che il mio linguaggio di programmazione preferito è Python. Ho scritto centinaia di migliaia di righe di codice con questo linguaggio, spaziando dalle applicazioni web fino a software dedicati su dispositivi Single Board Computer (SBC) come la Raspberry PI. Tuttavia Python, data la sua natura di linguaggio di programmazione di alto livello e interpretato, non è presente sui sistemi embedded a performance ridotte, come i microcontrollori da poche decine di centesimi di euro, anche se questi stanno diventando sempre più potenti e completi. Per esempio, la piattaforma STM32 annovera nel suo catalogo dei micro a 32bit che costano anche meno di 50 centesimi di euro, ma che sono in grado di offrire prestazioni elevatissime se li si paragona ai micro 8-bit.

Tuttavia, le cose stanno recentemente cambiando, ed esistono diverse iniziative (una di queste è anche italiana) che puntano a portare Python sui microcontrollori di fascia bassa. Micropython, avviata da Damien George, è la più famosa di queste e ha come scopo il porting di Python 3.x verso i micro low-cost. μPython non è un semplice porting uno-a-uno di Python, ma è un riadattamento completo di CPython alle architetture hardware con risorse fisiche estremamente limitate (vi basti pensare che la RAM di questi micro è tipicamente inferiore ai 100k). Inoltre, il progetto μPython è riuscito anche a raccogliere fondi tramite una campagna kickstarter per poter immettere sul mercato due board di sviluppo dedicate: la prima, chiamata PyBoard, è una scheda di sviluppo molto semplice costruita attorno ad un micro STM32F4 di ST; la seconda, WiPy, è invece basata sul micro CC3200 di Texas Instruments, che ingloba un processore Wi-Fi al suo interno. Essendo la PyBoard basata su di un micro STM32F4, e sulla HAL rilasciata da ST, è abbastanza semplice adattare μPython ad altre board con lo stesso micro, anche a quelle sviluppate ad hoc. In questo post vi mostrerò come adattare μPython alla scheda di sviluppo low-cost Nucleo-F401RE di ST, di cui ho già parlato molto in altri tutorial. Dato che non vi è ancora supporto ufficiale a questa scheda, ho forkato l'intero repository github e fatte tutte le modifiche necessarie. P scaricarlo dal mio account github.

Requisiti hardware e software

In questo post assumerò i seguenti requisiti:

  • La disponibilità di una scheda STM32Nucleo-F401RE.
  • Un computer con un sistema operativo UNIX-Like (sia Linux che MacOS vanno bene - non dovrebbe comunque essere difficile adattare le istruzioni ad un PC Windows).
  • Una toolchain GCC correttamente installata e configurata nel percorso ~/STM32Toolchain/gcc-toolchain
    • siete liberissimi di avere la toolchain altrove, ma adattate i path nelle mie istruzioni di conseguenza;
    • se avete bisogno di configurare una toolchain completa per STM32, questa guida vi sarà di aiuto.
  • L'ultima versione di OpenOCD (al momento la 0.9.0) correttamente compilata ed installata in ~/STM32Toolchain/openocd.

Guida veloce per impazienti

Prima che io descriva i cambiamenti effettuati ai sorgenti di μPython, illustrerò la procedura più veloce per programmare la propria scheda Nucleo con  μPython in meno di 5 minuti. Alla fine della procedura di configurazione ed installazione, sarete in grado di eseguire la classica console di Python - anche nota come read–eval–print loop (REPL)) - e cominciare a divertirvi con la vostra scheda Nucleo usando Python.

Come primo step, clonate il mio repository micropython da github nella directory ~/STM32Toolchain:

Adesso, entrate nella sottodirectory micropython/stmhal e lanciate la compilazione con il seguente comando:

Dopo un po' dovreste ottenere i binari del firmware, come mostrato nel seguito:

WOW. Il più è fatto 🙂 A questo punto, la documentazione ufficiale di μPython prosegue nel mostrare come caricare il firmware nella MCU della PyBoard usando la modalità DFU (questo è il modo con cui un microcontrollore STM32 - e in generale tutti i micro a core ARM - può essere programmato senza l'ausilio di circuiti esterni dedicati sulla scheda target). Tuttavia, tutte le board Nucleo sono equipaggiate con un programmatore ST-Link. Questo significa che non c'è modo - a meno di "giocare" con i pin esposti sui connettori "morpho") di programmare direttamente il micro usando il protocollo DFU. Per questo motivo, faremo ricorso ad OpenOCD. Senza lasciare la sottodirectory stmhal, collegate la board Nucleo al PC tramite la porta USB ed eseguite OpenOCD nel seguente modo:

Se tutto è andato per il verso giusto, vi appariranno i seguenti messaggi sul terminale:

Una volta che OpenOCD è connesso all'interfaccia ST-Link della vostra Nucleo, è possibile avviare il trasferimento del firmware di μPython nel micro target. Per fare questo, ci colleghiamo ad OpenOCD tramite una sessione telnet. Apriamo un altro terminale e digitiamo il seguente comando:

Siamo adesso connessi ad OpenOCD e possiamo caricare il firmware con questi comandi di OpenOCD:

Ok. Finito 🙂 Per testare μPython abbiamo bisogno di un emulatore di terminale, come Kermit o PicoCOM. Nei sistemi UNIX-like, la porta seriale virtuale fornita dall'interfaccia ST-Link è mappata con un nome di device simile a /dev/tty.usbmodem1a1313. Tuttavia, controllate nel vostro caso specifico il nome del device. Se usate Kermit, potete adoperare i seguenti comandi per collegarvi alla seriale virtuale:Schermata 2015-06-01 alle 14.48.43Se tutto è andato per il verso giusto, vi apparirà la console di Python. Da lì potrete cominciare a dare comandi. Ad esempio, per verificare che il LED LD2 funziona correttamente, è possibile farlo accendere con la seguente istruzione Python:

Come visto, grazie al lavoro svolto dalla community di μPython, non è stato affatto difficile effettuare il porting su una scheda Nucleo-F4. Se siete interessati a cosa ho modificato per adattare μPython alla Nucleo-F4, potete continuare a leggere il seguito di questo post. Quanto descritto può essere molto utile per adattare μPython ad una board custom che state sviluppando basata su un chip STM32F4.

Come adattare μPython ad una board diversa

Lasciatemi ribadirlo ancora una volta: il processo che sto per descrivervi vale solo per le schede basate su microcontrollore STM32. Per architetture diverse dovrete addentrarvi molto più a fondo nei sorgenti di μPython. Per adattare μPython ad una scheda diversa dalla PyBoard, dovete sostanzialmente seguire questi passi:

  • Creare una sottodirectory in stmhal/boards con il nome della vostra board (nel nostro caso, STM32F4NUCLEO).
  • Inserire all'interno di questa directory:
    • un file chiamato mpconfigboard.h contenente le direttive che descrivono la vostra board (più avanti spiego meglio il contenuto di questo file);
    • un file chiamato mpconfigboard.mk contenete il riferimento alla configurazione delle periferiche associate ai pin del micro, ossia le tabelle che definiscono le  Alternate Functions (AF);
    • un file chiamato pins.csv con la lista dei pin del micro usati (questo dipende da come è fatta la vostra scheda);
    • un file chiamato stm32f4xx_hal_conf.h che contiene la configurazione dell'HAL di STM32Cube (il file generato dal tool STM32CubeMX è più che sufficiente).

Analizziamo nel dettaglio questi file. Il file  mpconfigboard.h è utilizzato per istruire μPython circa le caratteristiche della board hardware. Questo file è sostanzialmente composto di una serie di direttive #define, che configurano la stmhal in base alle caratteristiche della vostra scheda. Nel caso della board STM32Nucleo-F401RE io ho usato queste direttive:

Le differenze tra la Nucleo e la scheda PyBoard sono sostanzialmente riconducibili ad un numero minore di LED utente (solo uno nel caso della Nucleo), diverse configurazioni della UART (per default, la porta seriale virtuale (VCP) dell'interfaccia ST-Link della nostra Nucleo è associata alla UART2), una diversa configurazione dello switch utente (quello blu nel caso della Nucleo) e nessun supporto alle schede SD. Quest'ultimo punto non è banale, in quanto non c'è un modo veloce per caricare i file sorgente con codice Python nella flash della Nucleo, così come invece accade nel caso della PyBoard. Per questo motivo, dobbiamo abilitare la console interattiva di Python (REPL) definendo queste due macro nel file mpconfigboard.h:

Come effettuare il debugging di micropython

Durante il porting di μPython su una scheda differente, può essere molto utile effettuare il debugging del firmware direttamente sul micro target della propria board. Questo è quanto si è reso necessario per le modifiche che io ho effettuato, e credo che sia abbastanza ovvio per chiunque debba adattare il codice ad un'altra scheda. Questo può essere agevolmente fatto usando GDB e OpenOCD. Tuttavia, prima di poter eseguire il debug step-by-step, è necessario compilare μPython con i simboli di debug, nel modo seguente:

Tuttavia, nel caso della scheda Nucleo-F401RE ho dovuto modificare un paio di cose prima di poter effettuare il debugging. Il micro della PyBoard ha ben 1Mb di memoria flash, mentre quello della Nucleo "solo" 512kb. Questo richiede di dover "ridurre" la dimensione del firmware, che altrimenti non riesce ad entrare nella memoria flash.
Ho, quindi, modificato il file Makefile alla linea 57 cambiando l'istruzione da "COPT = -O0" a "COPT = -O1" (questo abilita il primo livello di ottimizzazione del compilatore, il che contribuisce a ridurre in maniera drastica la dimensione del firmware). Inoltre, ho dovuto incrementare la regione FLASH_ISR da 16k a 32k (due blocchi da 16k) tramite la modifica al file di configurazione del linker (stm32f401.ld), come mostrato nel seguito:

Terminata la compilazione, è possibile caricare il firmware nel micro tramite GDB ed OpenOCD. Il primo step consiste nel lanciare GDB con il seguente comando:

Invocando GDB in questo modo, stiamo facendo tre cose:

  • --tui lancia GDB in modalità Text User Interface, una modalità interattiva che permette di vedere il codice sorgente durante la fase di debugging;
  • --eval-command="target remote localhost:3333" automaticamente connette GDB a OpenOCD usando la porta 3333 (la porta sulla quale OpenOCD è in ascolto per connessioni provenienti da GDB)
  • build-STM32F4NUCLEO/firmware.elf dice a GDB di pre-caricare il firmware da caricare sul micro target (sia il codice sia i simboli di debug).

Ok. Adesso possiamo procedere nel caricare il firmware in maniera analoga a come abbiamo visto nella prima parte di questo tutorial:

Una volta completato il caricamento, possiamo piazzare un breakpoint alla funzione main() e cominciare l'esecuzione del firmware:

La seguente capture mostra l'interfaccia di GDB durante il debugging: Schermata 2015-06-02 alle 07.36.03Per eseguire il debug step-by-step è possibile usare i classici comandi GDB come s(tep) per eseguire uno step-in e n(ext) per uno step-out. La descrizione dei comandi di GDB esula dagli scopi di questo tutorial.

Programmare con micropython

Come detto in precedenza, la scheda STM32Nucleo non fornisce un supporto diretto alle memory card SD come, invece, la PyBoard fa. Questo significa che non c'è un modo diretto per caricare sulla scheda i file .py. Ci sono solo due alternative che sono fornite di serie da μPython. La prima è usare la REPL. Tuttavia, questo è molto poco pratico se si vogliono fare le cose sul serio. La seconda possibilità è usare un'interfaccia sviluppata per l'esecuzione di comandi remoti inviati dal PC alla scheda, tramite l'uso di uno specifico strumento. Questo tool è chiamato pyboard ed è disponibile nella directory tools dei sorgenti di μPython. pyboard è estremamente semplice da usare, è può essere adoperato sia come una libreria sia come tool alla riga di comando. Per esempio, supponiamo di aver creato un file denominato foo.py e di volerlo eseguire sulla nostra Nucleo. È possibile invocare pyboard nel modo seguente:

Ogni volta che un file è trasferito verso la scheda target, μPython effettua un soft reset. Questo è molto utile durante la fase di sviluppo, ma richiede che il file deve essere ricaricato ogni qualvolta la scheda viene riavviata. Ho, quindi, introdotto una terza possibilità di programmare la nostra scheda Nucleo: una modalità di trasferimento file nella flash del micro.

Trasferire file .py nella memoria flash del micro STM32

Ogni qualvolta carichiamo μPython sulla nostra Nucleo, l'intera flash è cancellata da OpenOCD. Al primo boot successivo alla programmazione,  μPython ricostruisce un filesystem FAT nella memoria FLASH e lo monta come /flash. μPython crea anche due file chiamato boot.py e main.py. Questo lavoro è svolto dalla funzione init_flash_fs(), definita nel file stmhal/main.c:

Ciò che ho fatto è veramente molto semplice. Ho aggiunto un'ulteriore modalità di interazione quando μPython è in modalità REPL. Inviando la sequenza di controllo CTRL+F si mette μPython nella modalità che ho chiamato file transfer mode. μPython a quel punto aspetta il nome del file che stiamo per caricare, a sua volta seguito dalla sequenza di controllo CTRL+A. Una volta che CTRL+A è inviato alla console, μPython apre il file in modalità di creazione nella partizione /flash e comincia ad aspettare tutti i caratteri contenuti nel file, fintantoché CTRL+A è nuovamente inviato alla console. Quando ciò accade, μPython chiude il file e ritorna in attesa del nome di un nuovo file da creare. μPython esce da questo loop infinito quando viene inviata la sequenza di controllo CTRL+C, effettuando un soft-reset. Quindi, ricapitoliamo:

  1. Inviando CTRL+F alla REPL mette μPython in modalità file transfer.;
  2. μPython aspetta il nome del file che vogliamo creare (o sovrascrivere);
  3. quando finito, un CTRL+A mette μPython in attesa per il contenuto del file;
  4. per ogni carattere inviato, μPython lo inserisce nel file finché CTRL+A è nuovamente inviato;
  5. μPython torna al punto 2 finché non è inviato il carattere di controllo CTRL+C.

In questo modo è possibile salvare interi programmi Python nella flash interna del micro. Ho anche implementato un modo per cancellare i file, semplicemente aggiungendo un "-" prima del nome del file. Ad esempio, per cancellare il file foo.py va semplicemente digitato il nome -foo.py nella fase di richiesta del nome del file.
Per semplificare il processo di upload dei file, ho modificato il tool pyboard.py. Per trasferire una lista di file nella Nucleo, basta digitare:

Invece, per cancellarli:

Se siete curiosi circa le modifiche che ho fatto, vi basterò fare un git diff tra il mio repository è quello ufficiale. Comunque, gran parte del lavoro è svolto dalla routine pyexec_file_upload() nel file pyexec.c.


Check Also

Recensione #4: Stazione saldante JBC CD-2B

Internet è piena di stazioni saldanti a meno di €100. Si tratta di prodotti cinesi, …

One comment

  1. Ciao, complimenti per il post, molto interessante.

    Ne hai fatto uno anche per compilare sotto windows ?

    Grazie

    Matteo

Rispondi a Matteo Annulla risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.