XMC2GO & DAVE: How To

Qualche tempo fa la redazione di Elettronica Open Source, insieme con Infineon, ha messo in palio 50 schede XMC2GO e, da buon appassionato, non mi sono lasciato sfuggire l'occasione.
Se anche voi, come me, avete una di queste schedine e vi state chiedendo cosa farne e, come la maggior parte degli amanti di Arduino, come farlo evitando di leggere 850 pagine di manuale, questo articolo fa per voi. Cercheremo di procedere insieme alla scoperta delle varie opportunità offerte da questa piccola chicca prodotta dalla Infineon.

Dopo aver dato uno sguardo alla scheda, al microcontrollore XMC1100 e all'ambiente di programmazione DAVE, ci avventureremo nella scrittura di un piccolo programma d'esempio che faccia capire come funziona la programmazione per componenti (Component Programming).

 

La scheda XMC2GO

Recentemente Infineon ha messo sul mercato una piccola scheda di sviluppo con a bordo un microcontrollore (XMC1100) e un altro circuito integrato che fa da programmatore/debugger: la XMC2GO.
A differenza delle classiche schede di sviluppo, quindi, non ci sono funzionalità già a bordo, ma tutti i componenti accessori devono essere aggiunti di volta in volta, ponendo come unico limite alle possibilità della scheda, la nostra fantasia.

La scheda si presenta di dimensioni veramente ridotte, come si vede dalla foto, con 16 pin (cui vanno saldati i connettori, preferibilmente maschi visto che la scheda è breadboard-compatibile):

A parte i due pin di alimentazione, gli altri pin sono di tipo GPIO e ne vedremo in seguito le caratteristiche. Questi sono 16 pin di un integrato da 24; che fine hanno fatto gli altri?
Alcuni sono disconnessi, altri servono per la comunicazione con il debugger, due (pin 2.1 e 2.2) sono utilizzati per comunicare con il PC attraverso una seriale virtuale e altri due (pin 1.0 e 1.1) sono connessi a due LED indicatori per offrire un minimo di interazione con il programmatore.

Di seguito è riportato il diagramma a blocchi che mostra tutte le funzionalità della scheda

Il microcontrollore XMC1100

Il piccolo integrato con cui è equipaggiata la scheda XMC2GO è il micro ARM (Advanced Risc Machine) XMC1100, nel package VQFN-24, che offre 64 kB di memoria FLASH e 16 kB di RAM, di cui si riporta il pinout.

