Indice |
Introduzione
Quando si vuole iniziare a lavorare con un microcontrollore la parte più difficile è l' inizio. In questo breve (e anche banale) articolo descriverò il modo in cui mi sono avvicinato agli ATmega della Atmel. Per iniziare a lavorare con un micro ci sono diversi approcci, io ho utilizzato quello che, secondo la mia esperienza, mi sembra il più semplice, lineare e che presente il minor numero di problemi.
Parlando di ATmega non può non venire in mente Arduino. Soluzione sicuramente interessante ma, a mio modesto avviso, non "pura". Ho intrapreso la sperimentazione su ATmega in modo tradizionale almeno secondo i miei canoni.
Per motivi di lavoro ho avuto la necessità di utilizzare questa famiglia di micro, ho quindi ordinato l' emulatore JTAG ed un paio di evaluation kit specifici per la mia applicazione. Devono ancora arrivarmi dagli USA e, per non stare con le mani in mano ed incominciare a destreggiarmi con il sistema di sviluppo ed i micro, ho colto l' occasione di infilare dentro un ordine di materiali un paio di oggetti che mi hanno permesso di iniziare la sperimentazione in tempi brevissimi.
Scelta del microcontrollore
Per sperimentare su un micro che non conosco ho consultato il sito della Atmel per rendermi conto di quello che sono le varie famiglie. Una famiglia interessantissima è l' AVR 32 (che sarà molto probabilmente il micro che utilizzerò per il mio lavoro) ma, visto che i kit di valutazione/sviluppo su cui sperimenterò inizialmente la mia applicazione utilizzano gli Atmega, ho voluto iniziare con questa famiglia.
Il criterio che ho utilizzato è semplice: trovare un micro in un contenitore DIL (possibilmente a 40 pin) con sufficiente memoria per provare applicazioni di un certo spessore. Ho quindi individuato il micro che poteva fare più o meno al caso mio: ATmega324P. Questo micro ha una memoria FLASH da 32K, 2K di RAM che sono abbastanza per farci qualche bella cosina, un bel po' di I/O, convertitore A/D, EEPROM, UART, SPI ... insomma la versione Atmel di un PIC18F4625 o giù di lì, per intenderci. Costa poco più di un pacchetto di sigarette quindi si può comprare senza timore di diventare poveri per questo.
Il micro deve poi essere programmato. E' vero che ci sono bootloader ed artifizi vari che permettono di farlo senza comprare nulla ma un programmatore dedicato, magari della Atmel e che costi poco sarebbe sicuramente la soluzione migliore. Questo programmatore c'è ed è il AVRISP MKII. Costa 29€ e, se penso che Arduino ne costa 25 direi che vale la pena comprarlo anche perché mi permette di programmare sul serio i micro della Atmel e non devo essere legato ad un hardware specifico. Quindi ho inserito nell' ordine il micro ed il programmatore.
Il sistema di sviluppo
Per sviluppare sugli AVR l' Atmel offre AVR studio 5, un bellissimo sistema che comprende anche il compilatore C gcc. Per attrezzarmi ho seguito le istruzioni della Atmel scaricando ed installando il software. Per scaricare il software è necessaria una registrazione. Niente di che, ho messo i miei dati ed ho avuto via libera al download. La cosa bella di questo sistema di sviluppo (che ho appena iniziato ad utilizzare e che quindi conosco solo in minima parte) è la possibilità di creare nuovi progetti partendo da un progetto dimostrativo già esistente. Questo da la possibilità di avere già una base software funzionante su cui apportarci modifiche che, agli inizi, non è cosa da poco. Fa risparmiare tempo e permette di capire molto sul modo in cui i programmi devono essere scritti e su come utilizzare le periferiche. Basta impostare la sigla del micro che si vuole utilizzare e il sistema presenta tutti gli esempi che si possono utilizzare con il micro scelto. Direi che è una cosa grande!
La scheda per la sperimentazione
Che volete farci? Sono all' antica, amo il saldatore, le millefori ed i cablaggi filati. Ho montato un semplice circuito basandomi sulle informazioni del datasheet e del manuale utente del programmatore.
C'è poco da dire sul circuito ... non ha niente! Ho collegato il connettore dell' ISP ai piedini del micro, un quarzo montato su zoccolo per poterlo eventualmente cambiare, massa e alimentazione. E poi ci ho messo il mio buon caro LED rosso sull' alimentazione per sapere quando il circuito è sotto tensione.
Con poche e semplici filature ho collegato il tutto
Fuoco alle polveri!
Ed ora il primo programma. Come primo programma sono solito scrivere un ciclo infinito che incrementa una variabile byte che poi metto in una porta di uscita. In questo modo posso vedere qualcosa muoversi questo perché il semplice LED acceso non garantisce che il micro stia effettivamente girando, ma un dato "sparato" su una porta di uscita che continua ad incrementarsi si. Se tutto funziona con l' oscilloscopio devo vedere un' onda quadra sui pin della porta. Non solo, ma si può valutare la velocità del micro, verificare i cambiamenti dovuti al clock e la bontà del compilatore C. Nella sezione del datasheet che descrive le porte c'è un esempio in C che usa la porta B, in pratica si tratta di caricare nel Data Direction Register della porta il valore FF esadecimale per impostare tutti i pin della porta come uscite. Una sola istruzione di assegnazione. Dopo avere installato AVR Studio 5 ho collegato il programmatore all' USB sperando che il driver fosse già presente. Se il sistema di sviluppo è fatto bene il driver deve già esserci. Il driver c'era ed il programmatore si è installato correttamente, mi è bastato dire al PC di caricarsi il driver automaticamente e lui lo ha fatto.
Ho quindi fatto partire AVR Studio, ho scelto di creare un nuovo progetto selozionando la prima voce in alto a sinistra della pagina iniziale "New Project"
Nella pagina "New Project" ho selezionato "Empty Executable Project" ed ho messo il nome "Prova324P" nella casella "name", ho spuntato la box "Create directory for solution" ed ho premuto "OK"
Poi mi è comparsa la finestra per la selezione del dispositivo, ho selezionato ATmega324P e, cosa molto bella e comoda, nella parte destra mi sono comparsi tutto quello che mi serve del micro e cioe' il link al datasheet ed i tools che supportano tale micro (ho notato con piacere che c'è il link al mio programmatore).
Dopo qualche secondo e' comparsa la scheda con l' editor per il main.
Siccome a me piacciono le tabulazione con 2 spazi mi sono andato a cercare le opzioni dell' editor ed ho messo le cose a posto. Dopo ho aggiunto 3 righe di programma ottenendo questo main:
/* * prova.c * * Created: 19/05/11 22:26:53 * Author: TardoFreak */ #include <avr/io.h> int main(void) { unsigned char i; DDRB = 0xff; while(1) { //TODO:: Please write your application code PORTB = i; i++; } }
Ho costruito il progetto con il comando dal menù Build->Build solution, l' ha compilato senza errori. Poi ho chiamato la voce del menù Tools -> AVR Programming, nella finestra ho impostato come Tool AVRISP mkII, come device ATmega324 e come Interface ISP. A quel punto nella sezione memories ho caricato il file .hex e programmato il micro. Non me l' ha programmato subito perché il programmatore doveva essere aggiornato. Ho seguito le istruzioni, aggiornato il programmatore e quindi, dopo avere richiamato il tool di programmazione, programmato correttamente il micro.
Fatto!
Poi ho esplorato un po' i fuses, selezionato l' oscillatore esterno e tante altre cosette giusto per prendere un po' dimestichezza chiaramente tenendo sempre a portata di mano il datasheet. Ho anche provato a vedere gli effetti delle ottimizzazioni previste dal compilatore: molto valide. Dopo ho deciso di condividere con Voi questa piccola esperienza.
Addendum: Le interrupt
Se c'e una cosa che amo sono i timer seri, intendo quelli che hanno la possibilità di lavorare con un comparatore che, quando il conteggio raggiunge il valore del comparatore, resetti il timer e generi un interrupt. E' il sistema migliore per ottenere temporizzazioni precise. Non amo particolarmente i timers che generano l' interrupt quando vanno in overflow perché impongono un lavoro di compensazione del tempo di ricarica del valore che, se è semplice da fare quando si lavora in assembly, non lo è quando si lavora in C (bisogna andarsi a vedere il lavoro fatto dietro le quinte dal compilatore).
E' mia abitudine, quando ho a che fare con ritardi umanamente apprezzabili, di scrivere un' interrupt ciclica per la gestione di uno o più timers software e magari gestire anche il multiplexing di display se serve. E' un sistema semplice e, a mio avviso, molto pratico da utilizzare. L' idea è di avere dei timers che si decrementano fino ad arrivare a zero e dallo zero non si decrementino più. Quindi, per provare le interrupt con questo micro, ho pensato di scrivere questa semplice interrupt ciclica chiamata ogni 10ms. Ho poi collegato un LED con un resistenza al pin 1 del micro (PB0) con l' intenzione di vederlo lampeggiare con un periodo di 1s.
Il fatto è che ogni compilatore ha un modo suo (a volte bizzarro) per scrivere la routine di gestione dell' interrupt. Ad esempio con il C18 della Microchip biogna fare qualche salto mortale, in scrivere un' istruzione di salto in assembly e via dicendo, il C30 (PIC24/33) utilizza un modo diverso ed infine il C3 (che è un compilatore gcc) ne usa un altro ancora. Sappiamo bene che le interrupt ed il modo per scrivere le relative routines di servizio sono strettamente legate all' architettura della macchina. L' ATmega324P ha una lista di vettori di interrupt e non c'è la possibilità di impostarne la priorità o meglio la priorità è fissa nel senso che il vettore 1 è il RESET ed ha la massima priorità e, man mano che il numero del vettore aumenta diminuisce la priorità. Avremo così la INT0 che ha priorità massima mentre la USART1_TX quella minima . Questa caratteristica se da una parte limita le possibilità, dall' altra rende il lavoro più semplice e, quando si è agli inizi, non può che essere un bene. Per complicarsi la vita non serve impegno, è già complicata di suo e ritrovarsi dentro casini monumentali è un attimo.
GCC (non la Atmel, mi hanno fatto notare in un autorevole commento) ci ha anche messo del suo per rendere la vita più semplice scrivendo una macro che rende la dichiarazione della routine di servizio semplicissima. Basta infatti scrivere ISR(INT1_vect) per dichiarare, ad esempio, la routine di servizio dell' INT1. Io avevo bisogno dell' interrupt sul comparatore A del timer 1 e quindi ho scritto ISR(TIMER1_COMPA_vect). All' interno della routine di servizio non c'è bisogno di "riarmare" o ri-abilitare niente. Si scrive la ruotine e ... finito il chiasso.
Questo è il listato del programmino che fa lampeggiare il LED alla frequenza di 1Hz. ed utilizza un timer software.
/* * prova324p.c * * Created: 20/05/11 15:09:24 * Author: TardoFreak */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/pgmspace.h> // Timer software. // Nota: vanno dichiarate come "volatile" per fare in modo che il // valore sia sempre e comunque letto evitando che l' ottimizzazione // non lo faccia. volatile unsigned short SoftTimer1; // Routine di servizio chiamata quando il contenuto del comparatore A // corrisponde al valore del timer. Quando raggiunge tale valore il // timer viene resettato e viene invocata questa routine. ISR(TIMER1_COMPA_vect) { if(SoftTimer1) SoftTimer1--; } //-------------------------------------------------------------------------- int main(void) { // Inizializza timer 1 per timeout 10ms // Questa istruzione assicura che l' I/O clock per il timer1 sia abilitato PRR0 &= ~(1<<PRTIM1); // Carica il registro di comparazione per ottenere 10ms OCR1A = 3125; // Enable output compare A match TIMSK1 = (1<<OCIE1A)|(0<<TOIE1); // Avvia il timer1, prescaler 1/64 modo operativo Clear Top Count TCCR1B = (0<<WGM13)|(1<<WGM12)|(0<<CS12)|(1<<CS11)|(1<<CS10); // Predispone la porta B come uscita DDRB = 0xff; // Abilita le interrupt sei(); while(1) { if(!SoftTimer1) { PORTB ^= 0x01; // Commuta l' uscita PB0 SoftTimer1 = 50; // Carica il timer software per intervallo 500ms. } } }
Per sapere velocemente come fare per scrivere ed utilizzare il timer ho aperto un progetto di esempio (che si richiama direttamente dall' IDE) ed in poco tempo ho implementato la cosa.
Conclusioni
Questo articoletto è stato scritto velocemente come velocemente sono riuscito a fare la prima prova con l' ATmega. Devo dire che la Atmel ha veramente fatto le cose per bene ed è necessario dirlo a chiare lettere. Oggi pomeriggio mi è arrivato il materiale, dopo mezz' ora avevo il circuito montato, dopo un' altra ora il micro programmato e funzionante. Ci tenevo a pubblicare questa mia semplice e banale esperienza proprio perché Atmel si è meritata i miei complimenti. Niente da dire, i mcro sono molto belli, il sistema di sviluppo è ottimo, da quello che ho potuto leggere nei giorni precedenti le librerie dovrebbero essere ricche ma sono ancora cose che devo scoprire. Quello che mi ha impressionato è la semplicità d' uso e la velocità con cui sono riuscito a fare la prima prova. E questo per me è importante. Se devo essere sincero l' idea di avvicinarmi ad una nuova famiglia di micro mi mette l' ansia (per non dire che mi fa venire l' orticaria). Solo l' idea di dovermi leggere manuali da 400 pagine, linker, direttive specifiche della macchina e del compilatore, settaggi ed opzioni a destra e a manca per fare un programmino del pettine che modula un uscita del micro mi distrugge. Sarà forse perché non ho più vent' anni ma la cosa mi pesa un po'. Con Atmel ho trovato qualcosa che mi ha evitato tutti questi mal di pancia.
Mi dispiace solo di non poter dire altrettanto della Microchip. Il suo sistema è buono però dovrebbe prendere esempio dalla Atmel di come si realizza un sistema di sviluppo semplice e potente. I PIC si meriterebbero un qualcosa del genere poiché sono dei micro veramente belli e poco costosi ... ma tant' è.