I microcontrollori I sistemi meccatronici integrano all’interno del dispositivo meccanico/fisico di interazione una parte di elettronica analogico digitale che ne consente l’utilizzo/controllo ad alto livello (sistema embedded). La particolare integrazione di meccanica/elettronica realizzata in un sistema di questo tipo consente di ottimizzare ulteriormente i vincoli di progettazione in maniera da produrre sistemi compatti altamente performanti. Definizione di microcontrollore Un controllore è una dispositivo logico utilizzato per i processi di regolazione dei sistemi nell’ambiente. Applicazioni tipiche possono essere sistemi di servoguida in automobile, il controllo delle testine e dei motori in un videoregistratore, termostati di ambiente o altro. Nel passato i controllori erano costruiti tramite componenti discreti, dapprima meccanici, poi elettromeccanici, quindi con dispositivi semielettronici (ad esempio relé a stato solido). In seguito con l’avvento dei microprocessori, e grazie alla loro flessibilità molti sistemi di controllo sono stati realizzati con piccole schede di controllo composte dall’integrazione della logica di calcolo offerta dal microprocessore con le relative componenti necessarie al suo funzionamento (memoria, dischi, clock,...) ed alla sua interazione con l’ambiente (periferiche di acquisizione dati, di attuazione, e di comunicazione e controllo remoto). Man mano che questo processo di inegrazione e miniaturizzazione è continuato, tutte queste componenti sono state integrate in un unico dispositivo. Un microcontrollore è un chip integrato che include in sè molte delle componenti necessarie per la realizzazione di un sistema di controllo. Da un punto di vista computazionale, un microcontrollore è analogo ad un mocroprocessore (possiede una Central Processing Unit ed esegue istruzioni a controlloo di programma su una memoria dati), ma rispetto a quest’ultimo esso si differenzia per un certo numero di funzionalità integrate: • Integrazione della memoria di programma: spesso in forma di EEPROM, ma sempre più di frequente in PROM, ROM o FLASH, questi dispositivi hanno tutti una memoria statica di lunga durata, che sopravvive agli spegnimenti e riaccenzioni ed in grado di conservare il processo/programma di controllo da eseguire per tutto il tempo necessario. L’integrazione della memoria programma nel dispositivo consente la rimozione delle memorie fisse esterne (Dischi, ROM, altro) riducendo l’ingombro ed il costo del sistema di controllo ed accelerando in tal modo la prontezza del sistema ad eseguire i propri compiti. • • • Integrazione della memoria dati: la memoria dati, di più frequente accesso, anche se ridotta in dimensioni (considerati i compiti piuttosto semplici demandati ai microcontrollori) viene integrata in tutti i microcontrollori. Questa memoria in venere varia da poche decine di byte a qualche migliaio ed è sufficiente a memorizzare tutti i dati dinamici alla esecuzione dei processi di controllo. Integrazione servizi di timing: molti micro integrano degli oscillatori interni in maniera da poter funzionare anche in assenza di un sistema di oscillazione esterna e richiedere quindi soltanto la connessione con l’alimentazione ed i dispositivi da controllare. Integrazione delle periferiche: il numero di periferiche implementate in un microcontrollore varia tantissimo da caso a caso, tutti i micro comunque integrano almeno delle porte digitali per il controllo (TTL) di alcune linee esterne. Il numero di periferiche integrate varia con i piedini disponibili nel package e molto spesso sono riconfigurabili in prospettiva della applicazione preposta. E` quindi immaginabile che rispetto ad una archiettura di controllo di sistema organizzata con microprocessori e periferiche, una architettura equivalente basata su microcontrollori consente di ridurre notevolmente la complessità dello schema circuitale in quanto molti dei servizi (memoria, I/O,...) sono inclusi nelle funzionalità base dei micro. L’integrazione delle funzionalità non è comunque soltanto limitata ad un aspetto fisico (più sistemi in uno stesso chip) ma molto spesso integrata nella struttura di controllo in maniera da rendere semplice l’accesso ed il controllo di tutte le periferiche di I/O riducendo in tal modo non solo la complessità di progettazione elettronica ma anche quella di programmazione digitale. Resistenza Elettrica Motore Valvole Pompa Cestello I-6 I-7 8 a4 b4 7 b3 a3 3 6 b2 2 a2 V c c1 a1 0 mV 10-50 1 Power Drivers I-5 MC U Mic r o co n t ro l le r b1 5 I-3 P-10 I-8 4 0-100 deg I-9 Struttura di funzionamento di una Lavatrice In figura viene rappresentato l’impego di un sistema a microcontrollore per il controllo della elettronica di potenza e non in una lavatrice. Tutti gli elementi di controllo e display (manopole e led/display) sono connessi direttamente ad un microcontrollore centrale, mentre la parte di elettronica di potenza (motora, valvole, scaldatore, pompa) sono interconnesse allo stesso processore tramite un sistema di condizionamento dei segnali che consente di governare le singole componenti tramite l’I/O disponibile nel microcontrollore (Nello schema sono stati tralasciate le alimantazini dei sistemi). Compito del microcontrollore in questo caso sarà quello di coordinare gli elementi necesari per il lavaggio in funzione delle opzioni regolate nei controlli (tipo di lavaggio, temperatura, velocità della centrifuga e così via). Per la realizzazione del sistema di controllo sarà necessario: 1. definire il comportamento del sistema in funzione della programmazione espressa tramite le manopole e dello stato corrente del lavaggio 2. Codificare il comportamento del sistema in base ad un insieme di regole temporali. 3. Definire le operazioni e la relativa sincronizzazione delgi elementi di potenza che effettueranno il lavaggio 4. Implementare la sequenza di stimoli necessaria per ottenere la sincronizzazione 5. Scrivere il programma di controllo da eseguire sul microcontrollore perché tale sequenza di stimoli sia correttamente prodotta ai piedini del microcontrollore. Struttura di un Microcontrollore La struttura di un microcontrollore (Micro Control Unit = MCU) è per certi versi simile a quella dei microprocessori (MPU) con i quali condividono la struttura logica di calcolo base. Il cuore dell’unità di calcolo di una MCU è costituito da: • Registri • • • • • • Unità logico aritmetica Contatore di programma Unità di fetch Decodificatore di istruzioni Memorie (dati, programma e dati statici) Moduli di I/O In figura è rappresentata una possibile organizzazione generale di un microcontrollore (sebbene molte poi siano quelle reali) da cui si intravede come queste componenti siano interconnesse. In un microcontrollore, tutte le unità principali comunicano mediante un BUS (4,8,16,32 bit) le cui dimensioni sono poi connesse con quella che vedremo sarà la tipologia di microcontrollore. Questo bus, essendo l’unità di trasferimento base per tutti i dati, è collegato alla capacità di manipolare le informazioni all’interno del microcontrollore. Quando ci riferiermo a microcontrollori a 8 bit, intenderemo che la capacità di gestione dati del microcontrollore è organizzata su strutture delle stesse dimensioni e quindi anche il BUS interno dovrà possedere le medesime linee. Le componenti in figura sono divise in due gruppi: alla destra del BUS tutte le componenti I/O modulari che offrono le funzionalità di controllo, alla sinistra la parte dedicata all’elaborazione delle informazioni. La divisione non è solo grafica ma anche funzionale nel senso che ogni periferica comunica con la parte di calcolo tramite una porzione di memoria accessibile sul BUS e visibile nello spazio di memoria del processore. Programmare un dispositivo di IO diventa pertanto accedere in forma, modi e tempi particolari a determinate zone di memoria. Il funzionamento dei moduli di elaborazione è invece analogo a quello di un microcontrollore, con la differenza che le memorie necessarie al funzionamento sono già incluse nella architettura base (sebbene memorie aggiuntive possono essere connesse dall’esterno). In questo modo il microcontrollore può divenire un elemento indipendente che funziona quando alimentato e connesso ad un oscillatore che ne genera il clock (talvolta incluso nel chip stesso). Il microcontrollore non ha la complessità di un computer, non ci sono firmware e/o sistemi operativi che ci aiutano a sviluppare il codice, ma si offrono al programmatore semplicemente con le loro caratteristiche hardware. Non è pertanto immaginabile di poter scrivere in un microcontrollore codice analogo a quello che scriveremo per un microprocessore, ad esempio la libreria base del linguaggio “C”, la stdio, in questo caso non ci servirebbe a nulla, perché non esistono schermi, file o quant’altro necessario per il funzionamento. All’accensione un microcontrollore provvede ad eseguire il codice programmato a partire da una determinata locazione di memoria (usualmente 0x0000) in accordo con il proprio codice mnemonico di istruzioni (identificato dalla unità di decodifica). Sebbene il meccanismo di funzionamento sia alquanto universale, per una migliore facilità di comprensione del funzionamento sembra opportuno specializzarne i contenuti su di una architettura particolare. Pertanto, per la descrizione dettagliata di come questa fase di esecuzione esecuzione ha luogo si rimanda al capitolo specifico sui processori ATMEL che introdurranno il problema su quella determinata tipologia di processore. Principi di programmazione di un Microcontrollore I microcontrollori vengono in generale programmati in moltissimi modi: talvolta direttamente tramite lo sviluppo in codice oggetto, talvolta utilizzando linguaggi di programmazione più sofisticati (C, C++, pascal, basic,...) e alcune volte addirittura in alcuni casi in linguaggi visuali, come ad esempio consentono alcuni tools sofisticati di sviluppo. Programmazione di microcontrollori Codice Oggetto (Variabile da MCU a MCU) Linguaggio C/C++ Basic Linguaggi grafici Altri Nonostante l’elevata disponibilità di tools di sviluppo sofisticati, la moggior parte del codice di programmazione viene sviluppata direttamente in codice oggetto. Ciò è maggiormente dovuto a due caratteristiche: l’esiguità delle risorse disponibili all’interno di un microcontrollore e la maggior efficacia (in termini di dimensioni del codice, ottimizzazione dei processi, velocità e consumi) dello sviluppo del software quando questo viene condotto direttamente nel codice oggetto dei microcontrollore; analogamente la semplicità dei compiti cui tali microcontrollori sono generalmente preposti rende semplice lo sviluppo di software in codice oggetto. Inoltre, per i programmatori esperti, la dimistichezza con lo sviluppo del codice, affiancata alla esperienza ed alle librerie che gli stessi sviluppatori via via si sviluppano, rendono relativamente poco complicata, la complessità aggiunta dallo sviluppo in un linguaggio di basso livello come il codice oggetto. A prescindere dal tipo di linguaggio utilizzato e dal tipo specifico di microcontrollore tutti i sistemi di questo tipo consentono due tipologie fondamentali di programmazione: la programmazione ad interruzione e la programmazione a controllo di programma: la programmazione a controllo di interruzione è associata a delle procedure che il sistema è in grad di eseguire in presenza di eventi segnalati dalle periferiche predisposte sul microcontrollore. La programmazione a controllo di programma viene invece svolta ordinariamente dal microcontrollore appena questo viene acceso. Vale la pena sottolineare che questi tipi di programmazione, sebbene simili a quelle che ordinariamente siamo abituati a conoscere per i sistemi operativi differiscono profondamente per semplicità di meccanismo, rendendo quindi possibile la descrizione dei principi di funzionamento almeno in linea generale. Nessun microcontrollore viene infatti provvisto con un sistema operativo come nel caso dei personal computer ed ogni tipo di esecuzione sullo stesso verrà dettagliatamente programmata dal suo sviluppatore. Appena alimentato tutti i microcontrollori eseguono una porzione di codice predefinita, procedendo nella sua esecuzione esaminando linearmente lo spazio di istruzioni del programma fino all’occorrenza di eventi particolari. L’esecuzione del codice può differire leggermente da microcontrollore ad un altro ed anche all’interno di uno stesso microcontrollore in funzione del tipo particolare di programmazione effettuata. In tutti i casi, in base a questo principio risulta possibile definire (codificare) gli algoritmi di controllo del microcontrollore memorizzando le istruzioni in codice oggetto, a partire dal primo indirizzo di esecuzione per il microcontrollore. Qualora abilitate (via software o via programmazione), le interruzioni possono sospendere l’esecuzione ordinaria del programma andando a richiamare delle procedure collocate in indirizzi specifici e ritornando alla esecuzione dello stesso al temine delle routine di servizio. Questo meccanismo consente la gestione delle operazioni di controllo collegate ai segnali ricevuti dalle periferiche minimizzando l’attenzione che è richiesta per sorvegliare le stesse (si parla in questo caso di controllo attivo tramite polling). Controllo di programma Controllo di interruzione Sleep mode Meccanismi di controllo esecuzione L’esecuzione del codice di programma avviene ad eventi specifici (accensione, risveglio, reset), e prosegue in maniera non continuativa (in quanto le interruzioni possono sospenderla temporaneamente) fino a eventi specificati: interruzioni, spegnimento, reset, sleep L’esecuzione del codice di interruzione non avviene finché alcuni eventi (interrupt) specifici non vengono segnalati dalle periferiche L’esecuzione del codice di programma è sospesa mentre alcune perfieriche (il cui funzionamento è scollegato dal processore) continuano a lavorare ed eventualmente segnalare la necessità di un risveglio al processore centrale. Alcuni microcontrollori (ad oggi quasi tutti) consentono inoltre di far “addormentare” il dispositivo e risvegliarlo solo in presenza di alcuni eventi da interruzione. Una tale funzionalità consente infatti di risparmiare energia in tutte quelle applicazioni di controllo dove la stessa è un bene prezioso (dispositivi portatili, orologi, telefoni, oppure mobili, spaziali, volanti,...). Lo sviluppo di software per microcontrollori Lo sviluppo del software per sistemi di questo tipo è molto delicato, l’impossibilità di accedere con programmi specifici al software ed alla diagnostica del funzionamento dei sistemi integrati rende critico il processo di sviluppo. E’ importante rilevare che sviluppare codice per un microcontrollore richiede inizialmente un grosso impegno in termini di bagaglio di conoscenze da acquisire per la gestione dei dispositivi integrati e della struttura del codice di esecuzione. Come per i processori e per i computer in generale, ogni microcontrollore presenta infatti un “microcosmo” di informazioni che è necessario conoscere per l’esatta generazione del codice di funzionamento, la sua programmazione ed il suo debug. In quest’ottica diventare uno sviluppatore di applicativi meccatronici potrebbe apparire un lavoro immane a prima vista, ma come vedremo (e come sarà meglio chiaro durante il corso), ci sono molti elementi che ci aiuteranno in questo compito: 1. In primo luogo, ogni produttore di microcontrollori, cerca di sviluppare i propri sistemi in base ad una famiglia di architetture. Utilizzare il termine famiglia significa che, sebbene taluni microcontrollori possono risultare differenti, ci sono una serie di elementi “genetici” che accomunano i diversi membri della famiglia stessa e che facilitano lo sviluppo ed il porting di applicazioni tra i “parenti”. L’utilizzo di una famiglia è di comodità sia per lo sviluppatore che in questo modo può apprendere i principi base di programmazione e muoversi facilmente attraverso tutta la gamma dei prodotti disponibili, sia per il produttore che può ottimizzare la struttura della famiglia confidando in un ampia gamma di utilizzatori e che migliorie in un prodotto si riflettono automaticamente su tutta la linea. Tra gli elementi “genetici” che vale la pena sottolineare: il linguaggio di programmazione e l’architettura della parte di microprocesso, le periferiche principali, la loro collocazione in memoria ed il loro utilizzo, l’ambiente semplificato di sviluppo omogeneo per tutti gli elementi della stessa famiglia; 2. Talvolta come è successo per alcuni microcontrollori particolarmente diffusi, l’architettura di sviluppo è aperta, consentendo la costituzione di una famiglia che non è più limitata ad un solo produttore ma diffusa largamente tra più fornitori; 3. Inoltre, come verrà descritto in seguito, ogni famiglia di microcontrollori viene supportata da una serie di tools di sviluppo, molto spesso forniti dallo stesso produttore e talvolta provvisti da compagnie di supporto. Tali strumenti consentono un accesso ed uno sviluppo semplificato del codice e delle infrastrutture necessarie per integrare un sistema microcontrollato, consentendo la simulazione, il debug, e lo sviluppo in un ambiente facilitato (talvolta in C, C++ o strumenti grafici) e lasciando che lo sviluppatore si concentri esclusivamente sugli obiettivi funzionali che gli interessa perseguire senza curarsi dei dettagli dell’implementazione software. Al fianco degli ambienti integrati di sviluppo (IDE) che comprendono simulatori, compilatori ad alto livello, e talvolta anche tool di sviluppo grafico (ad icone, simile a quanto disponibile per Matlab), risulta spesso utile la disponibilità di un sistema di verifica rapida. Le schede di sviluppo sono dei sistemi elettronici assemblati che includono al fianco di un microcontrollore un numero ampio e variegato di interfacce di interazione connesse con le periferiche del microcontrollore. Ad esempio led e puldanti possono essere connessi alle porte digitali, le porte seriali sono adattate per la comunicazione con personal computer, gli ingressi analogici sono forniti di connettori di semplice utilizzo. Le schede di sviluppo in sostanza consento no di sperimentare il funzionamento del proprio codice anche in assenza di un vero e proprio progetto hardware di sistema. Le schede di sviluppo possono essere offerte dalla stessa casa produttrice (come avviene nel caso microchip, atmel, oppure possono essere offerte da produttori di terze parti come nel caso thompsom ad esempio). Anche per ciò che concerne il software di sviluppo una buona parte delle case produttrici si impegnano per corredare i proprio prodotti con compilatori, assemblatori e debugger, anche se spesso questo tipo di lavoro richiede lo sviluppo di competenze e la manutenzione di prodotto che molto spesso possono andare oltre le specifiche competenze del produttore. Per questo motivo molti produttori si affidano ad aziende di terze parti che per i loro sistemi producono la linea di sviluppo più idonea. Detti produttori (come la keil, la IAR ed altri) possono infatti trarre valore aggiunto dalla specializzazione ottenuta sull’ambiente di sviluppo a prescindere dal tipo particolare di microcontrollore di riferimento. Altri produttori ancora si affidano all’adozione di particolari standard di architetture che possono usufruire di prodotti di sviluppo già presenti sul mercato (philips, atmel, toshiba, siemens, samsung) e facendo così affidamento sulla possibilità per gli sviluppatori di riutilizzare sistemi di sviluppo e standard ormai acquisiti. La programmazione dei processori ATMEL La scelta di utilizzare il microcontrollore ATMEL come riferimento nel corso di meccatronica è attualmente legata alla relativa disponibilità di un’ampio insieme di strumenti di sviluppo, ad un’attivo gruppo di interesse di riferimento di accesso pubblico per la ricerca di informazioni, al costo relativamente ridotto dei sistemi e del ambiente di sviluppo. In particolare per noi sarà di rilevante interesse la possibilità di accedere al microcontrollore a tre differenti livelli: architettura di sistema a microcodice di programmazione, programmazione in linguaggio C, programmazione tramite il real time workshop di matlab. La struttura e le caratteristiche generali sono rappresentate in figura. Nella struttura AVR a otto bit ogni processore possiede 32 registri di otto byte ciascuno sui quali si può operare mediante un set di 133 istruzioni. La maggior parte di queste istruzioni esegue in un unico ciclo di processore consentendo in tal modo un elevato flusso di operazioni. Le principali periferiche comprendono: 1) due timer/contatori a 8 bit dotati di prescaler separati e di comparatori 2) due timer separati a 16 bit con prescaler e comparatori separati 3) 6 canali modulabili in PWM con risoluzione variabile da 2 a 16 bit 4) 8 convertitori Adc a 10 bit ciascuno 5) Porta seriale SPI 6) Interfaccia seriale a due linee 7) 2 USART bidirezionale 8) Watchdog Timer 9) Comparatore analogico In più l’atmega 128 offre le seguenti caratteristiche particalri: memoria di programma 128KB, 4KB EEPROM, 4KB SRAM, 53 linee di IO. I registri di sistema Il comportamento del processore (come macchina a stati di tipo complessa) è regolato non solo dalle istruzioni in codice macchina che esso incontra ma anche da un insieme di registri particolari. Trentadue registri utente sono a disposizione dell’utente, ogni volta che il programmatore avrà bisogno di manipolare dei dati egli potrà farlo tramite l’utilizzo dei dati nei registri del microcontrollore. I registri sono corrispondenti alle prime trentadue locazioni di memoria ram ma sono indirizzabili direttamente dal linguaggio assembler. I trentadue registri possono essere utilizzati come registri di input/output indipendenti per operazioni a 8 bit, oppure, in maniera combinata come registri di IO su operazioni a 16 bit. Gli ultimi sei registri possono inoltre essere utilizzati come puntatori a 16 bit per le operazioni di accesso indiretto in memoria. I core dei microcontrollori si dividono in due grandi categorie, quelli basati sull’accumulatore e quelli basati sui registri. Nei core basati sull’accumulatore esiste un registro di tipo particolare, denominato accumulatore appunto in gradi di effettuare un numero più esteso di operazioni, in particolare le operazioni matematiche. Nelle altre categorie invece tutti i registri sono equivalenti in termini di programmazione (approccio risc based). Tutte le operazioni possono essere effettuate tramite la memoria, la ALU ed i registri a disposizione del programmatore. A seconda del tipo di operazione i registri X, Y, Z, possono essere dotati di funzioni particolari come ad esempio l’incremento automatico oppure il decremento automatico. Il registro di stato (STATUS REGISTER) contiene le informazioni relative al risultato delle istruzioni eseguite più di recente ed è aggiornato ogni volta che la ALU effettua qualche elaborazione tra i registri. Gli otto bit del registro di stato hanno le seguenti caratteristiche: bit Name POV 7 I RW 0 6 T RW 0 5 H RW 0 4 B RW 0 3 V RW 0 2 N RW 0 1 Z RW 0 0 C RW 0 • Bit 7 – I: Global Interrupt Enable: quando questo bit viene messo a zero nessuna interruzione potrà sospendere il flusso di programma del processore, il bit viene messo a zero automaticamente durante l’esecuzione delle procedure di interrupt e ripristinato a 1 dall’istruzione RETI, ma può anche essere controllato manualmente con le istruzioni SEI e CLI. • Bit 6 – T: Bit Copy Storage Il bit T viene utilizzato in tutte le istruzioni del processore che operano con un bit implicito (sorgente o destinazione) di riferimento (BLD e BST). • Bit 5 – H: Half Carry Flag E il bit di semiriporto, utilizzato per vedere se un riporto è stato generato da o verso il nibble basso del registro su cui si effettua l’operazione. Utile spesso nelle operazioni che richiedono l’uso di Binary Coded Decimal. • Bit 4 – S: Sign Bit, S = N . V Bit del segno • Bit 3 – V: Two’s Complement Overflow Flag Overflow in modalità di complemento a due • Bit 2 – N: Negative Flag Bit di risultano negative • Bit 1 – Z: Zero Flag Bit di zero, settato ogni volt ache il risultato dell’ultima operazione è zero • Bit 0 – C: Carry Flag Bit del riporto Nota: il set di istruzioni di processori che operano a 8 bit è molto elementare, esso prevede, somme sottrazioni e shift di elementi tutti a 8 bit. Per la estensione delle operazioni pensate su 8 bit a formati matematici più complessi risulta necessario provvedere alcuni bit (N, S, V, Z, C) che indicano (operazione per operazione) ogni volta che durante una operazione un’evento di tipo particolare sia accaduto (riporto, zero,...). Come vedremo tra le operazioni di un microprocessore, oltre alle operazioni matematiche esisteranno alcune operazioni di controllo flusso molto elementari (salti condizionati) che consentono la gestione del flusso ogni volta che ho un riporto, un risultato nullo o altro. Lo stack, anche detta pila, è una porzione di memoria utilizzta per la conservazioni di tati temporanei, ad esempio memorizzare il valore di rientro di una procedura oppure le variabili da ritornare al chiamante. Il puntatore allo stack è un registro che punta sempre alla cima dello stack (si noti che in questo caso lo stack è implementato come crescente dalla direzione più alta verso la più bassa, questo impleca che l’inserimento di qualcosa nello stack faccia diminuire il valore del puntatore). La memoria dello stack viene collocata nella memoria SRAM del MCU e il suo valore è inizializzato dal programmatore in funzione delle proprie esigenze. Ovviamente esso dovrà puntare come valore minimo di partenza sopra l’ultima delle locazioni riservate al processore (0x60). Il puntatore allo stack è implementato come una coppia ri registri (SPH, SPL) ad otto bit, nei microcontrollori con meno di 256 byte di memoria solo SPL sarà utilizzato. Il valore iniziale di questi registri è posto a zero e sarà cura del programmatore inizializzarli come dovuto. La piedinatura di alcuni AVR La piedinatura dei microcontrollori Atmel cambia da dispositivo a dispositivo, ed anche all’interno dello stesso dispositivo essa può variare in funzione del tipo particolare di package adottato per il sistema. In aprticolare vedremo di seguito due dei microcontrollori base della famiglia AVR: l’8515 (probabilmente il più usato) e l’Atmega 128, attualmente il TOP della gamma disponibile. L’Atmega128 è attualmente disponibile in un package di 64 piedini a montaggio superficiale, mentre l’8515 è disponibile nel classico (e più maneggevole) DIL40. I relativi piedini corrispondono a porte digitali di ingresso uscita (nel casopiù semplice), ovvero a delle funzioni ausiliarie, configurabili porta per porta in funzione delle funzionalità necessarie. Più avanti verrà mostrato come sia possibile configurare le porte singolarmente oppure abilitare le funzionalità desiderate. Descrizione della piedinatura L’Atmega 128 è un dispositivo dotato di 64 piedini corrispondenti a 7 porte (A, B, C, D, E, F, G) per la gestione dei segnali di controllo e alcuni piedini con finalità differenti descritti di seguito: VCC (2) GND (3) Porta A (PA7..PA0) Porta B (PB7..PB0) Porta C (PC7..PC0) Porta D (PD7..PD0) Porta E (PE7..PE0) Porta F (PF7..PF0) Porta G (PG4..PG0) Avcc Aref PEN XTAL1 XTAL2 RESET Tensione di alimentazione Massa La porta A è una porta di IO digitale comprensiva di resistori di pullup (selezionabili separatamente bit per pbit). La porta A ha caratteristiche di DRIVING completamente simmetriche con alte correnti di sinking e sourcing. Quando sono ingressi i pin della porta A condotti a zero genereranno corrente fino a quando i relativi resistori di pull up non saranno disabilitati. I PIN della porta A sono in alta impedenza (TRISTATE) al reset anche se il clock non fosse attivo. La porta B ha caratteristiche analoghe alla porta A. La porta C ha caratteristiche analoghe alla porta A. La porta D ha caratteristiche analoghe alla porta A. La porta E ha caratteristiche analoghe alla porta A. La porta F serve per il convertitore AD, alternativamente se il convertitore è disabilitato essa può servier come le porte di IO digitali. Quando l’interfaccia JTAG è attiva i suoi pin possono essere utilizzati per la programmazione ed il debug. La porta G ha caratteristiche analoghe alla porta A. Alimentazione Analogica, viene usato per i piedini del convertitore analogico, e deve essere connesso a Vcc con un filtro passa basso, anche quando il convertitore non viene utilizzato. Alimentazione di riferimento convertitore Analogico Programming ENAble Piedino di alimentazione del quarzo esterno Piedino di alimentazione del quarzo esterno Segnale per il ripristino del funzionamento del processore Tutti i piedini delle varie porte, oltre ad avere la funzionalità base di servire come terminali di IO digitale possono essere utilizzati per altre funzioni specifiche come spiegato più avanti. La programmazione in codice oggetto Per la programmazione in codice oggetto, bisognerà fissare alcune convenzioni riportate nella seguente tabella e che saranno utilizzate nello spiegare le istruzioni di sistema: Rd Rd R K k b s X,Y,Z A q Registro di destinazione (o sorgente) nel file dei registri Registro sorgente nel file dei registri Risultato a seguito della operazione Dato costante Indirizzo costante Bit nel file dei registri oppure registro di I/O (3bit) Bit nella parola di stato Registri utilizzati per l’indirizzamento indiretto dei dati, su 16 bit ciascuno, consistono di due registri ciascuno: X=R27R26, Y=R29R28, Z=R31R30 Indirizzo nello spazio di I/O Indirizzo diretto 6bit I registri RAMPX, RAMPY e RAMPZ (X,Y,Z) si possono utilizzare come puntatori ad una ram esterna ed abilitano l’indirizzamento indiretto all’intero spazio di dati sui microcontrollori con più di 64KB di spazio dati oppure la raccolta delle costanti sui microcontrollori con più di 64KB di spazio memoria per i programmi. In particolare, ad esempio, l’Atmega128 è il primo dei MCU Avr con più di 64Kb, essendo i registri puntatore di 16 bit essi possono indirizzare soltanto 64KB di memoria per volta. Il registro di paginazione della memoria programm RAMPZ nel suo bit più basso viene utilizzato per identificare se si interessa accedere ai primi 64KB oppure alla pagina di memoria flash seguente. Gli altri bit (1-7) sono attualmente riservati, essi si leggono come 0 e vanno scritti a 0. In futuro potranno essere usati per gestire quantità ancora più grosse di memoria. Sui processori AVR, esistono differenti categorie di istruzioni a seconda di come questi operano sui registri e/o sulla memoria. I tipi di istruzione sono definiti invece dalla seguente tabella: Tipo di istruzione Organizzazione Istruzioni dirette a registro singolo (OP,Rd) Istruzioni dirette a dure registri (Op, Rr, Rd) Istruzioni dirette nello spazio di I/O (Op, Rd/Rr, A) Indirizzamento dati diretto (Op, Rr,Rd, DataAddr) Indirizzamento dati indiretto, con disp (Op, Rr,Rd,q) Indirizzamento diretto dati memeotia (Op, MSB, LSB) Salti indiretti in memoria Salti relativi: Opcode, k 11 bit di opcode e 5 bit per il registro 6 bit di opcode e 5+5 bit per identificare i due registri 5 bit opcode, 5 bit registro, 6 bit indirizzo memoria 11 bit di opcode, 5bit registro, 16 bit indirizzo 5 bit opcode, 5 bit Reg, 6 bit q 10 bit opcode, 22 bit indirizzo Z è usato come nuova locazione PC=PC+1+k, k è di 12 bit, complemento 2 Durante le istruzioni, una serie di queste possono avere un indirizzamento indiretto implicito, in questo caso una serie di regole viene utilizzata per determinare sia la corretta locazione della cella indirizzata, sia l’aggiornamento dello stato dei registri in funzione del tipo di opcode utilizzato. La seguente tabella riassume le relazioni principali nel controllo dell’indirizzamento: Tipo di istruzione Organizzazione Indirizzamento dati indiretto Indirizzamneto indiretto con predecremento Indirizzamento indiretto con post-incremento X,Y,Z vengono usati come indirizzi (?) A(T) = (R(T)-1) , R(T+1) = R(T)-1 R=X,Y,Z A(T) = R(T), R(T+1) = R(T) + 1 R=X,Y,Z Indirizzamento costante in memoria programma Indirizzamento costante memoria e post incremento Si usa RAMPZ, Z in maniera indiretta A(T) = R(T), R(T+1) = R(T) + 1 R=RAMPZ,Z NOTA1: Istruzioni dirette nello spazio di I/O, nei microcontrollori come l’Atmega128 aventi più di 64 byte di I/O memory (6 bit) il restante spazio di I/O potrà essere acceduto come se fosse un indirizzamento dati (e non memoria). NOTA2: nell’indirizzamento dati indiretto con displacement il valore nello spazio dati viene sommato (a seconda dell’opcode) al valore contenuto nella memoria indirizzata dai registri Y o Z, consentendo in tal modo un indirizzamnto indiretto parametrico NOTA3: come detto in precedenza, nell’indirizzamento indiretto (con il registro Z) nello spazio di memoria, quando configurato il bit di memoria estesa (ELPM) il registro RAMPZ estenderà (completera) l’insirizzo. Di seguito verrà analizzato sommariamente l’elenco delle istruzioni in codice oggetto (codice assembler) che può essere utilizzato nell’ambiente di sviluppo (per essere precisi, ciò che viene riportato è il codice assembler, che è collegato al codice oggetto dalle tabelle di conversione deglio OPCODES in codic macchina). L’elenco riportato distingue le istruzioni assembler in categorie a seconda della tipologia di operazioni che essi effettuano: • • • • • Operazioni aritmetiche Operazioni di salto condizionato Operazioni di trasferimento dati Operazioni sui bit Operazioni di controllo del microprocessore. Le operazioni aritmetiche comprendono somma (ADX), sottrazione (SBX), AND e OR logici, operazioni di complemento, operazioni sui bit, incremento e decremento ed un ampio numero di moltiplicazioni tra numeri con segno e senza segno, con frazioni o senza frazioni. Non tutti i registri possono essere utilizzati in tutte le operazioni, ed in particolare, mentre le istruzioni di accesso nello spazio di I/O sono generalmente scritte per funzionare bene solo sulle locazioni di memoria di I/O più basse e risultano inadeguate per gli ultimi processori Atmega con 256 byte di spazio di input output. A piede di ogni tabella di istruzioni sono riportate le istruzioni con limitazione sui registri e/o sui valori indirizzati. Nella legenda d,r saranno utilizzati per indicare i registri consentiti, A gli spazi di indirizzamento nello spazio dei dati, K lo spazio di indirizzamento nello spazio di indirizzi di programma. Le istruzioni matematiche che riportano una qualche limitazione sui registri e/o gli indirizzi possibili sono le seguenti: LDI, CBR, CPI, ANDI, ORI, SBR, SBCI, SUBI, d = [16, 31] , K=8bit FMUL, FMULS, FMULS, MULSU, d,r = [16, 23] SBIW, ADIW d = 24, 26, 28, 30, K=6bit SER d = [16, 31] MULS d,r = [16, 31] Quasi tutte le istruzioni aritmentiche che supportano l’indirizzamento diretto degli operandi sono limitate ai registri da 16 a 31. Tali registri sono preferibili per l’utilizzo di operazioni di elaborazione matematica, mentre i restanti sono utilizzabili come supporto nelle operazioni e/o per il controllo e trasferimento da e verso memoria e da e verso lo spazio di ingresso/uscita. Analogamente le istruzioni con limitazioni sui valori di indirizzamento possibili sono: BRBC, BRBS, BRCC, BRCS, BREQ, BRGE, BRHC, BRHS, BRID, BRIE, BRLO, BRLT, BRMI, BRNE, BRPL, BRSH, BRTC, BRTS, BRVC, BRVS K = 7Bit RCALL, RJMP K = 12bit JMP, CALL K = 16, 22bit Limitazioni: LDS, STS IN, OUT LPM LD, ST MOVW k = 16bit A = 6bit register = Z register = X, Y, Z consente lo spostamento solo tra registri pari Le limitazioni sulle operazioni sono relative agli indirizzi assumibili da A: SBI, SBIC, SBIS, CBI A = 5bit NOTE: 1. Questa istruzione non è disponibile in tutti I dispositivi, per controllare se l’istruzione è disponibile bisognerà consunltare l’help specifico del relative dispositivo. 2. Non tutte le varianti di questa istruzione sono disponibili in tutti i dispositivi. Come per la prima nota, controllarne la presenza nel manuale specifico del dispositivo. 3. Non tutte le varianti dell’istruzione LPM sono disponibili in tutti I dispositivi. Il modulo LPM non è in particolare implementato nel AT90S1200. 4. I tempi di calcolo per l’accesso in memoria assume che la memoria utilizzata sia quella interna, e non sono validi qualora l’accesso sia effettuato ad una memoria esterna. Per cio che concerne le istruzioni LD, ST, LDS, STS, PUSH, POP, bisognerà aggiungere un ciclo per ogni stato di attesa. Per le istruzioni CALL, ICALL, EICALL, RCALL, RET, RETI in tutti I dispositivi a 16bit bisognerà aggiungere tre cicli più due per ogni stato di attesa. Analogamente, per le istruzioni CALL, ICALL, EICALL, RCALL, RET, RETI nei dispositivi a 22bit bisognerà aggiungere cinque cicli più tre per ogni stato di attesa. Le istruzioni di confronto tra registri (CP, CPI, SUB, SUBI) aggiornano tutti i flag della parola di stato e può essere utilizzata per determinare le relazioni tra I valori confrontati. Essa può essere utilizzata, in congiunzione con le istruzioni di salto condizionate per determinare l’ordine di esecuzione del programma (controllo di flusso). La seguente tabella facilita la codifica del codice di salto in funzione del risultato di test. Test Opcode Test Opcode Commento Rd>Rr BRLT Rd <= Rr BRGE Signed Rd>=Rr BRGE Rd < Rr BRLT Signed Rd=Rr BREQ Rd<>Rr BRNE Signed Rd<=Rr BRGE Rd>Rr BRLT Signed Rd<Rr BRLT Rd>=Rr BRGE Signed Rd>Rr BRLQ Rd<=Rr BRSH Unsigned Rd>=Rr BRSH/BRCC Rd<Rr BRLQ/BRCS Unsigned Rd=Rr BREQ Rd<>Rr BRNE Unsigned Rd<=Rr BRSH Rd>Rr BRLQ Unsigned Rd<Rr BRLQ/BRCS Rd>=Rr BRSH/BRCC Unsigned BRCC Carry BRCS No carry Negative BRMI Positive BRPL Overflow BRVS No Over BRVC Zero BREQ Nonzero BRNE Nota: 1. Scambiare Rd e Rr nell’operazione prima di effettuare il test, ovvero eseguire, CP Rd,Rr −> CP Rr,Rd La programmazione dei “fusibili” I microcontrollori mettono a disposizione un area di memoria eeprom in cui programmare un insieme di configuazioni di default che regolano il successivo funzionamento del micro. Detti bit (generalmente nominati fuses) consentono di configurare alcuni comportamenti particolarmente rilevanti. Se abbiamo a disposizione l’AVRstudio potremo configurare questi bit direttamente tramite la loro interpretazione logica e senza interessarci del valore da impostare. In particolare questi bit consentiranno di: • Blocchi di programmazione della memoria (LB2,LB1) sia flash che eeprom; • Blocchi di programmazione ed esecuzione nel settore delle applicazioni (BLB02,BLB01); • Blocchi specifici di programmazione ed esecuzione nel settore di boot (BLB12, BLB11); • Abilitazione OCD: default NO • Abilitazione JTAG: default SI • Abilitazione programmazione tramite seriale: default SI • Watchdog ON: default NO • EEPROMSAVE: salva eeprom su un erase, default NO • Dimensione Boot sector: 1K default • • • • • BOOT reset: usa il settore di boot al reset, default NO Clock divide: divedel il clock per 8, default NO Start up time Sorgente del Clock Clock calibration byte La gestione delle interruzioni L’AVR ha a disposizione numerosi sorgenti di interruzione, ognuna di queste può essere collegata ad una procedura differente mediante una tablella in indirizzamento delle procedure di interruzione. Inoltre ogni interruzione può essere singolarmente mascherata o abilitata tramite un apposito insieme di registri maschera. Inoltre utilizzando alcune opzioni di compilazione BLB02, BLB12 è possibile disabilitare in maniera sicura gli interrupt. Ci sono due tipi fondamentali di interrupt nell’AVR: gli interrupt si evento (soglia) e gli interrupt su condizione (valore). I primi sono sollevati ogni volta che una condizione si presenta e segnalano la loro condizione su un bit di interrupt che va ripristinato prima di uscire dalla procedura di gestione dell’interruzione. I secondi invece sollevano l’interrupt solo se la condizione persiste al momento in cui l’interrupt andrebbe servito. Questo significa che se la CPU era occupata durante le condizioni di interrupt e queste cessano prima che essa sia libera, nessun interrupt handler verrà eseguito anche quando il controllore sarà di nuovo disponibile per servirla. Quando il processore esce da una interruzione esso ritorna sempre al flusso principale di programma ed esegue almeno una o due istruzioni prima che un nuovo interrupt possa essere eseguito (sebbene questo possa essere già in attesa). Il registro di stato non viene salvato automaticamente all’esecuzione di una procedura ad interruzione. Questo compito va gestito manualmente. Il tempo di risposta ad un interrupt corrisponde ad un minimo di 4 cicli di clock, dopo tale periodo le istruzioni puntate dal relativo vettore di interruzione verranno eseguite. Ill vettore viene trattato come se fosse una istruzione jump al codice dell interrupte questo può richiedere tre cicli di clock per un primo servizio. Se un interrupt occorre mentr il processore sta eseguendo una istruzione che richiede più di un ciclo l’istruzione verrà completata prima che l’interrupt possa essere eseguito. Il return da un interrupt richiede 4 cicli di clock, in questa fase il program counter viene estratto dallo stack e lo stack vinene aggiornato propriamente. La struttura della memoria In una MCU Atmega128 ci sono tre tipi di memoria lineari e regolari: la memoria programma, costituita da una FLASH riprogrammabile elettricamente di dimensioni pari a 128Kbyte, la memoria dati costituita da una SDRAM di dimensioni pari a 4Kbyte e la meomoria EEPROM, durevole tra diverse accensioni e di dimensioni pari a 4Kbyte. Descrizione dell’ambiente di sviluppo L’ambiente di sviluppo fornito nel corso consiste nelle seguenti componenti: • • AVRstudio WinAVR-XXXXXXX-bin-install.exe Tutte le componenti software fornite nel corso sono disponibili come software di pubblico (GPL o simili) dominio e sono correntemente scaricabili tramite rete internet seguendo gli opportuni link software a partire dal sito www.AVRFreaks.org che colleziona tutto il materiale disponibile per i processori Atmel della famiglia AVR. L’AVRstudio è un ambiente di sviluppo distribuito dalla Atmel corporation in grado di gestire progetti di sviluppo scritti in linguaggio assembler (AVR appunto). L’ambiente dispone delle seguenti funzionalità che lo rendono molto interessante per gli scopi del corso: gestione di progetti anche complessi, disponibilità di operare su tutti i prodotti della gamma Atmel, assemblatore, linker, e simulatore/debugger integrato nel ambiente di sviluppo. Possibilità di simulare tutti i processori della gamma e di controllare il flusso del programma agendo via software sui pin virtuali dei piedini del microcontrollore simulato. Inoltre per coloro che sono interessati alle fasi implementative dello sviluppo il sistema consente di integrarsi con diversi dispositivi di sviluppo rapidi (STK 200/500/501). L’AVR consente inoltre di importare e “debuggare” codice oggetto in un formato COFF, esteso dalla Atmel stesso. In tal modo, utilizzando le funzionalità offerte dal WinAVR sarà possibile interagire con i sistemi AVR e sviluppare codice anche in linguaggi di alto livello, ed eseguire la loro simulazione nell’ambiente di sviluppo prima di provarli sul microcontrollore. Il software WinAvr è infatti un progetto di pubblico dominio (disponibile su sourceforge) che integra diverse funzionalità aggiuntive e parzialmente complementari a quelle offerte dal AVRstudio, ed in particolare: un meta-compilatore per architettura AVR in grado di eseguire in ambiente windows, un simulatore compatibile con debugger GDB su IP, alcuni tools per la programmazione, un editor sensibile al contesto (CSE) denominato programmer Notepad. Nell’installare i software per lo sviluppo se l’ambiente AVR studio verra’ installato successivamente al WINavr esso trovera’ l’installazione del compilatore GNU (AVR-GCC) sulla macchina in oggetto e preparera’ l’ambiente stesso per sviluppare programmi direttamente in codice C. Nell’ambito di questo corso faremo raramente riferimento alla programmazione in assembler e la utilizzeremo soprattutto per capire i meccanismi di base del funzionamento del processore ed analizzare cosa succede nel sistema quando la diagnostica del programma in codice C non sara’ sufficiente per comprendere il comportamento del microcontrollore (in particolare per la gestione dei vettori di interruzione ed il passaggio dei parametri). Installazione e configurazione dei programmi L’istallazione del codice è alquanto semplice, l’AVRstudio è fornito di un programma di installazione integrato analogo a quelli già disponibili per tutti i programmi windows, l’utente rimane libero di scegliere il percorso e le opzioni di installazione in accordo con le caratteristiche del proprio computer. A termine installazione una apposita voce aggiunta al menù di avvia consente l’esecuzione del programma. Alla sua esecuzione il progrmma apparirà con una finestra analoga alla seguente: L’ambiente di sviluppo (IDE) è analogo per certi versi ai prodotti di sviluppo software disponibili per il sistema windows. E’ possibile gestire sia file di codice che progetti complessi. Questi ultimi consentono l’esecuzione di codice sviluppato per tutta la linea di prodotti Atmel. L’uso del programma verrà mostrato durante le lezioni e sarà più chiaro in seguito quando verranno discusse le procedure di compilazione simulazione e debug dei software sviluppati. L’istallazione di WinAVR è per certi versi simile a quella dell’AVRstudio, nel senso che una procedura di installazione guidata ci aiuterà a configurare le opzioni più idonee per il nostro calcolatore. A termine installazione tuttavia non sarà disponibile nessun ambiente integrato per lo sviluppo ma una serie di tools preconfigurati ed installati nella directory scelta per l’installazione. L’ambiente di sviluppo include le seguenti componenti: il GCC3.3.1 (o seguente) configurato per la compilazione di codice C,C++ in oggetti di tipo AVR; le Binutils 2.1.4 che consentono la conversione tra il formato ELF standard del GCC nel formato ECOFF supportato da AVRstudio; le avr-libc che offrono un ampia libreria di funzioni C standard disponibile al microcontrollore; l’AVRdude un programma che consente di programmare gli AVR tramite molti dispositivi compreso l’interfaccia per porta parallela; AVRice, un debugger compatibile JTAG (sconsigliato a questo livello di sviluppo, per il grosso lavoro ancora in corso di reverse engineering sui protocolli ATMEL) esso può essere utilizzato per interfacciare il GDB con le interfacce JTAG della AVR; SimulAVR è un simulatore di architetture AVR esso può essere configurato per eseguire il debug via rete e quindi essere utilizzato in congiunzione con le funzionalità offerte dal GDB; Insight 5.3.91 è una interfaccia grafica per il GDB; UISP è un ISP (in serial programming) tool; il GDB 5.3.91 in grado di connettersi con SimulAVR e AVRdude ed eseguire il debug del codice sviluppato; TkInfo per accedere a tutta la documentazione in linea disponibile;alcuni programmi Win32 per la gestione dell’ambiente di compilazione “make, bash,...”; Programmers Notepad è un editor sensibile al contesto e riconfigurabile come un IDE; alcuni esempi che mostrano come utilizzare l’ambiente di sviluppo in congiunzione con AVR studio. Dopo aver eseguito l’installazione la prima cosa che è opportuno fare è quella di prendere visione dei tools installati (eseguendo le demo nel albero delle directory). Il programmer notepad non è nell’albero delle directory di default, per coloro che trovassero utile il programma possono inserirlo per averlo disponibile ovunque. Nelle directory di documentazione sono disponibili help files che spieganocome configurare i singoli programmi per avere una integrazione ottimale tra i tools (di WinAvr) e verso l’AVRstudio offerto dalla atmel. In particolare converrà analizzare i makefile di esempio in grado di compilare file in C/C++ e convertirli nel particolare formato COFF esteso supportato dal’AVRstudio. Tutti i progetti in linguaggio di alto livello possono infatti a richiesta venire convertiti nel formato COFF che AVRstudio può importare. In questo modo risulta possibile eseguire il debug di codice C all’interno del ambiente di AVRStudio e pertanto utilizzando tutte le funzionalità offerte dall’IDE. In particolare vale la pena osservare che non esiste in WinAvr un unico tool grafico da cui è possibile controllare tutte le funzionalità offerte dal pacchetto ma che, viceversa, molte di queste sono accessibili solo da linea di comando o con l’esecuzione simultanea di più applicazioni. A tal fine appare opportuno suggerire di preconfigurare una finestra di comando con le variabili di ambiente opportune per l’accesso a tutti i tools disposti nel pacchetto. Sebbene tutte le funzionalità siano eseguibili tramite i tools predisposti, alcuni makefile di esempio possono aiutare i novizi a eseguire le procedure fondamentali: compilazione, conversione in formato ECOFF. Nonostante sia possibile configurare l’ambiente di sviluppo AVR per compilare automaticamente e ricaricare il codice ottenuto di nuovo all’interno dell’IDE si suggerisce di eseguire a mano le operazioni necessarie in maniera da prendere confidenza più approfondita con i singoli tools. Una volta che questa pratica sia stata ottenuta a livello approfondito lo studente potrà consultare gli help files forniti con la documentazione per studiare come automatizzare tutte le operazioni