Questo microcontrollore è equipaggiato con numerose periferiche, in particolare:

  • Due Universal Serial Interface Channel (USIC) che possono essere configurate come UART, double-SPI o quad-SPI (Serial Peripheral Interface), I2C (Integrated Interchip Communication), I2S (Integrated Interchip Sound, una variante dell'I2C fatta appositamente per circuiti audio) e LIN (Local Interconnect Network);
  • Fino a 12 canali di convertitori A/D a 12 bit;
  • Una unità di Capture/Compare 4 (CCU4) per realizzare contatori e timer;
  • Watchdog Timer (WDT);
  • Generatore di numeri pseudocasuali (PRNG);
  • Sensore di temperatura interna (TSE);
  • Real Time Clock (RTC).

La gestione delle periferiche è affidata ad un nucleo Cortex-M0 dotato di moltiplicatore hardware e di timer di sistema (SysTick) per il supporto delle operazioni, oltre che di un vettore di interrupt gestito da un apposito controller (NVIC – Nested Vector Interrupt Controller) e di una unità per la gestione di richieste di servizio interne ed esterne (ERU – Event Request Unit).

Ognuna di queste periferiche avrà i suoi registri di controllo e di configurazione che NON saranno analizzati in questo articolo, il cui scopo è proprio dare un approccio pratico alla programmazione dell'XMC1100 a bordo della scheda, senza dover per forza leggere un manuale da 800 pagine.

Una particolarità di questo microcontrollore è la possibilità di “mappare” le periferiche sui pin di uscita, cosicchè al progettista è affidato il compito di gestire le risorse hardware oltre che software; si riporta, quindi, la mappa delle funzionalità di ciascuno dei 24 pin:

Alcuni di questi pin supportano funzionalità esclusivamente di input o di output e ciascuno di essi è configurabile attraverso appositi registri.
Come vedremo in seguito, l'ambiente di sviluppo DAVE semplifica di molto la programmazione, attraverso dei tool che offrono la scelta dei pin hardware da associare alle periferiche attraverso appositi menu a tendina.

DAVE e la programmazione per componenti

A questo punto bisogna capire come DAVE, l'ambiente di programmazione della Infineon, può facilitarci la vita evitandoci la lettura dell'intero manuale del chip, attraverso la programmazione per componenti.

Ma cos'è la programmazione per componenti?

In inglese Component Programming, la programmazione per componenti è quella branca dell'ingegneria del software che pone l'accento sulla separazione estrema tra parti di un software che servono a funzionalità diverse; detta in poche parole, questo tipo di programmazione si avvicina ad un approccio quasi “elettronico”, nel senso che parti di codice indipendenti l'una dall'altra e compilabili separatamente (ognuna delle quali prende il nome di modulo, o componente), offrono delle interfacce di ingresso e di uscita attraverso le quali si collegano tra loro, proprio come lo sono i componenti elettronici grazie a segnali elettrici in ingresso o in uscita dalle interfacce. Proprio questa caratteristica di avvicinarsi al mondo dell'elettronica, in cui il progettista deve costruire sistemi “incollando” assieme parti indipendenti, distingue la programmazione per componenti dalla programmazione a oggetti, il cui scopo principale è creare degli oggetti (anche mentali) ai quali applicare una serie di verbi e di nomi per rendere il codice più comprensibile da chi deve programmare.

Sebbene l'utilizzo dei componenti non sia una funzionalità nativa del linguaggio C, utilizzato per la programmazione in DAVE, proprio questo software offre un'interfaccia grafica (più o meno intuitiva) per definire i componenti (in DAVE saranno chiamati “applicazioni”) utilizzati per il proprio programma e “tradurli” in librerie C che saranno poi utilizzate nella compilazione del codice.

 

Il nostro primo progetto con DAVE

Per il nostro primo progetto con DAVE faremo qualcosa di veramente semplice: un classico “Hello World”, con l'aggiunta di qualche LED lampeggiante.

  • Apriamo DAVE e selezioniamo dal menu “File->New Project”;
  • dal menu che seguirà dovremo scegliere “DAVE CE project” e selezionare come tool-chain “ARM-GCC Application for XMC”;
  • diamo un nome al progetto e clickiamo “Next”.

Si aprirà una nuova finestra con la scelta del “target”, ovvero del microcontrollore che andremo a programmare; selezioniamo l'XMC1100-Q024F0064 e facciamo click su “Finish”.

Siamo pronti per iniziare.

A questo punto dovremmo avere davanti una schermata fatta in questo modo: in alto a sinistra le cartelle del workspace con i nostri progetti; in basso a sinistra una finestra dal titolo “S/W App dependency tree view”; al centro in alto uno spazio vuoto che verrà occupato dall'editor a dalla GUI per la configurazione dei componenti; in basso un altro spazio vuoto con diverse tab tra cui “S/W App connectivity View” e “H/W Connectivity View”; a destra una tab dal titolo “App Selection View” che servirà per selezionare le Apps utilizzate nel progetto, ovvero i componenti. Per selezionare un'app basta cercarla e fare doppio click su di essa; a questo punto l'app apparirà anche nella “S/W connectivity View” e nella “H/W Connectivity View”.

Selezioniamo le seguenti App:

  • SYSTM001: necessaria per gestire i timer di sistema;
  • UART001: servirà per la comunicazione con il pc;
  • NVIC_SR101: con questa app dovremo solo abilitare il vettore degli interrupt;
  • IO004 due volte: per controllare entrambi i LED a bordo della XMC2GO

Fatto ciò facciamo click col tasto destro su IO004/0 e selezioniamo la voce “add user label”; cambiamo nome in LED1; facciamo lo stesso con IO004/1 chiamandolo LED2.
Quando avremo finito dovremmo avere una situazione del genere nella tab “S/W Connectivity View”:

Passiamo a configurare i componenti inseriti nel progetto.

  • Per prima cosa occupiamoci del timer: doppio click su SYSTM001/0; impostiamo un tempo di 100 ms semplicemente scrivendo 100 nella prima casella di testo partendo dall'alto (“SysTick Interval in millisec”);

N.B.: quando si scrive qualcosa in una casella di testo è bene premere invio per essere sicuri che le modifiche siano state recepite dal sistema.

  • A questo punto, doppio click su NVIC-SR101/0; spuntiamo la casella “enable interrupt at initialization” e lasciamo il resto così com'è;
  • Per quanto riguarda l'UART, doppio click su UART001/0 e cambiamo solo il baud rate ponendolo pari a 19200 (o diverso a seconda delle preferenze). Con i settaggi dell'UART aperta, in basso si potranno notare delle tab che prima non c'erano: una di queste è “FIFO Configuration”; selezioniamola e cambiamo la lunghezza della FIFO scegliendo quella maggiore possibile (32);
  • Doppio click prima su LED1 e poi su LED2 per spuntare la voce “output enabled”.

Dal punto di vista software i nostri componenti sono stati configurati; ora dobbiamo decidere a quali pin connettere le periferiche.

In alto tra i menu cerchiamo l'icona “manual pin Assignment”, evidenziata in figura.

  • Click sull'icona;
  • Nella schermata che si aprirà colleghiamo le periferiche di IO ai pin connessi ai LED (pin 1.0 e 1.1) e l'UART ai pin per la comunicazione col pc (2.1 e 2.2);
  • clickiamo sul pulsante “Solve and Save”.


Ora che è tutto pronto possiamo finalmente generare le librerie che ci serviranno per scrivere il nostro programma. Per farlo basta fare click sul pulsante “Genertate Code”.


Fatto questo,DAVE genererà in automatico tutti i file che servono per utilizzare le periferiche configurate; nella tab di sinistra esploriamo la cartella “Dave->Generated” all'interno del nostro progetto, per trovare due nuove cartelle: “inc” e “src”; la prima contiene i file header relativi alle periferiche selezionate mentre la seconda i file sorgente, quelli con estensione “.c”. Pe capire quali funzioni richiamare all'interno del codice, basterà fare riferimento ai file “.h” nella cartella “inc”.

I file segnati come “_Conf.h” all'interno delle varie sottodirectory nella cartella “inc” fanno riferimento alle strutture di tipo “manipolatore” (handle in inglese) a cui dovremo sempre fare riferimento nel nostro programma per interagire con le periferiche.
Tutti i file di libreria sono già inclusi nel percorso di compilazione, quindi non dobbiamo fare altro che aprire il file “main.c”.

Il programma da creare, nello specifico, farà lampeggiare i LED ogni 500 ms in maniera alternata e scriverà un messaggio ogni 2 s sulla porta seriale del pc sfruttando l'interrupt del timer di sistema configurato quando abbiamo settato il SysTick Interval a 100ms.

Prima di modificare il file main.c diamo uno sguardo a “Startup_XMC1100.s” nella cartella “Startup”; questo è un file assembler in cui è definito il vettore degli interrupt: ad ogni tipo di interrupt è associata univocamente una funzione di tipo void e senza argomento, il cui nome è scritto in questo file. La funzione che ci interessa si chiama “SysTick_Handler” che è la routine che viene richiamata quando il timer di sistema raggiunge il numero di conteggi da noi indicati (i famosi 100 ms).DAVE ha automaticamente generato, tra le varie librerie, anche un'implementazione di questa funzione che è quantomai lontana dai nostri scopi. È quindi opportuno che il programma non richiami quella funzione, ma una scritta da noi all'interno del file main.c. Per fare ciò non dobbiamo fare altro che cambiare il nome “SysTick_Handler” in uno di nostra scelta (ad esempio “SysTick_Handler0” o “mSysTickHandler”) ogni volta che lo incontriamo nel file “Startup_XMC1100.s”. Siamo finalmente pronti a scrivere il nostro programma.

Apriamo il file main.c che si trova nella cartella principale del progetto: il file ci presenta un main con una funzione di inizializzazione e un loop infinito.
Cominciamo col definire due costanti:

#define LED_TOGGLE_TICKS 5 //conteggi prima di far lampeggiare un LED
#define UART_MSG_TICKS 20 //conteggi prima di inviare un messaggio sulla UART

Quindi includiamo la libreria string.h che ci servirà in seguito

#include <string.h>

Quindi all'interno del main appena prima del loop infinito accendiamo uno dei due LED per fare in modo che essi lampeggino in maniera alternata

IO004_SetPin(IO004_Handle0);

A questo punto creiamo la routine che sarà lanciata al verificarsi dell'interrupt (nel mio caso “mSysTick_Handler”).

void mSysTick_Handler(null){

    static uint8_t led_count=0; //la dicitura static serve a far si che tra due
                                //istanze diverse della funzione, il valore
                                //della variabile sia conservato.
    if(led_count==LED_TOGGLE_TICKS){
       IO004_TogglePin(IO004_Handle0);
       IO004_TogglePin(IO004_Handle1);
       led_count==0; //azzeriamo il contatore
    }
}

In questo modo abbiamo un programma che fa blinkare i due LED ogni 500 ms.
A questo punto andiamo ad implementare la comunicazione con l'UART.

Per prima cosa creiamo la variabile globale che indica se si deve o meno aprire la comunicazione sull'UART

uint8_t uartSendMsg=0;

e nel main la variabile che contiene i nostri messaggi e la variabile che terrà conto di quale messaggio si deve inviare:

static const unsigned char* message[]...
...{“Hello world!\r\n”,“Fine del Tutorial\r\n\r\n”};
uint8_t msg=0;

A questo punto il loop infinito dovrà assumere la seguente forma:

while(1){

   if(uartSendMsg){
      while(!UART001_WriteDataBytes(&UART001_Handler, // il while serve a fare in
            &message[msg][0], strlen(message[msg]))); //modo che non ci siano   
      uartSendMsg=0; //errori di trasmissione
      msg=(msg+1)%2; //in questo modo la variabile msg assume solo valore 0 o 1
   }

}

Non resta altro da fare che andare a settare a 1 la variabile uartSendMsg quando è stato raggiunto il numero di conteggi prefissato, proprio come fatto con i LED, all'interno della routine che serve l'interrupt, creando una variabile di conteggio statica

static uint8_t uart_count=0;

e aggiungiamo il seguente codice

if(uart_count==UART_MSG_TICKS){
   uartSendMsg=1;
   uart_count=0;
}

Non resta altro da fare che allegare il codice del file main.c completo.

 

Adesso bisogna compilare il codice: dal menu “Project” in alto, scegliere la voce “build active project”.

Per caricare il file binario appena creato a bordo del microcontrollore apriamo il menu “Debug” e selezioniamo “Debug” (unica voce). Si aprirà una schermata come la seguente:

Selezioniamo “Tasking C/C++ Debugger” e clickiamo sul pulsante “nuovo” (evidenziato in rosso).
Nella schermata successiva selezioniamo come target la scheda XMC2GO e, prima di chiudere, clickiamo su “Apply”.


Ora nella schermata principale di Eclipse clickiamo sull'icona “DEBUG” per caricare il codice a bordo della scheda;

Eclipse ci chiederà se vogliamo aprire la visualizzazione per il debug, rispondiamo “yes”. Effettivamente constateremo che la scheda non sta facendo nulla: questo perchè dobbiamo far partire il codice interagendo con in comandi di debug.

clickiamo sul tasto “Resume” per far finalmente partire il codice e godere i frutti di questo tutorial.

Resto in attesa di feedback e commenti degli utenti che hanno ricevuto l'XMC2GO e hanno provato ad usarlo. Sono anche a disposizione per chiarire eventuali dubbi su DAVE o sulla programmazione per componenti, nei limiti delle mie modeste conoscenze.

 

Images credits: XMC2GO reference manual, XMC1100 reference manual

Scarica subito una copia gratis

7 Commenti

  1. Avatar photo Piero Boccadoro 25 Giugno 2014
  2. Avatar photo Ernesto.Sorrentino 25 Giugno 2014
  3. Avatar photo Emanuele 25 Giugno 2014
  4. Avatar photo Ernesto.Sorrentino 25 Giugno 2014
  5. Avatar photo Gius_Res 25 Giugno 2014
  6. Avatar photo Piero Boccadoro 25 Giugno 2014
  7. Avatar photo dodo 25 Giugno 2014

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend