in Elettronica, Embedded Logs, Informatica

Embedded Logs – From scratch pt.1: Setup dell’ambiente AVR-GCC con Make & CMake

Avete mai voluto creare una vostra scheda per un dispositivo, avendo completo controllo del processo di progettazione hardware e software? Questo articolo è il primo di una serie sul progettare ed implementare un sistema basato su microcontrollori AVR ATMega da zero, includendo dettagli relativi all’alimentazione del chip, strumentazione, installazione del compilatore e via dicendo.

Il motivo per cui ho iniziato ad interessarmi di informatica tanti anni fa era legato al volere conoscere ogni dettaglio del processo di funzionamento di un computer, e questa serie vuole essere un piccolo estratto di una fase di questa ricerca.

Il progetto

L’idea è semplice: costruire una scheda basata su un microcontrollore ATMega, partendo con una semplice breadboard, il chip, e qualche componente. Una volta fatto questo, la natura prototipale del tutto si presterà facilmente ad estendere il caso d’uso a qualsiasi tipo di progetto gestibile dal chip.

Un altro livello di controllo che voglio avere è quello relativo agli strumenti di sviluppo: non voglio usare un IDE ma cercare di avere più controllo possibile sul processo tramite utilizzo di un editor moderno e di un sistema di compilazione basato su Makefile (con o senza CMake).

Fortunatamente il supporto per l’architettura AVR (su cui i chip ATMega sono basati) nella comunità open source è di ottimo livello: il progetto GNU fornisce infatti il compilatore avr-gcc (che di default offre un front-end per il linguaggio C ed uno per C++), oltre che il debugger avr-gdb. Il progetto avrdudes si occupa invece di fornire una implementazione aggiornata della avr-libc e dello strumento principale per interfacciarsi con il chip stesso: avrdude.

Il chip selezionato per questo scopo è un ATMega644, ma qualsiasi altro ATMega con taglio di memoria flash entro i 128K va più che bene. Un comodo particolare di questi chip è quello relativo al loro packaging: sono infatti quasi tutti disponibili nel package PDIP through hole, che li rende di facile utilizzo direttamente su breadboard.

Un ATMega644, con architettura AVR

Ambiente di sviluppo AVR

Per sviluppo di applicazioni per microcontrollori o per qualsiasi altro target, ho due editor/IDE che preferisco rispetto a qualsiasi altra opzione: VScode/codium e JetBrains CLion: mi concentrerò sul primo in quanto gratuito ed open-source (o circa!).

Per quanto riguarda l’installazione del compilatore e degli altri strumenti, ci sono diverse opzioni disponibili a seconda del sistema operativo in uso e della necessità di una versione più o meno recente. In questi articoli e nella mia esperienza privata utilizzo Ubuntu 20.04 su CPU intel x86_64, ma l’approccio è stato testato anche su altre macchine con successo.

Installazione di GCC, ecc. su Linux Ubuntu tramite apt

Tutti gli strumenti necessari a partire con lo sviluppo su Ubuntu sono ottenibili tramite apt:

sudo apt install gcc-avr gdb-avr avrdude avr-libc

L’unica nota negativa di questo approccio è che il compilatore condiviso sul repository apt è piuttosto datato. Per ovviare a questo problema, propongo una soluzione che consiste nel compilare avr-gcc da sorgente qualche paragrafo più sotto.

In ogni caso, quanto installabile tramite apt va più che bene per i nostri scopi.

Nel caso in cui vogliate utilizzare CMake, è possibile reperirne il package da apt, o installarne una versione più recente utilizzando la repository APT di Kitware (solo per Ubuntu <= 20.04), seguendo le istruzioni ufficiali.

Installazione di GCC, ecc. su Windows (Mingw64/MSYS2) tramite pacman

Un semplice modo di installare un ambiente di compilazione su Windows è quello di usare MSYS2, scaricabile da questo link. MSYS2 è un insieme di librerie e strumenti che include pacman come gestore di pacchetti. Usando il package repository mingw64, è possibile installare quanto ci serve tramite il seguente comando:

pacman -S mingw-w64-x86_64-avr-binutils \
          mingw-w64-x86_64-avr-gcc \
          mingw-w64-x86_64-avr-gdb \
          mingw-w64-x86_64-avr-libc \
          mingw-w64-x86_64-avrdude

Una volta fatto ciò, aggiungendo la cartella dove gli eseguibili sono stati installati alla variabile d’ambiente PATH, potremo far sì che siano visibili anche fuori dall’ambiente MSYS2/MINGW64. Se, per esempio, MSYS2 è stato installato in C:\msys64 (il percorso standard), bisognerà aggiungere questi due percorsi alla variabile PATH:

c:\msys64\usr\bin
c:\msys64\mingw64\usr\bin

Anche su MSYS è possibile installare CMake dal repository mingw64 tramite pacman:

pacman -S mingw-w64-x86_64-cmake

Compilazione da sorgente su Linux tramite docker

Per ovviare alla problematica delle versioni non recenti, ho creato un container docker che include un ambiente con tutte le dipendenze necessarie per compilare avr-gcc, avr-binutils e avr-libc da sorgente.

Lo potete trovare nel github antima @github/antima/avr-gcc-env, per compilare tutto bisogna prima decidere che versione delle applicazioni desiderate compilare. Ad esempio, per replicare quanto ho generato nel mio setup, potete eseguire i seguenti comandi:

git clone https://github.com/antima/avr-gcc-env
docker build \ --build-arg BINUTILS_VERSION=2.38 \ --build-arg GCC_VERSION=11.3.0 \ --build-arg LIBC_VERSION=2.1.0 \ -t avr-gcc-env:11.3.0-binutils-2.38-libc-2.1.0 .
docker run --rm -v $(pwd)/avr-env:/local/avr avr-gcc-env:11.3.0-binutils-2.38-libc-2.1.0

Il processo impiegherà un po’ di tempo, anche a seconda del processore in uso. Una volta arrivato alla fine, avrete tutto l’ambiente di compilazione (inclusi strumenti di utilità) nella sotto-cartella avr-env contenuta nella repo clonata.

Potete a questo punto spostare la cartella avr-env/avr in una posizione qualsiasi del filesystem (a me piace posizionarla in ~/.local), ed inserire la sotto-cartella avr-env/avr/bin nel PATH.

Setup di VScode/codium

VScodium è la versione a licenza MIT e senza telemetria di VScode, con cui condivide il core applicativo: è la mia opzione preferita tra le due ma qualsiasi versione utilizziate andrà bene. Avevo parlato di questa piattaforma in un articolo sullo sviluppo embedded, sempre qui su antima.

Utilizzo principalmente tre plugin, che possono essere installati direttamente dal MarketPlace all’interno di vscode:

  • CMake/CMake tools, per il supporto CMake e per il syntax-highlighting.
  • clangd, per la code-completion, ricerca contestuale, errori in-editor, ecc.
  • Native Debug, per avere la possibilità di debuggare applicazioni direttamente nell’editor tramite gdb.

Makefile o CMake?

Questa è parzialmente una preferenza personale: generalmente, quando sviluppo su un ambiente unix-like da solo o con altre persone con ambienti simili, trovo di più semplice utilizzo un setup con un Makefile e make.

Quando però si vuole lavorare su più sistemi diversi, si ha a cuore la problematica del multi-piattaforma, e non si vuole stare a modificare Makefile a mano, e pensare a problematiche di compatibilità CMake è la soluzione vincente. In particolare CMake è un meta-build system, con lo scopo di generare build system, che poi possono essere basati su make, ninja, o qualsiasi altro sistema supportato.

In ogni caso, qualsiasi sia la vostra scelta, sul github di antima.it è possibile trovare una repo con un template di progetto che supporta sia make che CMake, che è utile per partire con uno scheletro minimale, clonandola e iniziando a scrivere codice senza troppi pensieri. La trovate @github/antima/avr-template.

L’alberatura è piuttosto lineare e semplice: una cartella inc dove inserire gli header C, una cartella src dove inserire i file sorgente. Queste due cartelle sono automaticamente utilizzate dal Makefile o dal CMakeLists.txt per trovare i file sorgente e gli header. Questo comportamente è personalizzabile modificando i file stessi, se ad esempio desiderate avere un’organizzazione diversa del codice.

Un piccolo hello world

int main(void) {
    for(;;) {
__asm("NOP");
} }

Questo piccolo estratto di codice esegue un loop che non fa niente ma in modo stiloso: è solo qui per testare che tutto il nostro setup funzioni correttamente. Creiamo un file main.c dentro la cartella /src e copiamo questo esempio al suo interno.

Compilare tramite make

Prima di partire con la compilazione, ci sono alcuni dettagli da finalizzare all’interno del Makefile condiviso su github. Bisognerà infatti indicare che tipo di chip è in utilizzo, la sua frequenza di funzionamento, ed un nome per il progetto.

Le prime due opzioni vanno impostate dal seguente blocco di codice (righe 9/10/11 del Makefile):

# MCU configuration
# Set MCU type, clock frequency and programmer
MCU=
CLOCK_FREQ=
PROG_STR=atmelice
 
Ad esempio, nel mio caso, considerando le impostazioni di default dell’ATMega644, inserirò:
 
# MCU configuration
# Set MCU type, clock frequency and programmer
MCU=atmega644
CLOCK_FREQ=1000000
PROG_STR=atmelice
 
Per ora non è importante cosa viene inserito come contenuto dell’opzione CLOCK_FREQ, ne riparlerò in un articolo successivo. Per conoscere il nome da utilizzare per il chip che avete scelto, basterà consultare la lista resa disponibile da avrdude, tramite il comando:
 
avrdude -p ?

La terza opzione è presente alla riga 22 (il default è “template_project”), e darà il nome ai file generati dal processo di compilazione:

# Project configuration
PROJ_NAME=template_project
PROJ_BLD=$(BUILD_DIR)/$(PROJ_NAME)

Compilare questo progetto iper-minimale tramite il Makefile pre-configurato è a questo punto molto semplice; basterà posizionarsi nella root del progetto ed eseguire il seguente comando:

make

Una volta terminato il processo, verrà creata una cartella build, ignorata da git, all’interno della quale saranno presenti i file oggetto corrispondenti ai file sorgente scritti, e tre file output della compilazione: uno .elf, uno .hex ed uno .bin.

Compilare tramite CMake + make/ninja

Prima di generare un build system tramite CMake, anche in questo caso vanno prima configurate le opzioni relative al tipo di chip, frequenza di funzionamento e nome del progetto, in modo simile a quanto effettuato con Make.

project(template_project C) # Set the project name
# MCU configuration
# Set MCU type, clock frequency and programmer
set(MCU "")
set(CLOCK_FREQ "")
set(PROG_STR atmelice)
A questo punto, per generare un build system:
mkdir build && cd build
cmake .. -B .
 
Una volta terminato il processo, la fase di compilazione sarà analoga a quanto visto con make.
 
make
 
Se siete su windows, o preferite usare ninja come sistema di automazione, basterà eseguire:
 
ninja

Integrazione con VScodium

Questa è la chicca che unisce il pieno controllo alla comodità di un ambiente moderno di sviluppo: l’integrazione di CMake con VScodium. Per effettuare il setup del progetto con CMake vi basterà aprire l’editor e seguire le istruzioni, e sarà importante aver settato la variabile PATH per puntare al compilatore gcc.

Conclusioni

Ora che l’ambiente è pronto, cosa andremo a programmare? Nei prossimi articoli andrò più nel dettaglio del chip che abbiamo scelto, come farlo funzionare e come interagirci, per poi passare alla scrittura di una applicazione.

Per questo articolo è tutto, per eventuali approfondimenti o chiarimenti vi invito come al solito a commentare nella sezione qui sotto!

Scrivi un commento

Commento