Indice |
Introduzione
Ho scritto questo articolo pensando a chi come me, dopo aver comprato una scheda hardware Arduino, si affaccia al mondo dei microcontrollori.
Non sono mai stato un amante degli IDE, perchè penso che nella maggior parte delle volte, per chi comincia a programmare possono essere controproducenti riguardo alcuni dettagli, come per esempio la correzione automatica degli errori di sintassi, che non abitua il programmatore ad imparare dalle proprie correzioni (cosa che non avviene tramite un normale editore di testo).
Nell'ambiente di sviluppo di Arduino però, il caso è differente. Se diamo una occhiata al codice dei numerosi esempi inclusi nel software (ed altrettanto disponibili in rete a questo indirizzo), e sapendo che l'hardware Arduino incorpora un microcontrollore della famiglia AVR, si nota che i programmi nascondono all'utente "il dietro le quinte" della programmazione del software con le avr-libc.
Per fare un esempio pratico, nel programma Blink, la chiamata alla funzione
pinMode(13,OUTPUT);
non ci insegna veramente a configurare un pin come output. Se dovessimo programmare un ATMEGA su un circuito che non è la scheda hardware Arduino, come faremmo senza la funzione pinMode?
Nel mio caso quindi, accendere un LED con la funzione digitalWrite non è soddisfacente. Ho bisogno di sapere di più, ovvero, quale codice si nasconde dietro ad una funzione di questo tipo? Questa curiosità è ciò che mi ha spinto a cominciare in una maniera differente e successivamente a scrivere questo articolo. Voglio precisare che non vuole essere l'ennesimo tutorial, ma bensì mostrare una alternativa altrettanto interessante per cominciare a programmare Arduino, con lo scopo di invogliare l'utente ad acquisire esperienza con la programmazione di un microcontrollore della famiglia AVR.
Blink2
Come esempio pratico ho scritto una versione alternativa di Blink (ovvero il programma che ci permette di fare lampeggiare un LED collegato al pin nº 13 della scheda hardware) chiamata Blink2, che pur essendo un programma veramente piccolo e semplice riesce a evidenziare qualche concetto di base.
Il primo requisito è ovviamente avere lo schema della scheda hardware (nel mio caso una Arduino Duemilanove con schema completo reperibile a questo indirizzo). Successivamente è bene procurarsi il datasheet del microcontrollore che ci si accinge a programmare (nel mio caso un ATMEGA328P con datasheet reperibile a questo indirizzo). In caso di disporre un hardware differente, si tratta solamente di seguire gli stessi passi per l'hardware acquistato.
Per motivi di spazio e formattazione, mi limito a rappresentare la porzione di circuito che rappresenta la connessione dei pin del microcontrollore con quelli a cui vengono collegati i componenti esterni.
Un occhio attento nota subito che il pin 13 a cui si collega il LED non è collegato al pin 13 del microcontrollore, ma bensì al nº 19, ovvero il PORTB:5 (PB5) o bit nº 5 del PORTB.
I pin dei microcontrollori a loro volta formano quelle che si chiamano "porte di I/O". Un microcontrollore può avere una, due, tre o più porte a seconda delle sue caratteristiche hardware. Normalmente vengono chiamate PORTA, PORTB, PORTC, etc. Ogni porta prima di essere usata deve essere configurata attraverso un apposito registro interno.
Nel caso dei microcontrollori AVR, il registro è chiamato Data Direction Register, come si può leggere a pagina 94 del datasheet (link sopra). Nel nostro caso il Data Direction Register associato al PORTB è il DDRB.
Il programma del LED lampeggiante quindi si basa solamente sul fatto di cambiare lo stato di un singolo pin (o chiamiamolo bit) in maniera intermittente. A tale scopo quindi useremo una funzione di temporizzazione chiamata _delay_ms (appartente alle librerie C AVR, ovvero le avr-libc).
Il codice C di Blink2 è il seguente
/* Blink2.c Arduino LED blinking on board pin nº 13 Author: Gohan */ /* Set XTAL frequency */ #define F_CPU 16000000UL /* I/O & delay headers */ #include <avr/io.h> #include <util/delay.h> int main (void) { /* set PORTB as output */ DDRB = 0xff; /* Infinite loop */ while(1) { /* turn on LED */ PORTB |= _BV(PB5); /* 1s delay */ _delay_ms(1000); /* turn off LED */ PORTB &= ~_BV(PB5); /* 1s delay */ _delay_ms(1000); } return 1; } /* end of LED program */
La costante F_CPU definisce la frequenza di clock alla quale lavorerà la CPU del microcontrollore grazie all' oscillatore esterno, ed è obbligatorio dichiarla al principio del codice in caso di utilizzare la funzione _delay_ms o qualsiasi altra funzione dichiarata in utils/delay.h. Se il valore della costante F_CPU non coincidesse con la frequenza di clock i ritardi sarebbero di tempi sbagliati. F_CPU è quindi il valore di riferimento di frequenza per le funzioni dichiarate nell'header appena citato.
L'Arduino Duemilanove monta un quarzo a 16MHz, con cui si spiega il valore attribuito alla riga #7.
Affinchè l'I/O funzioni, bisogna successivamente includere l'header file <avr/io.h>.
La funzione _delay_ms invece è dichiarata nell'header file <util/delay.h>.
Come si può vedere il programma è molto semplice e intuitivo, però confrontandolo con Blink
/* Blink Turns on an LED on for one second, then off for one second, repeatedly. This example code is in the public domain. */ void setup(){ // initialize the digital pin as an output. // Pin 13 has an LED connected on most Arduino boards: pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); // set the LED on delay(1000); // wait for a second digitalWrite(13, LOW); // set the LED off delay(1000); // wait for a second }
è evidente che quest'ultimo non mette in evidenza quello che sarebbe bene imparare fin da subito.
Caricamento del programma
Caricare il software funzionante nel microcontrollore vuol dire prima compilare il programma e successivamente generare il file .hex, che contiene la informazione che il microcontrollore può interpretare. Nel mio caso, che lavoro con Linux, ho usato lo stesso Arduino IDE per caricare il programma, semplicemente aprendo il codice in C e caricandolo come qualsiasi altro. Ricordarsi che i programmi fatti da sè e creati all'infuori dell' Arduino IDE possono solo essere aperti se salvati nella cartella di sketchbook, cartella in cui vengono salvati anche quando vengono scritti con tale ambiente di sviluppo.
Una alternativa "fai da te" consiste nel compilare il programma con avr-gcc, generare il file .hex con avr-objcopy ed infine caricare il file con avr-dude attraverso un opportuno makefile. Tale lavoro può però portare a delle complicazioni che potrebbero richiedere abbastanza tempo per essere risolte, in quanto è opportuno conoscere anche gli argomenti a riga di comando da passare al programma. Questo va oltre lo scopo di questo articolo, quindi il mio primo consiglio senza complicarsi la vita, è caricare il programma grazie all'IDE, che in questo caso è di grande aiuto.
Putroppo non uso Windows, quindi non avendo esperienza con questo OS mi limito a segnalare questo tutorial.
Scusate ma sono sempre stato un amante di Linux.
Conclusioni
Questo è stato il mio approccio personale per cominciare con Arduino. Considero che tale scheda può essere quindi uno strumento per familiarizzare con gli AVR. Per chi non avesse ancora comprato Arduino, il PIERIN AT90 di TardoFreak (reperibile al suo blog) rappresenta una ottima alternativa a mio parere.
Comprai Arduino poco più di un'anno fà, ricordo ancora il mio primo post sul forum nel quale chiesi informazioni su libri a riguardo. Dopo un anno di lezioni ed esami, finalmente ho avuto il tempo di metterci mano in questo modo. Spero quindi che quest'articolo venga preso in considerazione allo scopo per cui è stato scritto, e che possa essere uno stimolo di partenza per altre persone.
Per ultimo, non vuole assolutamente essere una critica alla metodologia applicata dallo staff creatore di Arduino secondo gli esempi.
Links Utili
AVR LibC Home Page, dove si può scaricare l'ultima versione del manuale delle avr-libc.